Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / ManagedLibraries / Security / System / Security / Cryptography / Xml / EncryptedXml.cs / 1305376 / 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.GetStandardSandbox(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 || symKey is Aes) { // 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're replacing the root element, but we can't just wholesale replace the owner // document's InnerXml, since we need to preserve any other top-level XML elements (such as // comments or the XML entity declaration. Instead, create a new document with the // decrypted XML, import it into the existing document, and replace just the root element. XmlDocument importDocument = new XmlDocument(); importDocument.LoadXml(m_encoding.GetString(decryptedData)); XmlNode importedNode = inputElement.OwnerDocument.ImportNode(importDocument.DocumentElement, true); parent.RemoveChild(inputElement); parent.AppendChild(importedNode); } 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 || symmetricAlgorithm is Aes) { // 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 || symmetricAlgorithm is Aes) { // 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.GetStandardSandbox(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 || symKey is Aes) { // 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're replacing the root element, but we can't just wholesale replace the owner // document's InnerXml, since we need to preserve any other top-level XML elements (such as // comments or the XML entity declaration. Instead, create a new document with the // decrypted XML, import it into the existing document, and replace just the root element. XmlDocument importDocument = new XmlDocument(); importDocument.LoadXml(m_encoding.GetString(decryptedData)); XmlNode importedNode = inputElement.OwnerDocument.ImportNode(importDocument.DocumentElement, true); parent.RemoveChild(inputElement); parent.AppendChild(importedNode); } 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 || symmetricAlgorithm is Aes) { // 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 || symmetricAlgorithm is Aes) { // 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SessionSymmetricMessageSecurityProtocolFactory.cs
- VisualTreeUtils.cs
- MonitoringDescriptionAttribute.cs
- SqlDataSourceCustomCommandPanel.cs
- WebPartDescriptionCollection.cs
- KeyGestureValueSerializer.cs
- OptionalColumn.cs
- ServiceOperationParameter.cs
- LabelDesigner.cs
- BufferBuilder.cs
- DataGridViewRowEventArgs.cs
- ObjectSpanRewriter.cs
- PagePropertiesChangingEventArgs.cs
- LessThan.cs
- CodeLinePragma.cs
- XhtmlBasicLabelAdapter.cs
- counter.cs
- ReadOnlyHierarchicalDataSource.cs
- AggregateNode.cs
- SoapEnumAttribute.cs
- SamlSubject.cs
- BulletDecorator.cs
- ZoneMembershipCondition.cs
- ProviderUtil.cs
- WindowsTitleBar.cs
- AdPostCacheSubstitution.cs
- DataGridViewAutoSizeColumnsModeEventArgs.cs
- EnumerableRowCollection.cs
- InputProviderSite.cs
- Types.cs
- LocatorGroup.cs
- FilePrompt.cs
- BaseDataBoundControlDesigner.cs
- XmlElementAttributes.cs
- TextBoxAutomationPeer.cs
- HelpInfo.cs
- DynamicObject.cs
- ConnectionManagementSection.cs
- IncrementalCompileAnalyzer.cs
- MediaCommands.cs
- BitmapPalette.cs
- PersonalizationStateQuery.cs
- followingsibling.cs
- EditorPart.cs
- Compiler.cs
- SmtpSection.cs
- AutomationEventArgs.cs
- XmlnsDefinitionAttribute.cs
- BaseProcessor.cs
- File.cs
- SqlFacetAttribute.cs
- securitycriticaldataformultiplegetandset.cs
- DesignParameter.cs
- XmlDomTextWriter.cs
- CqlParser.cs
- DataIdProcessor.cs
- HScrollProperties.cs
- sqlcontext.cs
- Odbc32.cs
- CustomAttributeSerializer.cs
- DataGridViewHeaderCell.cs
- HtmlControl.cs
- Point.cs
- PinnedBufferMemoryStream.cs
- DocumentGridPage.cs
- COM2FontConverter.cs
- PreloadedPackages.cs
- CustomValidator.cs
- BitmapFrameEncode.cs
- PasswordRecovery.cs
- Configuration.cs
- SqlFactory.cs
- SemanticBasicElement.cs
- BreakSafeBase.cs
- TimelineCollection.cs
- RelOps.cs
- EventBindingService.cs
- _ConnectStream.cs
- GB18030Encoding.cs
- InkCanvas.cs
- PermissionListSet.cs
- DbConnectionPoolGroupProviderInfo.cs
- CfgParser.cs
- SocketPermission.cs
- DataRecordInternal.cs
- HttpCacheVary.cs
- DecimalConstantAttribute.cs
- ServiceOperation.cs
- ResourceExpressionBuilder.cs
- CDSsyncETWBCLProvider.cs
- FontStyleConverter.cs
- DataGridViewColumnCollection.cs
- ClientRequest.cs
- DiscoveryDocumentSerializer.cs
- SpeechSeg.cs
- BitmapEffectrendercontext.cs
- RuleInfoComparer.cs
- TypeUtils.cs
- EntityProviderServices.cs
- UnmanagedBitmapWrapper.cs