Bagikan melalui


How to: Encrypt XML Elements with Asymmetric Keys

You can use the classes in the System.Security.Cryptography.Xml namespace to encrypt an element within an XML document. XML Encryption is a standard way to exchange or store encrypted XML data, without worrying about the data being easily read. For more information about the XML Encryption standard, see the World Wide Web Consortium (W3C) specification for XML Encryption located at http://www.w3.org/TR/xmldsig-core/.

You can use XML Encryption to replace any XML element or document with an <EncryptedData> element that contains the encrypted XML data. The <EncryptedData> element can also contain sub elements that include information about the keys and processes used during encryption. XML Encryption allows a document to contain multiple encrypted elements and allows an element to be encrypted multiple times. The code example in this procedure shows how to create an <EncryptedData> element along with several other sub elements that you can use later during decryption.

This example encrypts an XML element using two keys. It generates an RSA public/private key pair and saves the key pair to a secure key container. The example then creates a separate session key using the Advanced Encryption Standard (AES) algorithm, also called the Rijndael algorithm. The example uses the AES session key to encrypt the XML document and then uses the RSA public key to encrypt the AES session key. Finally, the example saves the encrypted AES session key and the encrypted XML data to the XML document within a new <EncryptedData> element.

To decrypt the XML element, you retrieve the RSA private key from the key container, use it to decrypt the session key, and then use the session key to decrypt the document. For more information about how to decrypt an XML element that was encrypted using this procedure, see How to: Decrypt XML Elements with Asymmetric Keys.

This example is appropriate for situations where multiple applications need to share encrypted data or where an application needs to save encrypted data between the times that it runs.

