如何:使用 X.509 证书加密 XML 元素

可以使用 System.Security.Cryptography.Xml 命名空间中的类加密 XML 文档内的元素。 XML 加密是交换或存储加密的 XML 数据的一种标准方式,使用后就无需担心数据被轻易读取。 有关 XML 加密标准的详细信息,请参阅万维网联合会 (W3C) 对于 XML 加密的规范,该规范位于 https://www.w3.org/TR/xmldsig-core/

可以使用 XML 加密将任何 XML 元素或文档替换为包含加密 XML 数据的 <EncryptedData> 元素。 <EncryptedData> 元素可以包含一些子元素来收入关于加密期间使用的密钥和进程的信息。 XML 加密允许文档包含多个加密元素,并允许对一个元素进行多次加密。 此过程中的代码示例演示了如何创建一个 <EncryptedData> 元素和几个其他子元素,以便以后在解密过程中使用。

此示例使用两个密钥对 XML 元素进行加密。 此示例以编程方式检索该证书,并通过 Encrypt 方法将其用于加密 XML 元素。 在内部,Encrypt 方法创建一个单独的会话密钥,并将其用于加密 XML 文档。 此方法对会话密钥进行加密,并将它与加密的 XML 一起保存在一个新的 <EncryptedData> 元素中。

要解密 XML 元素,只需调用DecryptDocument 方法,它会从存储区中自动检索 X.509 证书并执行必要的解密。 有关如何对按照此过程加密的 XML 元素进行解密的详细信息,请参阅如何:用 X.509 证书对 XML 元素进行解密

此示例适用于以下情况:多个应用程序需要共享加密数据,或应用程序需要保存它各次运行之间的加密数据。

使用 X.509 证书对 XML 元素进行加密

要运行此示例,你需要创建一个测试证书并将其保存在证书存储中。 针对该任务提供的说明仅适用于 Windows 证书创建工具 (Makecert.exe)

  1. 使用 Makecert.exe 生成 X.509 测试证书,并将其置于本地用户存储中。 必须生成一个交换密钥,且该密钥必须可导出。 运行以下命令:

    makecert -r -pe -n "CN=XML_ENC_TEST_CERT" -b 01/01/2020 -e 01/01/2025 -sky exchange -ss my  
    
  2. 创建 X509Store 对象,并进行初始化,以便打开当前用户存储区。

    X509Store store = new X509Store(StoreLocation.CurrentUser);
    
    Dim store As New X509Store(StoreLocation.CurrentUser)
    
  3. 在只读模式下打开存储区。

    store.Open(OpenFlags.ReadOnly);
    
    store.Open(OpenFlags.ReadOnly)
    
  4. 使用存储区中的所有证书初始化 X509Certificate2Collection

    X509Certificate2Collection certCollection = store.Certificates;
    
    Dim certCollection As X509Certificate2Collection = store.Certificates
    
  5. 枚举存储区中的证书,找到具有相应名称的证书。 在此示例中,证书名为 "CN=XML_ENC_TEST_CERT"

    X509Certificate2 cert = null;
    
    // Loop through each certificate and find the certificate
    // with the appropriate name.
    foreach (X509Certificate2 c in certCollection)
    {
        if (c.Subject == "CN=XML_ENC_TEST_CERT")
        {
            cert = c;
    
            break;
        }
    }
    
    Dim cert As X509Certificate2 = Nothing
    
    ' Loop through each certificate and find the certificate 
    ' with the appropriate name.
    Dim c As X509Certificate2
    For Each c In certCollection
        If c.Subject = "CN=XML_ENC_TEST_CERT" Then
            cert = c
    
            Exit For
        End If
    Next c
    
  6. 找到该证书后,关闭存储区。

    store.Close();
    
    store.Close()
    
  7. 通过从磁盘加载 XML 文件来创建 XmlDocument 对象。 XmlDocument 对象包含要加密的 XML 元素。

    XmlDocument xmlDoc = new XmlDocument();
    
    Dim xmlDoc As New XmlDocument()
    
  8. XmlDocument 对象中查找指定元素,并创建一个新的 XmlElement 对象来表示想要加密的元素。 在此示例中,加密了 "creditcard" 元素。

    XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
    
    Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(ElementToEncryptName)(0)
    
    
  9. 创建 EncryptedXml 类的新实例,并通过它使用 X.509 证书对指定元素进行加密。 Encrypt 方法以 EncryptedData 对象的形式返回加密元素。

    EncryptedXml eXml = new EncryptedXml();
    
    // Encrypt the element.
    EncryptedData edElement = eXml.Encrypt(elementToEncrypt, Cert);
    
    Dim eXml As New EncryptedXml()
    
    ' Encrypt the element.
    Dim edElement As EncryptedData = eXml.Encrypt(elementToEncrypt, Cert)
    
  10. 将原始 XmlDocument 对象中的元素替换为 EncryptedData 元素。

    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
    
    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
    
  11. 保存 XmlDocument 对象。

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

