EncryptedXml.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / clr / src / ManagedLibraries / Security / System / Security / Cryptography / Xml / EncryptedXml.cs / 5 / EncryptedXml.cs

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

// 
// EncryptedXml.cs 
//
// 04/01/2002 
//

namespace System.Security.Cryptography.Xml
{ 
    using System;
    using System.Collections; 
    using System.IO; 
    using System.Net;
    using System.Security; 
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Policy;
    using System.Text; 
    using System.Xml;
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public class EncryptedXml {
 
        //
        // public constant Url identifiers used within the XML Encryption classes
        //
 
        public const string XmlEncNamespaceUrl = "http://www.w3.org/2001/04/xmlenc#";
        public const string XmlEncElementUrl = "http://www.w3.org/2001/04/xmlenc#Element"; 
        public const string XmlEncElementContentUrl = "http://www.w3.org/2001/04/xmlenc#Content"; 
        public const string XmlEncEncryptedKeyUrl = "http://www.w3.org/2001/04/xmlenc#EncryptedKey";
 
        //
        // Symmetric Block Encryption
        //
 
        public const string XmlEncDESUrl = "http://www.w3.org/2001/04/xmlenc#des-cbc";
        public const string XmlEncTripleDESUrl = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; 
        public const string XmlEncAES128Url = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; 
        public const string XmlEncAES256Url = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";
        public const string XmlEncAES192Url = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; 

        //
        // Key Transport
        // 

        public const string XmlEncRSA15Url = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; 
        public const string XmlEncRSAOAEPUrl = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; 

        // 
        // Symmetric Key Wrap
        //

        public const string XmlEncTripleDESKeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-tripledes"; 
        public const string XmlEncAES128KeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-aes128";
        public const string XmlEncAES256KeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; 
        public const string XmlEncAES192KeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; 

        // 
        // Message Digest
        //

        public const string XmlEncSHA256Url = "http://www.w3.org/2001/04/xmlenc#sha256"; 
        public const string XmlEncSHA512Url = "http://www.w3.org/2001/04/xmlenc#sha512";
 
        // 
        // private members
        // 

        private XmlDocument m_document;
        private Evidence m_evidence;
        private XmlResolver m_xmlResolver; 
        // hash table defining the key name mapping
        private const int m_capacity = 4; // 4 is a reasonable capacity for 
                                          // the key name mapping hash table 
        private Hashtable m_keyNameMapping;
        private PaddingMode m_padding; 
        private CipherMode m_mode;
        private Encoding m_encoding;
        private string m_recipient;
 
        //
        // public constructors 
        // 

        public EncryptedXml () : this (new XmlDocument()) {} 

        public EncryptedXml (XmlDocument document) : this (document, null) {}

        public EncryptedXml (XmlDocument document, Evidence evidence) { 
            m_document = document;
            m_evidence = evidence; 
            m_xmlResolver = null; 
            // set the default padding to ISO-10126
            m_padding = PaddingMode.ISO10126; 
            // set the default cipher mode to CBC
            m_mode = CipherMode.CBC;
            // By default the encoding is going to be UTF8
            m_encoding = Encoding.UTF8; 
            m_keyNameMapping = new Hashtable(m_capacity);
        } 
 
        //
        // public properties 
        //

        // The evidence of the document being loaded: will be used to resolve external URIs
        public Evidence DocumentEvidence { 
            get { return m_evidence; }
            set { m_evidence = value; } 
        } 

        // The resolver to use for external entities 
        public XmlResolver Resolver {
            get { return m_xmlResolver; }
            set { m_xmlResolver = value; }
        } 

        // The padding to be used. XML Encryption uses ISO 10126 
        // but it's nice to provide a way to extend this to include other forms of paddings 
        public PaddingMode Padding {
            get { return m_padding; } 
            set { m_padding = value; }
        }

        // The cipher mode to be used. XML Encryption uses CBC padding 
        // but it's nice to provide a way to extend this to include other cipher modes
        public CipherMode Mode { 
            get { return m_mode; } 
            set { m_mode = value; }
        } 

        // The encoding of the XML document
        public Encoding Encoding {
            get { return m_encoding; } 
            set { m_encoding = value; }
        } 
 
        // This is used to specify the EncryptedKey elements that should be considered
        // when an EncyptedData references an EncryptedKey using a CarriedKeyName and Recipient 
        public string Recipient {
            get {
                // an unspecified value for an XmlAttribute is String.Empty
                if (m_recipient == null) 
                    m_recipient = String.Empty;
                return m_recipient; 
            } 
            set { m_recipient = value; }
        } 

        //
        // private methods
        // 

        private byte[] GetCipherValue (CipherData cipherData) { 
            if (cipherData == null) 
                throw new ArgumentNullException("cipherData");
 
            WebResponse response = null;
            Stream inputStream = null;

            if (cipherData.CipherValue != null) { 
                return cipherData.CipherValue;
            } else if (cipherData.CipherReference != null) { 
                if (cipherData.CipherReference.CipherValue != null) 
                    return cipherData.CipherReference.CipherValue;
                Stream decInputStream = null; 
                // See if the CipherReference is a local URI
                if (cipherData.CipherReference.Uri.Length == 0) {
                    // self referenced Uri
                    string baseUri = (m_document == null ? null : m_document.BaseURI); 
                    TransformChain tc = cipherData.CipherReference.TransformChain;
                    decInputStream = tc.TransformToOctetStream(m_document, m_xmlResolver, baseUri); 
                } else if (cipherData.CipherReference.Uri[0] == '#') { 
                    string idref = Utils.ExtractIdFromLocalUri(cipherData.CipherReference.Uri);
                    // Serialize 
                    inputStream = new MemoryStream(m_encoding.GetBytes(GetIdElement(m_document, idref).OuterXml));
                    string baseUri = (m_document == null ? null : m_document.BaseURI);
                    TransformChain tc = cipherData.CipherReference.TransformChain;
                    decInputStream = tc.TransformToOctetStream(inputStream, m_xmlResolver, baseUri); 
                } else {
                    DownloadCipherValue(cipherData, out inputStream, out decInputStream, out response); 
                } 
                // read the output stream into a memory stream
                byte[] cipherValue = null; 
                using (MemoryStream ms = new MemoryStream()) {
                    Utils.Pump(decInputStream, ms);
                    cipherValue = ms.ToArray();
                    // Close the stream and return 
                    if (response != null)
                        response.Close(); 
                    if (inputStream != null) 
                        inputStream.Close();
                    decInputStream.Close(); 
                }

                // cache the cipher value for Perf reasons in case we call this routine twice
                cipherData.CipherReference.CipherValue = cipherValue; 
                return cipherValue;
            } 
 
            // Throw a CryptographicException if we were unable to retrieve the cipher data.
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingCipherData")); 
        }

        private void DownloadCipherValue (CipherData cipherData, out Stream inputStream, out Stream decInputStream, out WebResponse response) {
            // maybe a network stream, make sure we allow just what is needed!! 
            PermissionSet ps = SecurityManager.ResolvePolicy(m_evidence);
            ps.PermitOnly(); 
            WebRequest request = WebRequest.Create(cipherData.CipherReference.Uri); 
            if (request == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotResolved"), cipherData.CipherReference.Uri); 
            response = request.GetResponse();
            if (response == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotResolved"), cipherData.CipherReference.Uri);
            inputStream = response.GetResponseStream(); 
            if (inputStream == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotResolved"), cipherData.CipherReference.Uri); 
            TransformChain tc = cipherData.CipherReference.TransformChain; 
            decInputStream = tc.TransformToOctetStream(inputStream, m_xmlResolver, cipherData.CipherReference.Uri);
        } 

        //
        // public virtual methods
        // 

        // This describes how the application wants to associate id references to elements 
        public virtual XmlElement GetIdElement (XmlDocument document, string idValue) { 
            if (document == null)
                return null; 
            XmlElement elem = null;

            // Get the element with idValue
            elem = document.GetElementById(idValue); 
            if (elem != null)
                return elem; 
            elem = document.SelectSingleNode("//*[@Id=\"" + idValue + "\"]") as XmlElement; 
            if (elem != null)
                return elem; 
            elem = document.SelectSingleNode("//*[@id=\"" + idValue + "\"]") as XmlElement;
            if (elem != null)
                return elem;
            elem = document.SelectSingleNode("//*[@ID=\"" + idValue + "\"]") as XmlElement; 

            return elem; 
        } 

        // default behaviour is to look for the IV in the CipherValue 
        public virtual byte[] GetDecryptionIV (EncryptedData encryptedData, string symmetricAlgorithmUri) {
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData");
 
            int initBytesSize = 0;
            // If the Uri is not provided by the application, try to get it from the EncryptionMethod 
            if (symmetricAlgorithmUri == null) { 
                if (encryptedData.EncryptionMethod == null)
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingAlgorithm")); 
                symmetricAlgorithmUri = encryptedData.EncryptionMethod.KeyAlgorithm;
            }
            switch (symmetricAlgorithmUri) {
            case EncryptedXml.XmlEncDESUrl: 
            case EncryptedXml.XmlEncTripleDESUrl:
                initBytesSize = 8; 
                break; 
            case EncryptedXml.XmlEncAES128Url:
            case EncryptedXml.XmlEncAES192Url: 
            case EncryptedXml.XmlEncAES256Url:
                initBytesSize = 16;
                break;
            default: 
                // The Uri is not supported.
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotSupported")); 
            } 
            byte[] IV = new byte[initBytesSize];
            byte[] cipherValue = GetCipherValue(encryptedData.CipherData); 
            Buffer.BlockCopy(cipherValue, 0, IV, 0, IV.Length);
            return IV;
        }
 
        // default behaviour is to look for keys defined by an EncryptedKey clause
        // either directly or through a KeyInfoRetrievalMethod, and key names in the key mapping 
        public virtual SymmetricAlgorithm GetDecryptionKey (EncryptedData encryptedData, string symmetricAlgorithmUri) { 
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData"); 

            if (encryptedData.KeyInfo == null)
                return null;
            IEnumerator keyInfoEnum = encryptedData.KeyInfo.GetEnumerator(); 
            KeyInfoRetrievalMethod kiRetrievalMethod;
            KeyInfoName kiName; 
            KeyInfoEncryptedKey kiEncKey; 
            EncryptedKey ek = null;
 
            while (keyInfoEnum.MoveNext()) {
                kiName = keyInfoEnum.Current as KeyInfoName;
                if (kiName != null) {
                    // Get the decryption key from the key mapping 
                    string keyName = kiName.Value;
                    if ((SymmetricAlgorithm) m_keyNameMapping[keyName] != null) 
                        return (SymmetricAlgorithm) m_keyNameMapping[keyName]; 
                    // try to get it from a CarriedKeyName
                    XmlNamespaceManager nsm = new XmlNamespaceManager(m_document.NameTable); 
                    nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
                    XmlNodeList encryptedKeyList = m_document.SelectNodes("//enc:EncryptedKey", nsm);
                    if (encryptedKeyList != null) {
                        foreach (XmlNode encryptedKeyNode in encryptedKeyList) { 
                            XmlElement encryptedKeyElement = encryptedKeyNode as XmlElement;
                            EncryptedKey ek1 = new EncryptedKey(); 
                            ek1.LoadXml(encryptedKeyElement); 
                            if (ek1.CarriedKeyName == keyName && ek1.Recipient == this.Recipient) {
                                ek = ek1; 
                                break;
                            }
                        }
                    } 
                    break;
                } 
                kiRetrievalMethod = keyInfoEnum.Current as KeyInfoRetrievalMethod; 
                if (kiRetrievalMethod != null) {
                    string idref = Utils.ExtractIdFromLocalUri(kiRetrievalMethod.Uri); 
                    ek = new EncryptedKey();
                    ek.LoadXml(GetIdElement(m_document, idref));
                    break;
                } 
                kiEncKey = keyInfoEnum.Current as KeyInfoEncryptedKey;
                if (kiEncKey != null) { 
                    ek = kiEncKey.EncryptedKey; 
                    break;
                } 
            }

            // if we have an EncryptedKey, decrypt to get the symmetric key
            if (ek != null) { 
                // now process the EncryptedKey, loop recursively
                // If the Uri is not provided by the application, try to get it from the EncryptionMethod 
                if (symmetricAlgorithmUri == null) { 
                    if (encryptedData.EncryptionMethod == null)
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingAlgorithm")); 
                    symmetricAlgorithmUri = encryptedData.EncryptionMethod.KeyAlgorithm;
                }
                byte[] key = DecryptEncryptedKey(ek);
                if (key == null) 
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingDecryptionKey"));
 
                SymmetricAlgorithm symAlg = (SymmetricAlgorithm) CryptoConfig.CreateFromName(symmetricAlgorithmUri); 
                symAlg.Key = key;
                return symAlg; 
            }
            return null;
        }
 
        // Try to decrypt the EncryptedKey given the key mapping
        public virtual byte[] DecryptEncryptedKey (EncryptedKey encryptedKey) { 
            if (encryptedKey == null) 
                throw new ArgumentNullException("encryptedKey");
            if (encryptedKey.KeyInfo == null) 
                return null;

            IEnumerator keyInfoEnum = encryptedKey.KeyInfo.GetEnumerator();
            KeyInfoName kiName; 
            KeyInfoX509Data kiX509Data;
            KeyInfoRetrievalMethod kiRetrievalMethod; 
            KeyInfoEncryptedKey kiEncKey; 
            EncryptedKey ek = null;
            bool fOAEP = false; 

            while (keyInfoEnum.MoveNext()) {
                kiName = keyInfoEnum.Current as KeyInfoName;
                if (kiName != null) { 
                    // Get the decryption key from the key mapping
                    string keyName = kiName.Value; 
                    Object kek = m_keyNameMapping[keyName]; 
                    if (kek != null) {
                        // kek is either a SymmetricAlgorithm or an RSA key, otherwise, we wouldn't be able to insert it in the hash table 
                        if (kek is SymmetricAlgorithm)
                            return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, (SymmetricAlgorithm) kek);

                        // kek is an RSA key: get fOAEP from the algorithm, default to false 
                        fOAEP = (encryptedKey.EncryptionMethod != null && encryptedKey.EncryptionMethod.KeyAlgorithm == EncryptedXml.XmlEncRSAOAEPUrl);
                        return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, (RSA) kek, fOAEP); 
                    } 
                    break;
                } 
                kiX509Data = keyInfoEnum.Current as KeyInfoX509Data;
                if (kiX509Data != null) {
                    X509Certificate2Collection collection = Utils.BuildBagOfCerts(kiX509Data, CertUsageType.Decryption);
                    foreach (X509Certificate2 certificate in collection) { 
                        RSA privateKey = certificate.PrivateKey as RSA;
                        if (privateKey != null) { 
                            fOAEP = (encryptedKey.EncryptionMethod != null && encryptedKey.EncryptionMethod.KeyAlgorithm == EncryptedXml.XmlEncRSAOAEPUrl); 
                            return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, privateKey, fOAEP);
                        } 
                    }
                    break;
                }
                kiRetrievalMethod = keyInfoEnum.Current as KeyInfoRetrievalMethod; 
                if (kiRetrievalMethod != null) {
                    string idref = Utils.ExtractIdFromLocalUri(kiRetrievalMethod.Uri); 
                    ek = new EncryptedKey(); 
                    ek.LoadXml(GetIdElement(m_document, idref));
                    return DecryptEncryptedKey(ek); 
                }
                kiEncKey = keyInfoEnum.Current as KeyInfoEncryptedKey;
                if (kiEncKey != null) {
                    ek = kiEncKey.EncryptedKey; 
                    // recursively process EncryptedKey elements
                    byte[] encryptionKey = DecryptEncryptedKey(ek); 
                    if (encryptionKey != null) { 
                        // this is a symmetric algorithm for sure
                        SymmetricAlgorithm symAlg = (SymmetricAlgorithm) CryptoConfig.CreateFromName(encryptedKey.EncryptionMethod.KeyAlgorithm); 
                        symAlg.Key = encryptionKey;
                        return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, symAlg);
                    }
                } 
            }
            return null; 
        } 