To encrypt an XML element with an asymmetric key

  1. Create a CspParameters object and specify the name of the key container.

    Dim cspParams As New CspParameters()
    cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
    
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
    
  2. Generate a symmetric key using the RSACryptoServiceProvider class. The key is automatically saved to the key container when you pass the CspParameters object to the constructor of the RSACryptoServiceProvider class. This key will be used to encrypt the AES session key and can be retrieved later to decrypt it.

    Dim rsaKey As New RSACryptoServiceProvider(cspParams)
    
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    
  3. Create an XmlDocument object by loading an XML file from disk. The XmlDocument object contains the XML element to encrypt.

    ' Create an XmlDocument object. 
    Dim xmlDoc As New XmlDocument()
    
    ' Load an XML file into the XmlDocument object. 
    Try
        xmlDoc.PreserveWhitespace = True
        xmlDoc.Load("test.xml")
    Catch e As Exception
        Console.WriteLine(e.Message)
    End Try
    
    // Create an XmlDocument object.
    XmlDocument xmlDoc = new XmlDocument();
    
    // Load an XML file into the XmlDocument object. 
    try
    {
        xmlDoc.PreserveWhitespace = true;
        xmlDoc.Load("test.xml");
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }
    
  4. Find the specified element in the XmlDocument object and create a new XmlElement object to represent the element you want to encrypt. In this example, the "creditcard" element is encrypted.

    Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(EncryptionElement)(0) '
    
    ' Throw an XmlException if the element was not found. 
    If elementToEncrypt Is Nothing Then 
        Throw New XmlException("The specified element was not found")
    End If
    
    XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
    
    // Throw an XmlException if the element was not found. 
    if (elementToEncrypt == null)
    {
        throw new XmlException("The specified element was not found");
    
    }
    
  5. Create a new session key using the RijndaelManaged class. This key will encrypt the XML element, and then be encrypted itself and placed in the XML document.

    ' Create a 256 bit Rijndael key.
    sessionKey = New RijndaelManaged()
    sessionKey.KeySize = 256
    
    // Create a 256 bit Rijndael key.
    sessionKey = new RijndaelManaged();
    sessionKey.KeySize = 256;
    
  6. Create a new instance of the EncryptedXml class and use it to encrypt the specified element using the session key. The EncryptData method returns the encrypted element as an array of encrypted bytes.

    Dim eXml As New EncryptedXml()
    
    Dim encryptedElement As Byte() = eXml.EncryptData(elementToEncrypt, sessionKey, False)
    
    EncryptedXml eXml = new EncryptedXml();
    
    byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
    
  7. Construct an EncryptedData object and populate it with the URL identifier of the encrypted XML element. This URL identifier lets a decrypting party know that the XML contains an encrypted element. You can use the XmlEncElementUrl field to specify the URL identifier. The plaintext XML element will be replaced by an <EncryptedData> element encapsulated by this EncryptedData object.

    Dim edElement As New EncryptedData()
    edElement.Type = EncryptedXml.XmlEncElementUrl
    edElement.Id = EncryptionElementID
    
    EncryptedData edElement = new EncryptedData();
    edElement.Type = EncryptedXml.XmlEncElementUrl;
    edElement.Id = EncryptionElementID;
    
  8. Create an EncryptionMethod object that is initialized to the URL identifier of the cryptographic algorithm used to generate the session key. Pass the EncryptionMethod object to the EncryptionMethod property.

    edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
    
    edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
    
  9. Create an EncryptedKey object to contain the encrypted session key. Encrypt the session key, add it to the EncryptedKey object, and enter a session key name and key identifier URL.

    Dim ek As New EncryptedKey()
    
    Dim encryptedKey As Byte() = EncryptedXml.EncryptKey(sessionKey.Key, Alg, False)
    
    ek.CipherData = New CipherData(encryptedKey)
    
    ek.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncRSA15Url)
    
    EncryptedKey ek = new EncryptedKey();
    
    byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);
    
    ek.CipherData = new CipherData(encryptedKey);
    
    ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
    
  10. Create a new DataReference object that maps the encrypted data to a particular session key. This optional step allows you to easily specify that multiple parts of an XML document were encrypted by a single key.

    Dim dRef As New DataReference()
    
    ' Specify the EncryptedData URI.
    dRef.Uri = "#" + EncryptionElementID
    
    ' Add the DataReference to the EncryptedKey.
    ek.AddReference(dRef)
    
    DataReference dRef = new DataReference();
    
    // Specify the EncryptedData URI.
    dRef.Uri = "#" + EncryptionElementID;
    
    // Add the DataReference to the EncryptedKey.
    ek.AddReference(dRef);
    
  11. Add the encrypted key to the EncryptedData object.

    edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
    
    edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
    
  12. Create a new KeyInfo object to specify the name of the RSA key. Add it to the EncryptedData object. This helps the decrypting party identify the correct asymmetric key to use when decrypting the session key.

    ' Create a new KeyInfoName element. 
    Dim kin As New KeyInfoName()
    
    ' Specify a name for the key.
    kin.Value = KeyName
    
    ' Add the KeyInfoName element to the 
    ' EncryptedKey object.
    ek.KeyInfo.AddClause(kin)
    
    // Create a new KeyInfoName element.
    KeyInfoName kin = new KeyInfoName();
    
    // Specify a name for the key.
    kin.Value = KeyName;
    
    // Add the KeyInfoName element to the 
    // EncryptedKey object.
    ek.KeyInfo.AddClause(kin);
    
  13. Add the encrypted element data to the EncryptedData object.

    edElement.CipherData.CipherValue = encryptedElement
    
    edElement.CipherData.CipherValue = encryptedElement;
    
  14. Replace the element from the original XmlDocument object with the EncryptedData element.

    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
    
    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
    
  15. Save the XmlDocument object.

    xmlDoc.Save("test.xml")
    
    xmlDoc.Save("test.xml");
    

Example

This example assumes that a file named "test.xml" exists in the same directory as the compiled program. It also assumes that "test.xml" contains a "creditcard" element. You can place the following XML into a file called test.xml and use it with this example.

<root>
    <creditcard>
        <number>19834209</number>
        <expiry>02/02/2002</expiry>
    </creditcard>
</root>
Imports System
Imports System.Xml
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml



