如何:使用数字签名为 XML 文档签名

可以使用 System.Security.Cryptography.Xml 命名空间中的类通过数字签名对 XML 文档或部分 XML 文档进行签名。 使用 XML 数字签名 (XMLDSIG),你可以验证签名后的数据没有被更改。 有关 XMLDSIG 标准的详细信息,请参阅万维网联盟 (W3C) 建议 XML 签名语法和处理

注意

本文中的代码适用于 Windows。

此过程中的代码示例演示了如何对整个 XML 文档进行数字签名,以及如何将签名附加到文档中的 <Signature> 元素中。 该示例创建一个 RSA 签名密钥,并将该密钥添加到安全密钥容器,然后使用该密钥对 XML 文档进行数字签名。 然后可以检索该密码来验证 XML 数字签名,或使用它对另一个 XML 文档进行签名。

有关如何使用此过程验证创建的 XML 数字签名的信息,请参阅 如何:验证 XML 文档的数字签名

对 XML 文档进行数字签名

  1. 创建 CspParameters 对象,并指定密钥容器的名称。

    CspParameters cspParams = new()
    {
        KeyContainerName = "XML_DSIG_RSA_KEY"
    };
    
    Dim cspParams As New CspParameters With {
        .KeyContainerName = "XML_DSIG_RSA_KEY"
    }
    
  2. 使用 RSACryptoServiceProvider 类生成一个非对称密钥。 当将 CspParameters 对象传递 RSACryptoServiceProvider 类的构造函数时,密钥将自动保存在密钥容器中。 此密钥将用于对 XML 文档签名。

    RSACryptoServiceProvider rsaKey = new(cspParams);
    
    Dim rsaKey As New RSACryptoServiceProvider(cspParams)
    
  3. 通过从磁盘加载 XML 文件来创建 XmlDocument 对象。 XmlDocument 对象包含要加密的 XML 元素。

    XmlDocument xmlDoc = new()
    {
        // Load an XML file into the XmlDocument object.
        PreserveWhitespace = true
    };
    xmlDoc.Load("test.xml");
    
    ' Load an XML file into the XmlDocument object.
    Dim xmlDoc As New XmlDocument With {
        .PreserveWhitespace = True
    }
    xmlDoc.Load("test.xml")
    
  4. 创建一个新的 SignedXml 对象,并向其传递 XmlDocument 对象。

    SignedXml signedXml = new(xmlDoc)
    {
    
    Dim signedXml As New SignedXml(xmlDoc)
    
  5. 将签名 RSA 密钥添加到 SignedXml 对象。

        SigningKey = rsaKey
    };
    
    signedXml.SigningKey = rsaKey
    
  6. 创建说明签名内容的 Reference 对象。 若要对整个文档进行签名,请将 Uri 属性设置为 ""

    // Create a reference to be signed.
    Reference reference = new()
    {
        Uri = ""
    };
    
    ' Create a reference to be signed.
    Dim reference As New Reference()
    reference.Uri = ""
    
  7. XmlDsigEnvelopedSignatureTransform 对象添加到 Reference 对象中。 变换使验证程序可以使用与签名工具所用的相同方式表示 XML 数据。 可以采用不同的方式表示 XML 数据,因此这一步对于验证至关重要。

    XmlDsigEnvelopedSignatureTransform env = new();
    reference.AddTransform(env);
    
    Dim env As New XmlDsigEnvelopedSignatureTransform()
    reference.AddTransform(env)
    
  8. Reference 对象添加到 SignedXml 对象中。

    signedXml.AddReference(reference);
    
    signedXml.AddReference(reference)
    
  9. 通过调用 ComputeSignature 方法计算签名。

    signedXml.ComputeSignature();
    
    signedXml.ComputeSignature()
    
  10. 检索签名(一个 <Signature> 元素)的 XML 表示形式,并将其保存到一个新的 XmlElement 对象中。

    XmlElement xmlDigitalSignature = signedXml.GetXml();
    
    Dim xmlDigitalSignature As XmlElement = signedXml.GetXml()
    
  11. 将元素追加到 XmlDocument 对象中。

    xmlDoc.DocumentElement?.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
    
    xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, True))
    
  12. 保存文档。

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

示例

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

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

