Rfc4050KeyFormatter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / System / Security / Cryptography / Rfc4050KeyFormatter.cs / 1305376 / Rfc4050KeyFormatter.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 

using System; 
using System.Diagnostics; 
using System.Diagnostics.CodeAnalysis;
using System.Globalization; 
using System.IO;
using System.Numerics;
using System.Xml;
using System.Xml.XPath; 
using System.Text;
using System.Diagnostics.Contracts; 
using Microsoft.Win32.SafeHandles; 

namespace System.Security.Cryptography { 
    /// 
    ///     Utility class to convert NCrypt keys into XML and back using a format similar to the one described
    ///     in RFC 4050 (http://www.ietf.org/rfc/rfc4050.txt).
    /// 
    ///     #RFC4050ECKeyFormat
    /// 
    ///     The format looks similar to the following: 
    ///
    ///          
    ///             
    ///                 
    ///             
    ///              
    ///                 
    ///                  
    ///              
    ///         
    ///  
    internal static class Rfc4050KeyFormatter {
        private const string DomainParametersRoot = "DomainParameters";
        private const string ECDHRoot = "ECDHKeyValue";
        private const string ECDsaRoot = "ECDSAKeyValue"; 
        private const string NamedCurveElement = "NamedCurve";
        private const string Namespace = "http://www.w3.org/2001/04/xmldsig-more#"; 
        private const string PublicKeyRoot = "PublicKey"; 
        private const string UrnAttribute = "URN";
        private const string ValueAttribute = "Value"; 
        private const string XElement = "X";
        private const string YElement = "Y";

        private const string XsiTypeAttribute = "type"; 
        private const string XsiTypeAttributeValue = "PrimeFieldElemType";
        private const string XsiNamespace = "http://www.w3.org/2001/XMLSchema-instance"; 
        private const string XsiNamespacePrefix = "xsi"; 

        private const string Prime256CurveUrn = "urn:oid:1.2.840.10045.3.1.7"; 
        private const string Prime384CurveUrn = "urn:oid:1.3.132.0.34";
        private const string Prime521CurveUrn = "urn:oid:1.3.132.0.35";

        ///  
        ///     Restore a key from XML
        ///  
        internal static CngKey FromXml(string xml) { 
            Contract.Requires(xml != null);
            Contract.Ensures(Contract.Result() != null); 

            // Load the XML into an XPathNavigator to access sub elements
            using (TextReader textReader = new StringReader(xml))
            using (XmlTextReader xmlReader = new XmlTextReader(textReader)) { 
                XPathDocument document = new XPathDocument(xmlReader);
                XPathNavigator navigator = document.CreateNavigator(); 
 
                // Move into the root element - we don't do a specific namespace check here for compatibility
                // with XML that Windows generates. 
                if (!navigator.MoveToFirstChild()) {
                    throw new ArgumentException(SR.GetString(SR.Cryptography_MissingDomainParameters));
                }
 
                // First figure out which algorithm this key belongs to
                CngAlgorithm algorithm = ReadAlgorithm(navigator); 
 
                // Then read out the public key value
                if (!navigator.MoveToNext(XPathNodeType.Element)) { 
                    throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
                }

                BigInteger x; 
                BigInteger y;
                ReadPublicKey(navigator, out x, out y); 
 
                // Finally, convert them into a key blob to import into a CngKey
                byte[] keyBlob = NCryptNative.BuildEccPublicBlob(algorithm.Algorithm, x, y); 
                return CngKey.Import(keyBlob, CngKeyBlobFormat.EccPublicBlob);
            }
        }
 