Class Program

    Shared Sub Main(ByVal args() As String)
        ' Create an XmlDocument object. 
        Dim xmlDoc As New XmlDocument()

        ' Load an XML file into the XmlDocument object. 
        Try
            xmlDoc.PreserveWhitespace = True
            xmlDoc.Load("test.xml")
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try 
        ' Create a new CspParameters object to specify 
        ' a key container. 
        Dim cspParams As New CspParameters()
        cspParams.KeyContainerName = "XML_ENC_RSA_KEY" 
        ' Create a new RSA key and save it in the container.  This key will encrypt 
        ' a symmetric key, which will then be encryped in the XML document. 
        Dim rsaKey As New RSACryptoServiceProvider(cspParams)
        Try 
            ' Encrypt the "creditcard" element.
            Encrypt(xmlDoc, "creditcard", "EncryptedElement1", rsaKey, "rsaKey")


            ' Save the XML document.
            xmlDoc.Save("test.xml")
            ' Display the encrypted XML to the console.
            Console.WriteLine("Encrypted XML:")
            Console.WriteLine()
            Console.WriteLine(xmlDoc.OuterXml)
            Decrypt(xmlDoc, rsaKey, "rsaKey")
            xmlDoc.Save("test.xml")
            ' Display the encrypted XML to the console.
            Console.WriteLine()
            Console.WriteLine("Decrypted XML:")
            Console.WriteLine()
            Console.WriteLine(xmlDoc.OuterXml)

        Catch e As Exception
            Console.WriteLine(e.Message)
        Finally 
            ' Clear the RSA key.
            rsaKey.Clear()
        End Try


        Console.ReadLine()

    End Sub 'Main


    Public Shared Sub Encrypt(ByVal Doc As XmlDocument, ByVal EncryptionElement As String, ByVal EncryptionElementID As String, ByVal Alg As RSA, ByVal KeyName As String)
        ' Check the arguments. 
        If Doc Is Nothing Then 
            Throw New ArgumentNullException("Doc")
        End If 
        If EncryptionElement Is Nothing Then 
            Throw New ArgumentNullException("EncryptionElement")
        End If 
        If EncryptionElementID Is Nothing Then 
            Throw New ArgumentNullException("EncryptionElementID")
        End If 
        If Alg Is Nothing Then 
            Throw New ArgumentNullException("Alg")
        End If 
        If KeyName Is Nothing Then 
            Throw New ArgumentNullException("KeyName")
        End If 
        '////////////////////////////////////////////// 
        ' Find the specified element in the XmlDocument 
        ' object and create a new XmlElemnt object. 
        '////////////////////////////////////////////// 
        Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(EncryptionElement)(0) '

        ' Throw an XmlException if the element was not found. 
        If elementToEncrypt Is Nothing Then 
            Throw New XmlException("The specified element was not found")
        End If 
        Dim sessionKey As RijndaelManaged = Nothing 

        Try 
            '//////////////////////////////////////////////// 
            ' Create a new instance of the EncryptedXml class 
            ' and use it to encrypt the XmlElement with the 
            ' a new random symmetric key. 
            '//////////////////////////////////////////////// 
            ' Create a 256 bit Rijndael key.
            sessionKey = New RijndaelManaged()
            sessionKey.KeySize = 256
            Dim eXml As New EncryptedXml()

            Dim encryptedElement As Byte() = eXml.EncryptData(elementToEncrypt, sessionKey, False)
            '////////////////////////////////////////////// 
            ' Construct an EncryptedData object and populate 
            ' it with the desired encryption information. 
            '////////////////////////////////////////////// 
            Dim edElement As New EncryptedData()
            edElement.Type = EncryptedXml.XmlEncElementUrl
            edElement.Id = EncryptionElementID
            ' Create an EncryptionMethod element so that the 
            ' receiver knows which algorithm to use for decryption.
            edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
            ' Encrypt the session key and add it to an EncryptedKey element. 
            Dim ek As New EncryptedKey()

            Dim encryptedKey As Byte() = EncryptedXml.EncryptKey(sessionKey.Key, Alg, False)

            ek.CipherData = New CipherData(encryptedKey)

            ek.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncRSA15Url)
            ' Create a new DataReference element 
            ' for the KeyInfo element.  This optional 
            ' element specifies which EncryptedData 
            ' uses this key.  An XML document can have 
            ' multiple EncryptedData elements that use 
            ' different keys. 
            Dim dRef As New DataReference()

            ' Specify the EncryptedData URI.
            dRef.Uri = "#" + EncryptionElementID

            ' Add the DataReference to the EncryptedKey.
            ek.AddReference(dRef)
            ' Add the encrypted key to the 
            ' EncryptedData object.
            edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
            ' Set the KeyInfo element to specify the 
            ' name of the RSA key. 
            ' Create a new KeyInfoName element. 
            Dim kin As New KeyInfoName()

            ' Specify a name for the key.
            kin.Value = KeyName

            ' Add the KeyInfoName element to the 
            ' EncryptedKey object.
            ek.KeyInfo.AddClause(kin)
            ' Add the encrypted element data to the 
            ' EncryptedData object.
            edElement.CipherData.CipherValue = encryptedElement
            '////////////////////////////////////////////////// 
            ' Replace the element from the original XmlDocument 
            ' object with the EncryptedData element. 
            '//////////////////////////////////////////////////
            EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
        Catch e As Exception
            ' re-throw the exception. 
            Throw e
        Finally 
            If Not (sessionKey Is Nothing) Then
                sessionKey.Clear()
            End If 
        End Try 

    End Sub 'Encrypt



    Public Shared Sub Decrypt(ByVal Doc As XmlDocument, ByVal Alg As RSA, ByVal KeyName As String)
        ' Check the arguments.   
        If Doc Is Nothing Then 
            Throw New ArgumentNullException("Doc")
        End If 
        If Alg Is Nothing Then 
            Throw New ArgumentNullException("Alg")
        End If 
        If KeyName Is Nothing Then 
            Throw New ArgumentNullException("KeyName")
        End If 
        ' Create a new EncryptedXml object. 
        Dim exml As New EncryptedXml(Doc)

        ' Add a key-name mapping. 
        ' This method can only decrypt documents 
        ' that present the specified key name.
        exml.AddKeyNameMapping(KeyName, Alg)

        ' Decrypt the element.
        exml.DecryptDocument()

    End Sub 'Decrypt 