[SupportedOSPlatform("Windows")]
public class SignXML
{
    public static void Main(String[] args)
    {
        try
        {
            // Create a new CspParameters object to specify
            // a key container.
            CspParameters cspParams = new()
            {
                KeyContainerName = "XML_DSIG_RSA_KEY"
            };

            // Create a new RSA signing key and save it in the container.
            RSACryptoServiceProvider rsaKey = new(cspParams);

            // Create a new XML document.
            XmlDocument xmlDoc = new()
            {
                // Load an XML file into the XmlDocument object.
                PreserveWhitespace = true
            };
            xmlDoc.Load("test.xml");

            // Sign the XML document.
            SignXml(xmlDoc, rsaKey);

            Console.WriteLine("XML file signed.");

            // Save the document.
            xmlDoc.Save("test.xml");
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

    // Sign an XML file.
    // This document cannot be verified unless the verifying
    // code has the key with which it was signed.
    public static void SignXml(XmlDocument xmlDoc, RSA rsaKey)
    {
        // Check arguments.
        if (xmlDoc == null)
            throw new ArgumentException(null, nameof(xmlDoc));
        if (rsaKey == null)
            throw new ArgumentException(null, nameof(rsaKey));

        // Create a SignedXml object.
        SignedXml signedXml = new(xmlDoc)
        {

            // Add the key to the SignedXml document.
            SigningKey = rsaKey
        };

        // Create a reference to be signed.
        Reference reference = new()
        {
            Uri = ""
        };

        // Add an enveloped transformation to the reference.
        XmlDsigEnvelopedSignatureTransform env = new();
        reference.AddTransform(env);

        // Add the reference to the SignedXml object.
        signedXml.AddReference(reference);

        // Compute the signature.
        signedXml.ComputeSignature();

        // Get the XML representation of the signature and save
        // it to an XmlElement object.
        XmlElement xmlDigitalSignature = signedXml.GetXml();

        // Append the element to the XML document.
        xmlDoc.DocumentElement?.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
    }
}
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml
Imports System.Xml

Module SignXML
    Sub Main(ByVal args() As String)
        Try
            ' Create a new CspParameters object to specify
            ' a key container.
            Dim cspParams As New CspParameters With {
                .KeyContainerName = "XML_DSIG_RSA_KEY"
            }
            ' Create a new RSA signing key and save it in the container. 
            Dim rsaKey As New RSACryptoServiceProvider(cspParams)
            ' Create a new XML document.
            ' Load an XML file into the XmlDocument object.
            Dim xmlDoc As New XmlDocument With {
                .PreserveWhitespace = True
            }
            xmlDoc.Load("test.xml")
            ' Sign the XML document. 
            SignXml(xmlDoc, rsaKey)

            Console.WriteLine("XML file signed.")

            ' Save the document.
            xmlDoc.Save("test.xml")
        Catch e As Exception
            Console.WriteLine(e.Message)
        End Try
    End Sub

    ' Sign an XML file. 
    ' This document cannot be verified unless the verifying 
    ' code has the key with which it was signed.
    Sub SignXml(ByVal xmlDoc As XmlDocument, ByVal rsaKey As RSA)
        ' Check arguments.
        If xmlDoc Is Nothing Then
            Throw New ArgumentException(
                "The XML doc cannot be nothing.", NameOf(xmlDoc))
        End If
        If rsaKey Is Nothing Then
            Throw New ArgumentException(
                "The RSA key cannot be nothing.", NameOf(rsaKey))
        End If
        ' Create a SignedXml object.
        Dim signedXml As New SignedXml(xmlDoc)
        ' Add the key to the SignedXml document.
        signedXml.SigningKey = rsaKey
        ' Create a reference to be signed.
        Dim reference As New Reference()
        reference.Uri = ""
        ' Add an enveloped transformation to the reference.
        Dim env As New XmlDsigEnvelopedSignatureTransform()
        reference.AddTransform(env)
        ' Add the reference to the SignedXml object.
        signedXml.AddReference(reference)
        ' Compute the signature.
        signedXml.ComputeSignature()
        ' Get the XML representation of the signature and save
        ' it to an XmlElement object.
        Dim xmlDigitalSignature As XmlElement = signedXml.GetXml()
        ' Append the element to the XML document.
        xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, True))
    End Sub
End Module

编译代码

.NET 安全性

切勿用纯文本存储或传输非对称密钥对的私钥。 有关对称和非对称加密密钥的详细信息,请参阅 生成加密和解密的密钥

切勿将私钥直接嵌入到源代码中。 可通过使用 Ildasm.exe(IL 反汇编程序) 或在文本编辑器(如记事本)中打开程序集的方式轻松从程序集中读取嵌入的密钥。

另请参阅