示例

此示例假定名为 "test.xml" 的文件与已编译程序存在于同一目录中。 它还假定 "test.xml" 包含 "creditcard" 元素。 可以将以下 XML 放在名为 test.xml 的文件,并将其用于以下示例。

<root>  
    <creditcard>  
        <number>19834209</number>  
        <expiry>02/02/2002</expiry>  
    </creditcard>  
</root>  
using System;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography.X509Certificates;

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

            // Load an XML file into the XmlDocument object.
            xmlDoc.PreserveWhitespace = true;
            xmlDoc.Load("test.xml");

            // Open the X.509 "Current User" store in read only mode.
            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);

            // Place all certificates in an X509Certificate2Collection object.
            X509Certificate2Collection certCollection = store.Certificates;

            X509Certificate2 cert = null;

            // Loop through each certificate and find the certificate
            // with the appropriate name.
            foreach (X509Certificate2 c in certCollection)
            {
                if (c.Subject == "CN=XML_ENC_TEST_CERT")
                {
                    cert = c;

                    break;
                }
            }

            if (cert == null)
            {
                throw new CryptographicException("The X.509 certificate could not be found.");
            }

            // Close the store.
            store.Close();

            // Encrypt the "creditcard" element.
            Encrypt(xmlDoc, "creditcard", cert);

            // 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);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

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

        ////////////////////////////////////////////////
        // Find the specified element in the XmlDocument
        // object and create a new XmlElement 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");
        }

        //////////////////////////////////////////////////
        // Create a new instance of the EncryptedXml class
        // and use it to encrypt the XmlElement with the
        // X.509 Certificate.
        //////////////////////////////////////////////////

        EncryptedXml eXml = new EncryptedXml();

        // Encrypt the element.
        EncryptedData edElement = eXml.Encrypt(elementToEncrypt, Cert);

        ////////////////////////////////////////////////////
        // Replace the element from the original XmlDocument
        // object with the EncryptedData element.
        ////////////////////////////////////////////////////
        EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
    }
}
Imports System.Xml
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml
Imports System.Security.Cryptography.X509Certificates

Module Program

    Sub Main(ByVal args() As String)
        Try
            ' Create an XmlDocument object.
            Dim xmlDoc As New XmlDocument()
            ' Load an XML file into the XmlDocument object.
            xmlDoc.PreserveWhitespace = True
            xmlDoc.Load("test.xml")

            ' Open the X.509 "Current User" store in read only mode.
            Dim store As New X509Store(StoreLocation.CurrentUser)
            store.Open(OpenFlags.ReadOnly)
            ' Place all certificates in an X509Certificate2Collection object.
            Dim certCollection As X509Certificate2Collection = store.Certificates
            Dim cert As X509Certificate2 = Nothing

            ' Loop through each certificate and find the certificate 
            ' with the appropriate name.
            Dim c As X509Certificate2
            For Each c In certCollection
                If c.Subject = "CN=XML_ENC_TEST_CERT" Then
                    cert = c

                    Exit For
                End If
            Next c
            If cert Is Nothing Then
                Throw New CryptographicException("The X.509 certificate could not be found.")
            End If

            ' Close the store.
            store.Close()
            ' Encrypt the "creditcard" element.
            Encrypt(xmlDoc, "creditcard", cert)

            ' 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)

        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try

    End Sub


    Sub Encrypt(ByVal Doc As XmlDocument, ByVal ElementToEncryptName As String, ByVal Cert As X509Certificate2)
        ' Check the arguments.  
        ArgumentNullException.ThrowIfNull(Doc)
        ArgumentNullException.ThrowIfNull(ElementToEncryptName)
        ArgumentNullException.ThrowIfNull(Cert)
        ''''''''''''''''''''''''''''''''''''''''''''''''
        ' Find the specified element in the XmlDocument
        ' object and create a new XmlElemnt object.
        ''''''''''''''''''''''''''''''''''''''''''''''''
        Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(ElementToEncryptName)(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

        ''''''''''''''''''''''''''''''''''''''''''''''''
        ' Create a new instance of the EncryptedXml class 
        ' and use it to encrypt the XmlElement with the 
        ' X.509 Certificate.
        ''''''''''''''''''''''''''''''''''''''''''''''''
        Dim eXml As New EncryptedXml()

        ' Encrypt the element.
        Dim edElement As EncryptedData = eXml.Encrypt(elementToEncrypt, Cert)
        ''''''''''''''''''''''''''''''''''''''''''''''''
        ' Replace the element from the original XmlDocument
        ' object with the EncryptedData element.
        ''''''''''''''''''''''''''''''''''''''''''''''''
        EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
    End Sub
End Module

编译代码

.NET 安全性

此示例中使用的 X.509 证书仅用于测试目的。 应用程序应使用受信任的证书授权生成的 X.509 证书。

另请参阅