End Class 'Program
using System;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;

class Program
{
    static void Main(string[] args)
    {
        // Create an XmlDocument object.
        XmlDocument xmlDoc = new XmlDocument();

        // Load an XML file into the XmlDocument object. 
        try
        {
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.Load("test.xml");
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }

        // Create a new CspParameters object to specify 
        // a key container.
        CspParameters cspParams = new CspParameters();
        cspParams.KeyContainerName = "XML_ENC_RSA_KEY";

        // Create a new RSA key and save it in the container.  This key will encrypt 
        // a symmetric key, which will then be encryped in the XML document.
        RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);

        try
        {
            // Encrypt the "creditcard" element.
            Encrypt(xmlDoc, "creditcard", "EncryptedElement1", rsaKey, "rsaKey");


            // Save the XML document.
            xmlDoc.Save("test.xml");

            // Display the encrypted XML to the console.
            Console.WriteLine("Encrypted XML:");
            Console.WriteLine();
            Console.WriteLine(xmlDoc.OuterXml);
            Decrypt(xmlDoc, rsaKey, "rsaKey");
            xmlDoc.Save("test.xml");
            // Display the encrypted XML to the console.
            Console.WriteLine();
            Console.WriteLine("Decrypted XML:");
            Console.WriteLine();
            Console.WriteLine(xmlDoc.OuterXml);

        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        finally
        {
            // Clear the RSA key.
            rsaKey.Clear();
        }


        Console.ReadLine();
    }