        // 
        // public methods
        //

        // defines a key name mapping. Default behaviour is to require the key object 
        // to be an RSA key or a SymmetricAlgorithm
        public void AddKeyNameMapping (string keyName, Object keyObject) { 
            if (keyName == null) 
                throw new ArgumentNullException("keyName");
            if (keyObject == null) 
                throw new ArgumentNullException("keyObject");

            if (!(keyObject is SymmetricAlgorithm) && !(keyObject is RSA))
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform")); 
            m_keyNameMapping.Add(keyName, keyObject);
        } 
 
        public void ClearKeyNameMappings () {
            m_keyNameMapping.Clear(); 
        }

        // Encrypts the given element with the certificate specified. The certificate is added as
        // an X509Data KeyInfo to an EncryptedKey (AES session key) generated randomly. 
        public EncryptedData Encrypt (XmlElement inputElement, X509Certificate2 certificate) {
            if (inputElement == null) 
                throw new ArgumentNullException("inputElement"); 
            if (certificate == null)
                throw new ArgumentNullException("certificate"); 

            if (X509Utils.OidToAlgId(certificate.PublicKey.Oid.Value) != CAPI.CALG_RSA_KEYX)
                throw new NotSupportedException(SecurityResources.GetResourceString("NotSupported_KeyAlgorithm"));
 
            // Create the EncryptedData object, using an AES-256 session key by default.
            EncryptedData ed = new EncryptedData(); 
            ed.Type = EncryptedXml.XmlEncElementUrl; 
            ed.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
 
            // Include the certificate in the EncryptedKey KeyInfo.
            EncryptedKey ek = new EncryptedKey();
            ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
            ek.KeyInfo.AddClause(new KeyInfoX509Data(certificate)); 

            // Create a random AES session key and encrypt it with the public key associated with the certificate. 
            RijndaelManaged rijn = new RijndaelManaged(); 
            ek.CipherData.CipherValue = EncryptedXml.EncryptKey(rijn.Key, certificate.PublicKey.Key as RSA, false);
 
            // Encrypt the input element with the random session key that we've created above.
            KeyInfoEncryptedKey kek = new KeyInfoEncryptedKey(ek);
            ed.KeyInfo.AddClause(kek);
            ed.CipherData.CipherValue = EncryptData(inputElement, rijn, false); 

            return ed; 
        } 