        /// 
        ///     Map a curve URN to the size of the key associated with the curve 
        ///  
        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The parameters to the exception are in the correct order")]
        private static int GetKeySize(string urn) { 
            Contract.Requires(!String.IsNullOrEmpty(urn));
            Contract.Ensures(Contract.Result() > 0);

            switch (urn) { 
                case Prime256CurveUrn:
                    return 256; 
 
                case Prime384CurveUrn:
                    return 384; 

                case Prime521CurveUrn:
                    return 521;
 
                default:
                    throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve), "algorithm"); 
            } 
        }
 
        /// 
        ///     Get the OID which represents an elliptic curve
        /// 
        private static string GetCurveUrn(CngAlgorithm algorithm) { 
            Contract.Requires(algorithm != null);
 
            if (algorithm == CngAlgorithm.ECDsaP256 || algorithm == CngAlgorithm.ECDiffieHellmanP256) { 
                return Prime256CurveUrn;
            } 
            else if (algorithm == CngAlgorithm.ECDsaP384 || algorithm == CngAlgorithm.ECDiffieHellmanP384) {
                return Prime384CurveUrn;
            }
            else if (algorithm == CngAlgorithm.ECDsaP521 || algorithm == CngAlgorithm.ECDiffieHellmanP521) { 
                return Prime521CurveUrn;
            } 
            else { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve), "algorithm");
            } 
        }

        /// 
        ///     Determine which ECC algorithm the key refers to 
        /// 
        private static CngAlgorithm ReadAlgorithm(XPathNavigator navigator) { 
            Contract.Requires(navigator != null); 
            Contract.Ensures(Contract.Result() != null);
 
            if (navigator.NamespaceURI != Namespace) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_UnexpectedXmlNamespace,
                                                         navigator.NamespaceURI,
                                                         Namespace)); 
            }
 
            // 
            // The name of the root element determines which algorithm to use, while the DomainParameters
            // element specifies which curve we should be using. 
            //

            bool isDHKey = navigator.Name == ECDHRoot;
            bool isDsaKey = navigator.Name == ECDsaRoot; 

            if (!isDHKey && !isDsaKey) { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm)); 
            }
 
            // Move into the DomainParameters element
            if (!navigator.MoveToFirstChild() || navigator.Name != DomainParametersRoot) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingDomainParameters));
            } 

            // Now move into the NamedCurve element 
            if (!navigator.MoveToFirstChild() || navigator.Name != NamedCurveElement) { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingDomainParameters));
            } 

            // And read its URN value
            if (!navigator.MoveToFirstAttribute() || navigator.Name != UrnAttribute || String.IsNullOrEmpty(navigator.Value)) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingDomainParameters)); 
            }
 
            int keySize = GetKeySize(navigator.Value); 

            // position the navigator at the end of the domain parameters 
            navigator.MoveToParent();   // NamedCurve
            navigator.MoveToParent();   // DomainParameters

            // 
            // Given the algorithm type and key size, we can now map back to a CNG algorithm ID
            // 
 
            if (isDHKey) {
                if (keySize == 256) { 
                    return CngAlgorithm.ECDiffieHellmanP256;
                }
                else if (keySize == 384) {
                    return CngAlgorithm.ECDiffieHellmanP384; 
                }
                else { 
                    Debug.Assert(keySize == 521, "keySize == 521"); 
                    return CngAlgorithm.ECDiffieHellmanP521;
                } 
            }
            else {
                Debug.Assert(isDsaKey, "isDsaKey");
 
                if (keySize == 256) {
                    return CngAlgorithm.ECDsaP256; 
                } 
                else if (keySize == 384) {
                    return CngAlgorithm.ECDsaP384; 
                }
                else {
                    Debug.Assert(keySize == 521, "keySize == 521");
                    return CngAlgorithm.ECDsaP521; 
                }
            } 
        } 

        ///  
        ///     Read the x and y components of the public key
        /// 
        private static void ReadPublicKey(XPathNavigator navigator, out BigInteger x, out BigInteger y) {
            Contract.Requires(navigator != null); 

            if (navigator.NamespaceURI != Namespace) { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_UnexpectedXmlNamespace, 
                                                         navigator.NamespaceURI,
                                                         Namespace)); 
            }

            if (navigator.Name != PublicKeyRoot) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey)); 
            }
 
            // First get the x parameter 
            if (!navigator.MoveToFirstChild() || navigator.Name != XElement) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey)); 
            }
            if (!navigator.MoveToFirstAttribute() || navigator.Name != ValueAttribute || String.IsNullOrEmpty(navigator.Value)) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
            } 

            x = BigInteger.Parse(navigator.Value, CultureInfo.InvariantCulture); 
            navigator.MoveToParent(); 

            // Then the y parameter 
            if (!navigator.MoveToNext(XPathNodeType.Element) || navigator.Name != YElement) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
            }
            if (!navigator.MoveToFirstAttribute() || navigator.Name != ValueAttribute || String.IsNullOrEmpty(navigator.Value)) { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
            } 
 
            y = BigInteger.Parse(navigator.Value, CultureInfo.InvariantCulture);
        } 

        /// 
        ///     Serialize out information about the elliptic curve
        ///  
        private static void WriteDomainParameters(XmlWriter writer, CngKey key) {
            Contract.Requires(writer != null); 
            Contract.Requires(key != null && (key.AlgorithmGroup == CngAlgorithmGroup.ECDsa || key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman)); 

            writer.WriteStartElement(DomainParametersRoot); 

            // We always use OIDs for the named prime curves
            writer.WriteStartElement(NamedCurveElement);
            writer.WriteAttributeString(UrnAttribute, GetCurveUrn(key.Algorithm)); 
            writer.WriteEndElement();   // 
 
            writer.WriteEndElement();   //  
        }
 
        private static void WritePublicKeyValue(XmlWriter writer, CngKey key) {
            Contract.Requires(writer != null);
            Contract.Requires(key != null && (key.AlgorithmGroup == CngAlgorithmGroup.ECDsa || key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman));
 
            writer.WriteStartElement(PublicKeyRoot);
 
            byte[] exportedKey = key.Export(CngKeyBlobFormat.EccPublicBlob); 
            BigInteger x;
            BigInteger y; 
            NCryptNative.UnpackEccPublicBlob(exportedKey, out x, out y);

            writer.WriteStartElement(XElement);
            writer.WriteAttributeString(ValueAttribute, x.ToString("R", CultureInfo.InvariantCulture)); 
            writer.WriteAttributeString(XsiNamespacePrefix, XsiTypeAttribute, XsiNamespace, XsiTypeAttributeValue);
            writer.WriteEndElement();   //  
 
            writer.WriteStartElement(YElement);
            writer.WriteAttributeString(ValueAttribute, y.ToString("R", CultureInfo.InvariantCulture)); 
            writer.WriteAttributeString(XsiNamespacePrefix, XsiTypeAttribute, XsiNamespace, XsiTypeAttributeValue);
            writer.WriteEndElement();   // 

            writer.WriteEndElement();   //  
        }
 
        ///  
        ///     Convert a key to XML
        ///  
        internal static string ToXml(CngKey key) {
            Contract.Requires(key != null && (key.AlgorithmGroup == CngAlgorithmGroup.ECDsa || key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman));
            Contract.Ensures(Contract.Result() != null);
 
            StringBuilder keyXml = new StringBuilder();
 
            XmlWriterSettings settings = new XmlWriterSettings(); 
            settings.Indent = true;
            settings.IndentChars = "  "; 
            settings.OmitXmlDeclaration = true;

            using (XmlWriter writer = XmlWriter.Create(keyXml, settings)) {
                // The root element depends upon the type of key 
                string rootElement = key.AlgorithmGroup == CngAlgorithmGroup.ECDsa ? ECDsaRoot : ECDHRoot;
                writer.WriteStartElement(rootElement, Namespace); 
 
                WriteDomainParameters(writer, key);
                WritePublicKeyValue(writer, key); 

                writer.WriteEndElement();   // root element
            }
 
            return keyXml.ToString();
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK