XML签名是基于XML格式的数字签名标准,用于对XML文档或文档中的部分内容生成数字签名,验证数据的来源合法性和内容完整性,广泛应用于Web服务、数据交换等场景。

XML签名的核心组成
一个标准的XML签名主要包含三个核心部分,各部分承担不同的功能:
- SignedInfo:包含被签名的数据引用、签名算法、规范转换规则等信息,是签名计算的核心输入。
- SignatureValue:对SignedInfo内容进行哈希和加密后得到的签名值,用于后续验证。
- KeyInfo:可选部分,包含验证签名所需的公钥信息,方便接收方获取验证密钥。
XML签名的工作流程
1. 签名生成流程
签名生成方需要按照以下步骤完成XML签名的生成:
- 确定需要签名的XML内容,可以是整个文档,也可以是文档中的特定节点。
- 对被签名的内容进行规范转换,统一XML的编码、换行、属性顺序等格式,避免格式差异导致哈希值不同。
- 使用哈希算法(如SHA256)计算被签名内容的摘要值。
- 将摘要值、签名算法、引用信息等组装成SignedInfo元素。
- 使用发送方的私钥对SignedInfo元素进行加密,得到SignatureValue。
- 将SignedInfo、SignatureValue、KeyInfo等元素组装成完整的XML签名,插入到原XML文档中。
2. 签名验证流程
接收方收到带签名的XML文档后,验证流程如下:
- 从XML文档中提取SignedInfo、SignatureValue、KeyInfo等签名相关信息。
- 按照签名中指定的规范转换规则,重新计算被签名内容的摘要值。
- 使用发送方的公钥对SignatureValue进行解密,得到原始计算的SignedInfo摘要值。
- 对比解密得到的摘要值和重新计算的SignedInfo摘要值,如果一致则签名验证通过。
XML签名如何确保完整性
XML签名通过多层机制保障数据的完整性,避免数据在传输或存储过程中被篡改:
- 哈希摘要校验:被签名的内容会先通过哈希算法生成固定长度的摘要,哪怕内容只修改一个字符,生成的摘要值都会完全不同,接收方重新计算摘要即可发现内容变化。
- 私钥加密保护:SignedInfo包含被签名内容的引用和摘要信息,使用私钥加密后,任何对内容的篡改都会导致解密后的摘要和重新计算的摘要不匹配,验证无法通过。
- 规范转换统一格式:签名过程会对XML内容进行规范转换,消除不同解析器对XML格式处理的差异,避免非内容修改导致的签名验证失败,同时保证只有内容本身的变化才会影响签名结果。
代码示例:Java实现XML签名生成与验证
以下是使用Java的XML签名API实现简单XML签名生成和验证的示例代码:
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class XmlSignatureDemo {
// 生成XML签名
public static void signXml(String xmlPath, String keyStorePath, String keyStorePass, String keyAlias, String keyPass) throws Exception {
// 加载XML文档
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(xmlPath));
// 加载密钥库获取私钥和证书
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(keyStorePath), keyStorePass.toCharArray());
PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keyPass.toCharArray());
X509Certificate cert = (X509Certificate) ks.getCertificate(keyAlias);
// 创建签名工厂
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// 创建规范转换方法
C14NMethod c14nMethod = fac.newC14NMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
// 创建签名算法
SignatureMethod signatureMethod = fac.newSignatureMethod(SignatureMethod.RSA_SHA256, null);
// 创建摘要算法
DigestMethod digestMethod = fac.newDigestMethod(DigestMethod.SHA256, null);
// 创建对文档的引用
List<Transform> transforms = new ArrayList<>();
transforms.add(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
Reference ref = fac.newReference("", digestMethod, transforms, null, null);
List<Reference> refs = Collections.singletonList(ref);
// 创建SignedInfo
SignedInfo signedInfo = fac.newSignedInfo(c14nMethod, signatureMethod, refs);
// 创建KeyInfo
KeyInfoFactory kif = fac.getKeyInfoFactory();
X509Data x509Data = kif.newX509Data(Collections.singletonList(cert));
KeyInfo keyInfo = kif.newKeyInfo(Collections.singletonList(x509Data));
// 创建XML签名
XMLSignature signature = fac.newXMLSignature(signedInfo, keyInfo);
DOMSignContext signContext = new DOMSignContext(privateKey, doc.getDocumentElement());
signature.sign(signContext);
System.out.println("XML签名生成完成");
}
// 验证XML签名
public static boolean validateXml(String xmlPath, String keyStorePath, String keyStorePass, String keyAlias) throws Exception {
// 加载XML文档
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(xmlPath));
// 加载密钥库获取公钥
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(keyStorePath), keyStorePass.toCharArray());
PublicKey publicKey = ks.getCertificate(keyAlias).getPublicKey();
// 查找签名节点
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("未找到XML签名节点");
}
// 创建验证上下文
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0));
// 验证签名
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
return signature.validate(valContext);
}
}
常见问题说明
在实际使用XML签名时,需要注意以下几点:
- 规范转换规则需要和接收方协商一致,否则容易出现签名验证失败的问题。
- 私钥需要妥善保管,一旦私钥泄露,攻击者可以伪造合法的XML签名。
- 如果只需要校验完整性不需要验证来源,也可以采用对称加密的方式生成签名,但安全性会低于非对称加密方案。
XML_signature完整性校验数字签名XML_加密修改时间:2026-06-14 22:57:39