        // Encrypts the given element with the key name specified. A corresponding key name mapping 
        // has to be defined before calling this method. The key name is added as
        // a KeyNameInfo KeyInfo to an EncryptedKey (AES session key) generated randomly.
        public EncryptedData Encrypt (XmlElement inputElement, string keyName) {
            if (inputElement == null) 
                throw new ArgumentNullException("inputElement");
            if (keyName == null) 
                throw new ArgumentNullException("keyName"); 

            Object encryptionKey = null; 
            if (m_keyNameMapping != null)
                encryptionKey = m_keyNameMapping[keyName];

            if (encryptionKey == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingEncryptionKey"));
 
            // kek is either a SymmetricAlgorithm or an RSA key, otherwise, we wouldn't be able to insert it in the hash table 
            SymmetricAlgorithm symKey = encryptionKey as SymmetricAlgorithm;
            RSA rsa = encryptionKey as RSA; 

            // Create the EncryptedData object, using an AES-256 session key by default.
            EncryptedData ed = new EncryptedData();
            ed.Type = EncryptedXml.XmlEncElementUrl; 
            ed.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
 
            // Include the key name in the EncryptedKey KeyInfo. 
            string encryptionMethod = null;
            if (symKey == null) { 
                encryptionMethod = EncryptedXml.XmlEncRSA15Url;
            } else if (symKey is TripleDES) {
                // CMS Triple DES Key Wrap
                encryptionMethod = EncryptedXml.XmlEncTripleDESKeyWrapUrl; 
            } else if (symKey is Rijndael) {
                // FIPS AES Key Wrap 
                switch (symKey.KeySize) { 
                case 128:
                    encryptionMethod = EncryptedXml.XmlEncAES128KeyWrapUrl; 
                    break;
                case 192:
                    encryptionMethod = EncryptedXml.XmlEncAES192KeyWrapUrl;
                    break; 
                case 256:
                    encryptionMethod = EncryptedXml.XmlEncAES256KeyWrapUrl; 
                    break; 
                }
            } else { 
                // throw an exception if the transform is not in the previous categories
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform"));
            }
            EncryptedKey ek = new EncryptedKey(); 
            ek.EncryptionMethod = new EncryptionMethod(encryptionMethod);
            ek.KeyInfo.AddClause(new KeyInfoName(keyName)); 
 
            // Create a random AES session key and encrypt it with the public key associated with the certificate.
            RijndaelManaged rijn = new RijndaelManaged(); 
            ek.CipherData.CipherValue = (symKey == null ? EncryptedXml.EncryptKey(rijn.Key, rsa, false) : EncryptedXml.EncryptKey(rijn.Key, symKey));

            // Encrypt the input element with the random session key that we've created above.
            KeyInfoEncryptedKey kek = new KeyInfoEncryptedKey(ek); 
            ed.KeyInfo.AddClause(kek);
            ed.CipherData.CipherValue = EncryptData(inputElement, rijn, false); 
 
            return ed;
        } 

        // decrypts the document using the defined key mapping in GetDecryptionKey
        // The behaviour of this method can be extended because GetDecryptionKey is virtual
        // the document is decrypted in place 
        public void DecryptDocument () {
            // Look for all EncryptedData elements and decrypt them 
            XmlNamespaceManager nsm = new XmlNamespaceManager(m_document.NameTable); 
            nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            XmlNodeList encryptedDataList = m_document.SelectNodes("//enc:EncryptedData", nsm); 
            if (encryptedDataList != null) {
                foreach (XmlNode encryptedDataNode in encryptedDataList) {
                    XmlElement encryptedDataElement = encryptedDataNode as XmlElement;
                    EncryptedData ed = new EncryptedData(); 
                    ed.LoadXml(encryptedDataElement);
                    SymmetricAlgorithm symAlg = GetDecryptionKey(ed, null); 
                    if (symAlg == null) 
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingDecryptionKey"));
                    byte[] decrypted = DecryptData(ed, symAlg); 
                    ReplaceData(encryptedDataElement, decrypted);
                }
            }
        } 

        // encrypts the supplied arbitrary data 
        public byte[] EncryptData (byte[] plaintext, SymmetricAlgorithm symmetricAlgorithm) { 
            if (plaintext == null)
                throw new ArgumentNullException("plaintext"); 
            if (symmetricAlgorithm == null)
                throw new ArgumentNullException("symmetricAlgorithm");

            // save the original symmetric algorithm 
            CipherMode origMode = symmetricAlgorithm.Mode;
            PaddingMode origPadding = symmetricAlgorithm.Padding; 
 
            byte[] cipher = null;
            try { 
                symmetricAlgorithm.Mode = m_mode;
                symmetricAlgorithm.Padding = m_padding;

                ICryptoTransform enc = symmetricAlgorithm.CreateEncryptor(); 
                cipher = enc.TransformFinalBlock(plaintext, 0, plaintext.Length);
            } finally { 
                // now restore the original symmetric algorithm 
                symmetricAlgorithm.Mode = origMode;
                symmetricAlgorithm.Padding = origPadding; 
            }

            byte[] output = null;
            if (m_mode == CipherMode.ECB) { 
                output = cipher;
            } else { 
                byte[] IV = symmetricAlgorithm.IV; 
                output = new byte[cipher.Length + IV.Length];
                Buffer.BlockCopy(IV, 0, output, 0, IV.Length); 
                Buffer.BlockCopy(cipher, 0, output, IV.Length, cipher.Length);
            }
            return output;
        } 

        // encrypts the supplied input element 
        public byte[] EncryptData (XmlElement inputElement, SymmetricAlgorithm symmetricAlgorithm, bool content) { 
            if (inputElement == null)
                throw new ArgumentNullException("inputElement"); 
            if (symmetricAlgorithm == null)
                throw new ArgumentNullException("symmetricAlgorithm");

            byte[] plainText = (content ? m_encoding.GetBytes(inputElement.InnerXml) : m_encoding.GetBytes(inputElement.OuterXml)); 
            return EncryptData(plainText, symmetricAlgorithm);
        } 
 
        // decrypts the supplied EncryptedData
        public byte[] DecryptData (EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm) { 
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData");
            if (symmetricAlgorithm == null)
                throw new ArgumentNullException("symmetricAlgorithm"); 

            // get the cipher value and decrypt 
            byte[] cipherValue = GetCipherValue(encryptedData.CipherData); 

            // save the original symmetric algorithm 
            CipherMode origMode = symmetricAlgorithm.Mode;
            PaddingMode origPadding = symmetricAlgorithm.Padding;
            byte[] origIV = symmetricAlgorithm.IV;
 
            // read the IV from cipherValue
            byte[] decryptionIV = null; 
            if (m_mode != CipherMode.ECB) 
                decryptionIV = GetDecryptionIV(encryptedData, null);
 
            byte[] output = null;
            try {
                int lengthIV = 0;
                if (decryptionIV != null) { 
                    symmetricAlgorithm.IV = decryptionIV;
                    lengthIV = decryptionIV.Length; 
                } 
                symmetricAlgorithm.Mode = m_mode;
                symmetricAlgorithm.Padding = m_padding; 

                ICryptoTransform dec = symmetricAlgorithm.CreateDecryptor();
                output = dec.TransformFinalBlock(cipherValue, lengthIV, cipherValue.Length - lengthIV);
            } finally { 
                // now restore the original symmetric algorithm
                symmetricAlgorithm.Mode = origMode; 
                symmetricAlgorithm.Padding = origPadding; 
                symmetricAlgorithm.IV = origIV;
            } 

            return output;
        }
 
        // This method replaces an EncryptedData element with the decrypted sequence of bytes
        public void ReplaceData (XmlElement inputElement, byte[] decryptedData) { 
            if (inputElement == null) 
                throw new ArgumentNullException ("inputElement");
            if (decryptedData == null) 
                throw new ArgumentNullException ("decryptedData");

            XmlNode parent = inputElement.ParentNode;
            if (parent.NodeType == XmlNodeType.Document) { 
                // We are replacing the root element, so we will just replace it with the decrypted bytes.
                parent.InnerXml = m_encoding.GetString(decryptedData); 
            } else { 
                XmlNode dummy = parent.OwnerDocument.CreateElement(parent.Prefix, parent.LocalName, parent.NamespaceURI);
 
                try {
                    parent.AppendChild(dummy);

                    // Replace the children of the dummy node with the sequence of bytes passed in. 
                    // The string will be parsed into DOM objects in the context of the parent of the EncryptedData element.
                    dummy.InnerXml = m_encoding.GetString(decryptedData); 
 
                    // Move the children of the dummy node up to the parent.
                    XmlNode child = dummy.FirstChild; 
                    XmlNode sibling = inputElement.NextSibling;

                    XmlNode nextChild = null;
                    while (child != null) { 
                        nextChild = child.NextSibling;
                        parent.InsertBefore(child, sibling); 
                        child = nextChild; 
                    }
                } 
                finally {
                    // Remove the dummy element.
                    parent.RemoveChild(dummy);
                } 

                // Remove the EncryptedData element 
                parent.RemoveChild(inputElement); 
            }
        } 

        //
        // public static methods
        // 

        // replaces the inputElement with the provided EncryptedData 
        public static void ReplaceElement (XmlElement inputElement, EncryptedData encryptedData, bool content) { 
            if (inputElement == null)
                throw new ArgumentNullException("inputElement"); 
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData");

            // First, get the XML representation of the EncryptedData object 
            XmlElement elemED = encryptedData.GetXml(inputElement.OwnerDocument);
            switch (content) { 
            case true: 
                // remove all children of the input element
                Utils.RemoveAllChildren(inputElement); 
                // then append the encrypted data as a child of the input element
                inputElement.AppendChild(elemED);
                break;
            case false: 
                XmlNode parentNode = inputElement.ParentNode;
                // remove the input element from the containing document 
                parentNode.ReplaceChild(elemED, inputElement); 
                break;
            } 
        }

        // wraps the supplied input key data using the provided symmetric algorithm
        public static byte[] EncryptKey (byte[] keyData, SymmetricAlgorithm symmetricAlgorithm) { 
            if (keyData == null)
                throw new ArgumentNullException("keyData"); 
            if (symmetricAlgorithm == null) 
                throw new ArgumentNullException("symmetricAlgorithm");
 
            if (symmetricAlgorithm is TripleDES) {
                // CMS Triple DES Key Wrap
                return SymmetricKeyWrap.TripleDESKeyWrapEncrypt(symmetricAlgorithm.Key, keyData);
            } else if (symmetricAlgorithm is Rijndael) { 
                // FIPS AES Key Wrap
                return SymmetricKeyWrap.AESKeyWrapEncrypt(symmetricAlgorithm.Key, keyData); 
            } 
            // throw an exception if the transform is not in the previous categories
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform")); 
        }


        // encrypts the supplied input key data using an RSA key and specifies whether we want to use OAEP 
        // padding or PKCS#1 v1.5 padding as described in the PKCS specification
        public static byte[] EncryptKey (byte[] keyData, RSA rsa, bool useOAEP) { 
            if (keyData == null) 
                throw new ArgumentNullException("keyData");
            if (rsa == null) 
                throw new ArgumentNullException("rsa");

            if (useOAEP) {
                RSAOAEPKeyExchangeFormatter rsaFormatter = new RSAOAEPKeyExchangeFormatter(rsa); 
                return rsaFormatter.CreateKeyExchange(keyData);
            } else { 
                RSAPKCS1KeyExchangeFormatter rsaFormatter = new RSAPKCS1KeyExchangeFormatter(rsa); 
                return rsaFormatter.CreateKeyExchange(keyData);
            } 
        }

        // decrypts the supplied wrapped key using the provided symmetric algorithm
        public static byte[] DecryptKey (byte[] keyData, SymmetricAlgorithm symmetricAlgorithm) { 
            if (keyData == null)
                throw new ArgumentNullException("keyData"); 
            if (symmetricAlgorithm == null) 
                throw new ArgumentNullException("symmetricAlgorithm");
 
            if (symmetricAlgorithm is TripleDES) {
                // CMS Triple DES Key Wrap
                return SymmetricKeyWrap.TripleDESKeyWrapDecrypt(symmetricAlgorithm.Key, keyData);
            } else if (symmetricAlgorithm is Rijndael) { 
                // FIPS AES Key Wrap
                return SymmetricKeyWrap.AESKeyWrapDecrypt(symmetricAlgorithm.Key, keyData); 
            } 
            // throw an exception if the transform is not in the previous categories
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform")); 
        }

        // decrypts the supplied data using an RSA key and specifies whether we want to use OAEP
        // padding or PKCS#1 v1.5 padding as described in the PKCS specification 
        public static byte[] DecryptKey (byte[] keyData, RSA rsa, bool useOAEP) {
            if (keyData == null) 
                throw new ArgumentNullException("keyData"); 
            if (rsa == null)
                throw new ArgumentNullException("rsa"); 

            if (useOAEP) {
                RSAOAEPKeyExchangeDeformatter rsaDeformatter = new RSAOAEPKeyExchangeDeformatter(rsa);
                return rsaDeformatter.DecryptKeyExchange(keyData); 
            } else {
                RSAPKCS1KeyExchangeDeformatter rsaDeformatter = new RSAPKCS1KeyExchangeDeformatter(rsa); 
                return rsaDeformatter.DecryptKeyExchange(keyData); 
            }
        } 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 

// 
// EncryptedXml.cs 
//
// 04/01/2002 
//

namespace System.Security.Cryptography.Xml
{ 
    using System;
    using System.Collections; 
    using System.IO; 
    using System.Net;
    using System.Security; 
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Policy;
    using System.Text; 
    using System.Xml;
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public class EncryptedXml {
 
        //
        // public constant Url identifiers used within the XML Encryption classes
        //
 
        public const string XmlEncNamespaceUrl = "http://www.w3.org/2001/04/xmlenc#";
        public const string XmlEncElementUrl = "http://www.w3.org/2001/04/xmlenc#Element"; 
        public const string XmlEncElementContentUrl = "http://www.w3.org/2001/04/xmlenc#Content"; 
        public const string XmlEncEncryptedKeyUrl = "http://www.w3.org/2001/04/xmlenc#EncryptedKey";
 
        //
        // Symmetric Block Encryption
        //
 
        public const string XmlEncDESUrl = "http://www.w3.org/2001/04/xmlenc#des-cbc";
        public const string XmlEncTripleDESUrl = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; 
        public const string XmlEncAES128Url = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; 
        public const string XmlEncAES256Url = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";
        public const string XmlEncAES192Url = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; 

        //
        // Key Transport
        // 

        public const string XmlEncRSA15Url = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; 
        public const string XmlEncRSAOAEPUrl = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; 

        // 
        // Symmetric Key Wrap
        //

        public const string XmlEncTripleDESKeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-tripledes"; 
        public const string XmlEncAES128KeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-aes128";
        public const string XmlEncAES256KeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; 
        public const string XmlEncAES192KeyWrapUrl = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; 

        // 
        // Message Digest
        //

        public const string XmlEncSHA256Url = "http://www.w3.org/2001/04/xmlenc#sha256"; 
        public const string XmlEncSHA512Url = "http://www.w3.org/2001/04/xmlenc#sha512";
 
        // 
        // private members
        // 

        private XmlDocument m_document;
        private Evidence m_evidence;
        private XmlResolver m_xmlResolver; 
        // hash table defining the key name mapping
        private const int m_capacity = 4; // 4 is a reasonable capacity for 
                                          // the key name mapping hash table 
        private Hashtable m_keyNameMapping;
        private PaddingMode m_padding; 
        private CipherMode m_mode;
        private Encoding m_encoding;
        private string m_recipient;
 
        //
        // public constructors 
        // 

        public EncryptedXml () : this (new XmlDocument()) {} 

        public EncryptedXml (XmlDocument document) : this (document, null) {}

        public EncryptedXml (XmlDocument document, Evidence evidence) { 
            m_document = document;
            m_evidence = evidence; 
            m_xmlResolver = null; 
            // set the default padding to ISO-10126
            m_padding = PaddingMode.ISO10126; 
            // set the default cipher mode to CBC
            m_mode = CipherMode.CBC;
            // By default the encoding is going to be UTF8
            m_encoding = Encoding.UTF8; 
            m_keyNameMapping = new Hashtable(m_capacity);
        } 
 
        //
        // public properties 
        //

        // The evidence of the document being loaded: will be used to resolve external URIs
        public Evidence DocumentEvidence { 
            get { return m_evidence; }
            set { m_evidence = value; } 
        } 

        // The resolver to use for external entities 
        public XmlResolver Resolver {
            get { return m_xmlResolver; }
            set { m_xmlResolver = value; }
        } 

        // The padding to be used. XML Encryption uses ISO 10126 
        // but it's nice to provide a way to extend this to include other forms of paddings 
        public PaddingMode Padding {
            get { return m_padding; } 
            set { m_padding = value; }
        }

        // The cipher mode to be used. XML Encryption uses CBC padding 
        // but it's nice to provide a way to extend this to include other cipher modes
        public CipherMode Mode { 
            get { return m_mode; } 
            set { m_mode = value; }
        } 

        // The encoding of the XML document
        public Encoding Encoding {
            get { return m_encoding; } 
            set { m_encoding = value; }
        } 
 
        // This is used to specify the EncryptedKey elements that should be considered
        // when an EncyptedData references an EncryptedKey using a CarriedKeyName and Recipient 
        public string Recipient {
            get {
                // an unspecified value for an XmlAttribute is String.Empty
                if (m_recipient == null) 
                    m_recipient = String.Empty;
                return m_recipient; 
            } 
            set { m_recipient = value; }
        } 

        //
        // private methods
        // 

        private byte[] GetCipherValue (CipherData cipherData) { 
            if (cipherData == null) 
                throw new ArgumentNullException("cipherData");
 
            WebResponse response = null;
            Stream inputStream = null;

            if (cipherData.CipherValue != null) { 
                return cipherData.CipherValue;
            } else if (cipherData.CipherReference != null) { 
                if (cipherData.CipherReference.CipherValue != null) 
                    return cipherData.CipherReference.CipherValue;
                Stream decInputStream = null; 
                // See if the CipherReference is a local URI
                if (cipherData.CipherReference.Uri.Length == 0) {
                    // self referenced Uri
                    string baseUri = (m_document == null ? null : m_document.BaseURI); 
                    TransformChain tc = cipherData.CipherReference.TransformChain;
                    decInputStream = tc.TransformToOctetStream(m_document, m_xmlResolver, baseUri); 
                } else if (cipherData.CipherReference.Uri[0] == '#') { 
                    string idref = Utils.ExtractIdFromLocalUri(cipherData.CipherReference.Uri);
                    // Serialize 
                    inputStream = new MemoryStream(m_encoding.GetBytes(GetIdElement(m_document, idref).OuterXml));
                    string baseUri = (m_document == null ? null : m_document.BaseURI);
                    TransformChain tc = cipherData.CipherReference.TransformChain;
                    decInputStream = tc.TransformToOctetStream(inputStream, m_xmlResolver, baseUri); 
                } else {
                    DownloadCipherValue(cipherData, out inputStream, out decInputStream, out response); 
                } 
                // read the output stream into a memory stream
                byte[] cipherValue = null; 
                using (MemoryStream ms = new MemoryStream()) {
                    Utils.Pump(decInputStream, ms);
                    cipherValue = ms.ToArray();
                    // Close the stream and return 
                    if (response != null)
                        response.Close(); 
                    if (inputStream != null) 
                        inputStream.Close();
                    decInputStream.Close(); 
                }

                // cache the cipher value for Perf reasons in case we call this routine twice
                cipherData.CipherReference.CipherValue = cipherValue; 
                return cipherValue;
            } 
 
            // Throw a CryptographicException if we were unable to retrieve the cipher data.
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingCipherData")); 
        }

        private void DownloadCipherValue (CipherData cipherData, out Stream inputStream, out Stream decInputStream, out WebResponse response) {
            // maybe a network stream, make sure we allow just what is needed!! 
            PermissionSet ps = SecurityManager.ResolvePolicy(m_evidence);
            ps.PermitOnly(); 
            WebRequest request = WebRequest.Create(cipherData.CipherReference.Uri); 
            if (request == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotResolved"), cipherData.CipherReference.Uri); 
            response = request.GetResponse();
            if (response == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotResolved"), cipherData.CipherReference.Uri);
            inputStream = response.GetResponseStream(); 
            if (inputStream == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotResolved"), cipherData.CipherReference.Uri); 
            TransformChain tc = cipherData.CipherReference.TransformChain; 
            decInputStream = tc.TransformToOctetStream(inputStream, m_xmlResolver, cipherData.CipherReference.Uri);
        } 

        //
        // public virtual methods
        // 

        // This describes how the application wants to associate id references to elements 
        public virtual XmlElement GetIdElement (XmlDocument document, string idValue) { 
            if (document == null)
                return null; 
            XmlElement elem = null;

            // Get the element with idValue
            elem = document.GetElementById(idValue); 
            if (elem != null)
                return elem; 
            elem = document.SelectSingleNode("//*[@Id=\"" + idValue + "\"]") as XmlElement; 
            if (elem != null)
                return elem; 
            elem = document.SelectSingleNode("//*[@id=\"" + idValue + "\"]") as XmlElement;
            if (elem != null)
                return elem;
            elem = document.SelectSingleNode("//*[@ID=\"" + idValue + "\"]") as XmlElement; 

            return elem; 
        } 

        // default behaviour is to look for the IV in the CipherValue 
        public virtual byte[] GetDecryptionIV (EncryptedData encryptedData, string symmetricAlgorithmUri) {
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData");
 
            int initBytesSize = 0;
            // If the Uri is not provided by the application, try to get it from the EncryptionMethod 
            if (symmetricAlgorithmUri == null) { 
                if (encryptedData.EncryptionMethod == null)
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingAlgorithm")); 
                symmetricAlgorithmUri = encryptedData.EncryptionMethod.KeyAlgorithm;
            }
            switch (symmetricAlgorithmUri) {
            case EncryptedXml.XmlEncDESUrl: 
            case EncryptedXml.XmlEncTripleDESUrl:
                initBytesSize = 8; 
                break; 
            case EncryptedXml.XmlEncAES128Url:
            case EncryptedXml.XmlEncAES192Url: 
            case EncryptedXml.XmlEncAES256Url:
                initBytesSize = 16;
                break;
            default: 
                // The Uri is not supported.
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriNotSupported")); 
            } 
            byte[] IV = new byte[initBytesSize];
            byte[] cipherValue = GetCipherValue(encryptedData.CipherData); 
            Buffer.BlockCopy(cipherValue, 0, IV, 0, IV.Length);
            return IV;
        }
 
        // default behaviour is to look for keys defined by an EncryptedKey clause
        // either directly or through a KeyInfoRetrievalMethod, and key names in the key mapping 
        public virtual SymmetricAlgorithm GetDecryptionKey (EncryptedData encryptedData, string symmetricAlgorithmUri) { 
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData"); 

            if (encryptedData.KeyInfo == null)
                return null;
            IEnumerator keyInfoEnum = encryptedData.KeyInfo.GetEnumerator(); 
            KeyInfoRetrievalMethod kiRetrievalMethod;
            KeyInfoName kiName; 
            KeyInfoEncryptedKey kiEncKey; 
            EncryptedKey ek = null;
 
            while (keyInfoEnum.MoveNext()) {
                kiName = keyInfoEnum.Current as KeyInfoName;
                if (kiName != null) {
                    // Get the decryption key from the key mapping 
                    string keyName = kiName.Value;
                    if ((SymmetricAlgorithm) m_keyNameMapping[keyName] != null) 
                        return (SymmetricAlgorithm) m_keyNameMapping[keyName]; 
                    // try to get it from a CarriedKeyName
                    XmlNamespaceManager nsm = new XmlNamespaceManager(m_document.NameTable); 
                    nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
                    XmlNodeList encryptedKeyList = m_document.SelectNodes("//enc:EncryptedKey", nsm);
                    if (encryptedKeyList != null) {
                        foreach (XmlNode encryptedKeyNode in encryptedKeyList) { 
                            XmlElement encryptedKeyElement = encryptedKeyNode as XmlElement;
                            EncryptedKey ek1 = new EncryptedKey(); 
                            ek1.LoadXml(encryptedKeyElement); 
                            if (ek1.CarriedKeyName == keyName && ek1.Recipient == this.Recipient) {
                                ek = ek1; 
                                break;
                            }
                        }
                    } 
                    break;
                } 
                kiRetrievalMethod = keyInfoEnum.Current as KeyInfoRetrievalMethod; 
                if (kiRetrievalMethod != null) {
                    string idref = Utils.ExtractIdFromLocalUri(kiRetrievalMethod.Uri); 
                    ek = new EncryptedKey();
                    ek.LoadXml(GetIdElement(m_document, idref));
                    break;
                } 
                kiEncKey = keyInfoEnum.Current as KeyInfoEncryptedKey;
                if (kiEncKey != null) { 
                    ek = kiEncKey.EncryptedKey; 
                    break;
                } 
            }

            // if we have an EncryptedKey, decrypt to get the symmetric key
            if (ek != null) { 
                // now process the EncryptedKey, loop recursively
                // If the Uri is not provided by the application, try to get it from the EncryptionMethod 
                if (symmetricAlgorithmUri == null) { 
                    if (encryptedData.EncryptionMethod == null)
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingAlgorithm")); 
                    symmetricAlgorithmUri = encryptedData.EncryptionMethod.KeyAlgorithm;
                }
                byte[] key = DecryptEncryptedKey(ek);
                if (key == null) 
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingDecryptionKey"));
 
                SymmetricAlgorithm symAlg = (SymmetricAlgorithm) CryptoConfig.CreateFromName(symmetricAlgorithmUri); 
                symAlg.Key = key;
                return symAlg; 
            }
            return null;
        }
 
        // Try to decrypt the EncryptedKey given the key mapping
        public virtual byte[] DecryptEncryptedKey (EncryptedKey encryptedKey) { 
            if (encryptedKey == null) 
                throw new ArgumentNullException("encryptedKey");
            if (encryptedKey.KeyInfo == null) 
                return null;

            IEnumerator keyInfoEnum = encryptedKey.KeyInfo.GetEnumerator();
            KeyInfoName kiName; 
            KeyInfoX509Data kiX509Data;
            KeyInfoRetrievalMethod kiRetrievalMethod; 
            KeyInfoEncryptedKey kiEncKey; 
            EncryptedKey ek = null;
            bool fOAEP = false; 

            while (keyInfoEnum.MoveNext()) {
                kiName = keyInfoEnum.Current as KeyInfoName;
                if (kiName != null) { 
                    // Get the decryption key from the key mapping
                    string keyName = kiName.Value; 
                    Object kek = m_keyNameMapping[keyName]; 
                    if (kek != null) {
                        // kek is either a SymmetricAlgorithm or an RSA key, otherwise, we wouldn't be able to insert it in the hash table 
                        if (kek is SymmetricAlgorithm)
                            return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, (SymmetricAlgorithm) kek);

                        // kek is an RSA key: get fOAEP from the algorithm, default to false 
                        fOAEP = (encryptedKey.EncryptionMethod != null && encryptedKey.EncryptionMethod.KeyAlgorithm == EncryptedXml.XmlEncRSAOAEPUrl);
                        return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, (RSA) kek, fOAEP); 
                    } 
                    break;
                } 
                kiX509Data = keyInfoEnum.Current as KeyInfoX509Data;
                if (kiX509Data != null) {
                    X509Certificate2Collection collection = Utils.BuildBagOfCerts(kiX509Data, CertUsageType.Decryption);
                    foreach (X509Certificate2 certificate in collection) { 
                        RSA privateKey = certificate.PrivateKey as RSA;
                        if (privateKey != null) { 
                            fOAEP = (encryptedKey.EncryptionMethod != null && encryptedKey.EncryptionMethod.KeyAlgorithm == EncryptedXml.XmlEncRSAOAEPUrl); 
                            return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, privateKey, fOAEP);
                        } 
                    }
                    break;
                }
                kiRetrievalMethod = keyInfoEnum.Current as KeyInfoRetrievalMethod; 
                if (kiRetrievalMethod != null) {
                    string idref = Utils.ExtractIdFromLocalUri(kiRetrievalMethod.Uri); 
                    ek = new EncryptedKey(); 
                    ek.LoadXml(GetIdElement(m_document, idref));
                    return DecryptEncryptedKey(ek); 
                }
                kiEncKey = keyInfoEnum.Current as KeyInfoEncryptedKey;
                if (kiEncKey != null) {
                    ek = kiEncKey.EncryptedKey; 
                    // recursively process EncryptedKey elements
                    byte[] encryptionKey = DecryptEncryptedKey(ek); 
                    if (encryptionKey != null) { 
                        // this is a symmetric algorithm for sure
                        SymmetricAlgorithm symAlg = (SymmetricAlgorithm) CryptoConfig.CreateFromName(encryptedKey.EncryptionMethod.KeyAlgorithm); 
                        symAlg.Key = encryptionKey;
                        return EncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, symAlg);
                    }
                } 
            }
            return null; 
        } 

        // 
        // public methods
        //

        // defines a key name mapping. Default behaviour is to require the key object 
        // to be an RSA key or a SymmetricAlgorithm
        public void AddKeyNameMapping (string keyName, Object keyObject) { 
            if (keyName == null) 
                throw new ArgumentNullException("keyName");
            if (keyObject == null) 
                throw new ArgumentNullException("keyObject");

            if (!(keyObject is SymmetricAlgorithm) && !(keyObject is RSA))
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform")); 
            m_keyNameMapping.Add(keyName, keyObject);
        } 
 
        public void ClearKeyNameMappings () {
            m_keyNameMapping.Clear(); 
        }

        // Encrypts the given element with the certificate specified. The certificate is added as
        // an X509Data KeyInfo to an EncryptedKey (AES session key) generated randomly. 
        public EncryptedData Encrypt (XmlElement inputElement, X509Certificate2 certificate) {
            if (inputElement == null) 
                throw new ArgumentNullException("inputElement"); 
            if (certificate == null)
                throw new ArgumentNullException("certificate"); 

            if (X509Utils.OidToAlgId(certificate.PublicKey.Oid.Value) != CAPI.CALG_RSA_KEYX)
                throw new NotSupportedException(SecurityResources.GetResourceString("NotSupported_KeyAlgorithm"));
 
            // Create the EncryptedData object, using an AES-256 session key by default.
            EncryptedData ed = new EncryptedData(); 
            ed.Type = EncryptedXml.XmlEncElementUrl; 
            ed.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
 
            // Include the certificate in the EncryptedKey KeyInfo.
            EncryptedKey ek = new EncryptedKey();
            ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
            ek.KeyInfo.AddClause(new KeyInfoX509Data(certificate)); 

            // Create a random AES session key and encrypt it with the public key associated with the certificate. 
            RijndaelManaged rijn = new RijndaelManaged(); 
            ek.CipherData.CipherValue = EncryptedXml.EncryptKey(rijn.Key, certificate.PublicKey.Key as RSA, false);
 
            // Encrypt the input element with the random session key that we've created above.
            KeyInfoEncryptedKey kek = new KeyInfoEncryptedKey(ek);
            ed.KeyInfo.AddClause(kek);
            ed.CipherData.CipherValue = EncryptData(inputElement, rijn, false); 

            return ed; 
        } 

        // Encrypts the given element with the key name specified. A corresponding key name mapping 
        // has to be defined before calling this method. The key name is added as
        // a KeyNameInfo KeyInfo to an EncryptedKey (AES session key) generated randomly.
        public EncryptedData Encrypt (XmlElement inputElement, string keyName) {
            if (inputElement == null) 
                throw new ArgumentNullException("inputElement");
            if (keyName == null) 
                throw new ArgumentNullException("keyName"); 

            Object encryptionKey = null; 
            if (m_keyNameMapping != null)
                encryptionKey = m_keyNameMapping[keyName];

            if (encryptionKey == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingEncryptionKey"));
 
            // kek is either a SymmetricAlgorithm or an RSA key, otherwise, we wouldn't be able to insert it in the hash table 
            SymmetricAlgorithm symKey = encryptionKey as SymmetricAlgorithm;
            RSA rsa = encryptionKey as RSA; 

            // Create the EncryptedData object, using an AES-256 session key by default.
            EncryptedData ed = new EncryptedData();
            ed.Type = EncryptedXml.XmlEncElementUrl; 
            ed.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
 
            // Include the key name in the EncryptedKey KeyInfo. 
            string encryptionMethod = null;
            if (symKey == null) { 
                encryptionMethod = EncryptedXml.XmlEncRSA15Url;
            } else if (symKey is TripleDES) {
                // CMS Triple DES Key Wrap
                encryptionMethod = EncryptedXml.XmlEncTripleDESKeyWrapUrl; 
            } else if (symKey is Rijndael) {
                // FIPS AES Key Wrap 
                switch (symKey.KeySize) { 
                case 128:
                    encryptionMethod = EncryptedXml.XmlEncAES128KeyWrapUrl; 
                    break;
                case 192:
                    encryptionMethod = EncryptedXml.XmlEncAES192KeyWrapUrl;
                    break; 
                case 256:
                    encryptionMethod = EncryptedXml.XmlEncAES256KeyWrapUrl; 
                    break; 
                }
            } else { 
                // throw an exception if the transform is not in the previous categories
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform"));
            }
            EncryptedKey ek = new EncryptedKey(); 
            ek.EncryptionMethod = new EncryptionMethod(encryptionMethod);
            ek.KeyInfo.AddClause(new KeyInfoName(keyName)); 
 
            // Create a random AES session key and encrypt it with the public key associated with the certificate.
            RijndaelManaged rijn = new RijndaelManaged(); 
            ek.CipherData.CipherValue = (symKey == null ? EncryptedXml.EncryptKey(rijn.Key, rsa, false) : EncryptedXml.EncryptKey(rijn.Key, symKey));

            // Encrypt the input element with the random session key that we've created above.
            KeyInfoEncryptedKey kek = new KeyInfoEncryptedKey(ek); 
            ed.KeyInfo.AddClause(kek);
            ed.CipherData.CipherValue = EncryptData(inputElement, rijn, false); 
 
            return ed;
        } 

        // decrypts the document using the defined key mapping in GetDecryptionKey
        // The behaviour of this method can be extended because GetDecryptionKey is virtual
        // the document is decrypted in place 
        public void DecryptDocument () {
            // Look for all EncryptedData elements and decrypt them 
            XmlNamespaceManager nsm = new XmlNamespaceManager(m_document.NameTable); 
            nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            XmlNodeList encryptedDataList = m_document.SelectNodes("//enc:EncryptedData", nsm); 
            if (encryptedDataList != null) {
                foreach (XmlNode encryptedDataNode in encryptedDataList) {
                    XmlElement encryptedDataElement = encryptedDataNode as XmlElement;
                    EncryptedData ed = new EncryptedData(); 
                    ed.LoadXml(encryptedDataElement);
                    SymmetricAlgorithm symAlg = GetDecryptionKey(ed, null); 
                    if (symAlg == null) 
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingDecryptionKey"));
                    byte[] decrypted = DecryptData(ed, symAlg); 
                    ReplaceData(encryptedDataElement, decrypted);
                }
            }
        } 

        // encrypts the supplied arbitrary data 
        public byte[] EncryptData (byte[] plaintext, SymmetricAlgorithm symmetricAlgorithm) { 
            if (plaintext == null)
                throw new ArgumentNullException("plaintext"); 
            if (symmetricAlgorithm == null)
                throw new ArgumentNullException("symmetricAlgorithm");

            // save the original symmetric algorithm 
            CipherMode origMode = symmetricAlgorithm.Mode;
            PaddingMode origPadding = symmetricAlgorithm.Padding; 
 
            byte[] cipher = null;
            try { 
                symmetricAlgorithm.Mode = m_mode;
                symmetricAlgorithm.Padding = m_padding;

                ICryptoTransform enc = symmetricAlgorithm.CreateEncryptor(); 
                cipher = enc.TransformFinalBlock(plaintext, 0, plaintext.Length);
            } finally { 
                // now restore the original symmetric algorithm 
                symmetricAlgorithm.Mode = origMode;
                symmetricAlgorithm.Padding = origPadding; 
            }

            byte[] output = null;
            if (m_mode == CipherMode.ECB) { 
                output = cipher;
            } else { 
                byte[] IV = symmetricAlgorithm.IV; 
                output = new byte[cipher.Length + IV.Length];
                Buffer.BlockCopy(IV, 0, output, 0, IV.Length); 
                Buffer.BlockCopy(cipher, 0, output, IV.Length, cipher.Length);
            }
            return output;
        } 

        // encrypts the supplied input element 
        public byte[] EncryptData (XmlElement inputElement, SymmetricAlgorithm symmetricAlgorithm, bool content) { 
            if (inputElement == null)
                throw new ArgumentNullException("inputElement"); 
            if (symmetricAlgorithm == null)
                throw new ArgumentNullException("symmetricAlgorithm");

            byte[] plainText = (content ? m_encoding.GetBytes(inputElement.InnerXml) : m_encoding.GetBytes(inputElement.OuterXml)); 
            return EncryptData(plainText, symmetricAlgorithm);
        } 
 
        // decrypts the supplied EncryptedData
        public byte[] DecryptData (EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm) { 
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData");
            if (symmetricAlgorithm == null)
                throw new ArgumentNullException("symmetricAlgorithm"); 

            // get the cipher value and decrypt 
            byte[] cipherValue = GetCipherValue(encryptedData.CipherData); 

            // save the original symmetric algorithm 
            CipherMode origMode = symmetricAlgorithm.Mode;
            PaddingMode origPadding = symmetricAlgorithm.Padding;
            byte[] origIV = symmetricAlgorithm.IV;
 
            // read the IV from cipherValue
            byte[] decryptionIV = null; 
            if (m_mode != CipherMode.ECB) 
                decryptionIV = GetDecryptionIV(encryptedData, null);
 
            byte[] output = null;
            try {
                int lengthIV = 0;
                if (decryptionIV != null) { 
                    symmetricAlgorithm.IV = decryptionIV;
                    lengthIV = decryptionIV.Length; 
                } 
                symmetricAlgorithm.Mode = m_mode;
                symmetricAlgorithm.Padding = m_padding; 

                ICryptoTransform dec = symmetricAlgorithm.CreateDecryptor();
                output = dec.TransformFinalBlock(cipherValue, lengthIV, cipherValue.Length - lengthIV);
            } finally { 
                // now restore the original symmetric algorithm
                symmetricAlgorithm.Mode = origMode; 
                symmetricAlgorithm.Padding = origPadding; 
                symmetricAlgorithm.IV = origIV;
            } 

            return output;
        }
 
        // This method replaces an EncryptedData element with the decrypted sequence of bytes
        public void ReplaceData (XmlElement inputElement, byte[] decryptedData) { 
            if (inputElement == null) 
                throw new ArgumentNullException ("inputElement");
            if (decryptedData == null) 
                throw new ArgumentNullException ("decryptedData");

            XmlNode parent = inputElement.ParentNode;
            if (parent.NodeType == XmlNodeType.Document) { 
                // We are replacing the root element, so we will just replace it with the decrypted bytes.
                parent.InnerXml = m_encoding.GetString(decryptedData); 
            } else { 
                XmlNode dummy = parent.OwnerDocument.CreateElement(parent.Prefix, parent.LocalName, parent.NamespaceURI);
 
                try {
                    parent.AppendChild(dummy);

                    // Replace the children of the dummy node with the sequence of bytes passed in. 
                    // The string will be parsed into DOM objects in the context of the parent of the EncryptedData element.
                    dummy.InnerXml = m_encoding.GetString(decryptedData); 
 
                    // Move the children of the dummy node up to the parent.
                    XmlNode child = dummy.FirstChild; 
                    XmlNode sibling = inputElement.NextSibling;

                    XmlNode nextChild = null;
                    while (child != null) { 
                        nextChild = child.NextSibling;
                        parent.InsertBefore(child, sibling); 
                        child = nextChild; 
                    }
                } 
                finally {
                    // Remove the dummy element.
                    parent.RemoveChild(dummy);
                } 

                // Remove the EncryptedData element 
                parent.RemoveChild(inputElement); 
            }
        } 

        //
        // public static methods
        // 

        // replaces the inputElement with the provided EncryptedData 
        public static void ReplaceElement (XmlElement inputElement, EncryptedData encryptedData, bool content) { 
            if (inputElement == null)
                throw new ArgumentNullException("inputElement"); 
            if (encryptedData == null)
                throw new ArgumentNullException("encryptedData");

            // First, get the XML representation of the EncryptedData object 
            XmlElement elemED = encryptedData.GetXml(inputElement.OwnerDocument);
            switch (content) { 
            case true: 
                // remove all children of the input element
                Utils.RemoveAllChildren(inputElement); 
                // then append the encrypted data as a child of the input element
                inputElement.AppendChild(elemED);
                break;
            case false: 
                XmlNode parentNode = inputElement.ParentNode;
                // remove the input element from the containing document 
                parentNode.ReplaceChild(elemED, inputElement); 
                break;
            } 
        }

        // wraps the supplied input key data using the provided symmetric algorithm
        public static byte[] EncryptKey (byte[] keyData, SymmetricAlgorithm symmetricAlgorithm) { 
            if (keyData == null)
                throw new ArgumentNullException("keyData"); 
            if (symmetricAlgorithm == null) 
                throw new ArgumentNullException("symmetricAlgorithm");
 
            if (symmetricAlgorithm is TripleDES) {
                // CMS Triple DES Key Wrap
                return SymmetricKeyWrap.TripleDESKeyWrapEncrypt(symmetricAlgorithm.Key, keyData);
            } else if (symmetricAlgorithm is Rijndael) { 
                // FIPS AES Key Wrap
                return SymmetricKeyWrap.AESKeyWrapEncrypt(symmetricAlgorithm.Key, keyData); 
            } 
            // throw an exception if the transform is not in the previous categories
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform")); 
        }


        // encrypts the supplied input key data using an RSA key and specifies whether we want to use OAEP 
        // padding or PKCS#1 v1.5 padding as described in the PKCS specification
        public static byte[] EncryptKey (byte[] keyData, RSA rsa, bool useOAEP) { 
            if (keyData == null) 
                throw new ArgumentNullException("keyData");
            if (rsa == null) 
                throw new ArgumentNullException("rsa");

            if (useOAEP) {
                RSAOAEPKeyExchangeFormatter rsaFormatter = new RSAOAEPKeyExchangeFormatter(rsa); 
                return rsaFormatter.CreateKeyExchange(keyData);
            } else { 
                RSAPKCS1KeyExchangeFormatter rsaFormatter = new RSAPKCS1KeyExchangeFormatter(rsa); 
                return rsaFormatter.CreateKeyExchange(keyData);
            } 
        }

        // decrypts the supplied wrapped key using the provided symmetric algorithm
        public static byte[] DecryptKey (byte[] keyData, SymmetricAlgorithm symmetricAlgorithm) { 
            if (keyData == null)
                throw new ArgumentNullException("keyData"); 
            if (symmetricAlgorithm == null) 
                throw new ArgumentNullException("symmetricAlgorithm");
 
            if (symmetricAlgorithm is TripleDES) {
                // CMS Triple DES Key Wrap
                return SymmetricKeyWrap.TripleDESKeyWrapDecrypt(symmetricAlgorithm.Key, keyData);
            } else if (symmetricAlgorithm is Rijndael) { 
                // FIPS AES Key Wrap
                return SymmetricKeyWrap.AESKeyWrapDecrypt(symmetricAlgorithm.Key, keyData); 
            } 
            // throw an exception if the transform is not in the previous categories
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_NotSupportedCryptographicTransform")); 
        }

        // decrypts the supplied data using an RSA key and specifies whether we want to use OAEP
        // padding or PKCS#1 v1.5 padding as described in the PKCS specification 
        public static byte[] DecryptKey (byte[] keyData, RSA rsa, bool useOAEP) {
            if (keyData == null) 
                throw new ArgumentNullException("keyData"); 
            if (rsa == null)
                throw new ArgumentNullException("rsa"); 

            if (useOAEP) {
                RSAOAEPKeyExchangeDeformatter rsaDeformatter = new RSAOAEPKeyExchangeDeformatter(rsa);
                return rsaDeformatter.DecryptKeyExchange(keyData); 
            } else {
                RSAPKCS1KeyExchangeDeformatter rsaDeformatter = new RSAPKCS1KeyExchangeDeformatter(rsa); 
                return rsaDeformatter.DecryptKeyExchange(keyData); 
            }
        } 
    }
}

// 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