    public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName)
    {
        // Check the arguments. 
        if (Doc == null)
            throw new ArgumentNullException("Doc");
        if (ElementToEncrypt == null)
            throw new ArgumentNullException("ElementToEncrypt");
        if (EncryptionElementID == null)
            throw new ArgumentNullException("EncryptionElementID");
        if (Alg == null)
            throw new ArgumentNullException("Alg");
        if (KeyName == null)
            throw new ArgumentNullException("KeyName");

        //////////////////////////////////////////////// 
        // Find the specified element in the XmlDocument 
        // object and create a new XmlElemnt object. 
        ////////////////////////////////////////////////
        XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;

        // Throw an XmlException if the element was not found. 
        if (elementToEncrypt == null)
        {
            throw new XmlException("The specified element was not found");

        }
        RijndaelManaged sessionKey = null;

        try
        {
            ////////////////////////////////////////////////// 
            // Create a new instance of the EncryptedXml class 
            // and use it to encrypt the XmlElement with the 
            // a new random symmetric key. 
            ////////////////////////////////////////////////// 

            // Create a 256 bit Rijndael key.
            sessionKey = new RijndaelManaged();
            sessionKey.KeySize = 256;

            EncryptedXml eXml = new EncryptedXml();

            byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
            //////////////////////////////////////////////// 
            // Construct an EncryptedData object and populate 
            // it with the desired encryption information. 
            ////////////////////////////////////////////////

            EncryptedData edElement = new EncryptedData();
            edElement.Type = EncryptedXml.XmlEncElementUrl;
            edElement.Id = EncryptionElementID;
            // Create an EncryptionMethod element so that the 
            // receiver knows which algorithm to use for decryption.

            edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
            // Encrypt the session key and add it to an EncryptedKey element.
            EncryptedKey ek = new EncryptedKey();

            byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);

            ek.CipherData = new CipherData(encryptedKey);

            ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);

            // Create a new DataReference element 
            // for the KeyInfo element.  This optional 
            // element specifies which EncryptedData 
            // uses this key.  An XML document can have 
            // multiple EncryptedData elements that use 
            // different keys.
            DataReference dRef = new DataReference();

            // Specify the EncryptedData URI.
            dRef.Uri = "#" + EncryptionElementID;

            // Add the DataReference to the EncryptedKey.
            ek.AddReference(dRef);
            // Add the encrypted key to the 
            // EncryptedData object.

            edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
            // Set the KeyInfo element to specify the 
            // name of the RSA key. 


            // Create a new KeyInfoName element.
            KeyInfoName kin = new KeyInfoName();

            // Specify a name for the key.
            kin.Value = KeyName;

            // Add the KeyInfoName element to the 
            // EncryptedKey object.
            ek.KeyInfo.AddClause(kin);
            // Add the encrypted element data to the 
            // EncryptedData object.
            edElement.CipherData.CipherValue = encryptedElement;
            //////////////////////////////////////////////////// 
            // Replace the element from the original XmlDocument 
            // object with the EncryptedData element. 
            ////////////////////////////////////////////////////
            EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
        }
        catch (Exception e)
        {
            // re-throw the exception. 
            throw e;
        }
        finally
        {
            if (sessionKey != null)
            {
                sessionKey.Clear();
            }

        }

    }

    public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
    {
        // Check the arguments.   
        if (Doc == null)
            throw new ArgumentNullException("Doc");
        if (Alg == null)
            throw new ArgumentNullException("Alg");
        if (KeyName == null)
            throw new ArgumentNullException("KeyName");

        // Create a new EncryptedXml object.
        EncryptedXml exml = new EncryptedXml(Doc);

        // Add a key-name mapping. 
        // This method can only decrypt documents 
        // that present the specified key name.
        exml.AddKeyNameMapping(KeyName, Alg);

        // Decrypt the element.
        exml.DecryptDocument();

    }

}

Compiling the Code

Security

Never store a symmetric cryptographic key in plaintext or transfer a symmetric key between machines in plaintext. Additionally, never store or transfer the private key of an asymmetric key pair in plaintext. For more information about symmetric and asymmetric cryptographic keys, see Generating Keys for Encryption and Decryption.

Never embed a key directly into your source code. Embedded keys can be easily read from an assembly using the MSIL Disassembler (Ildasm.exe) or by opening the assembly in a text editor such as Notepad.

When you are done using a cryptographic key, clear it from memory by setting each byte to zero or by calling the Clear method of the managed cryptography class. Cryptographic keys can sometimes be read from memory by a debugger or read from a hard drive if the memory location is paged to disk.

See Also

Tasks

How to: Decrypt XML Elements with Asymmetric Keys

Reference

System.Security.Cryptography.Xml

Other Resources

XML Encryption and Digital Signatures