You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中用xml-crypto为XML签名添加KeyInfo引用?

解决xml-crypto中XML签名包含KeyInfo引用的问题

问题分析

你的核心矛盾在于:KeyInfo是签名过程中动态生成的元素,在第一次computeSignature之前它并不存在于原始XML中,所以直接用XPath查找会失败;而重复调用computeSignature会生成新的签名节点,导致重复签名。

解决方案

1. 正确添加KeyInfo引用的核心思路

要在同一个签名中包含KeyInfo的引用,不能在签名后补加引用,而是要预先构造KeyInfo并嵌入到待签名XML中,或者直接通过URI引用KeyInfo的ID(无需XPath),确保所有待签名元素在签名计算前都已存在。

2. 无重复签名的实现代码

private localElemName: string;
private xmlBody: any;
private xmlBuilder = new XMLBuilder({
    attributeNamePrefix: '@_',
    ignoreAttributes: false,
    format: true,
});

createDigitalSignature(xmlObj: any){
    this.xmlBody = this.xmlBuilder.build(xmlObj);
    this.localElemName = this.getKey(xmlObj);
    const keyInfoId = '_8401036a-cd29-4f5b-a48a-9ecf4d515d98';
    
    // 临时生成KeyInfo内容
    const tempSig = new SignedXml({ 
        publicCert: fs.readFileSync(path.join(__dirname, "../../certificates_keys", "BANK0201_TRANSPORT_TEST.pem")) 
    });
    const keyInfoContent = tempSig.getKeyInfoContent({ prefix: 'ds' });
    
    // 将KeyInfo嵌入原始XML(需根据你的XML结构调整嵌入位置)
    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(this.xmlBody, 'application/xml');
    const targetParentNode = xmlDoc.querySelector(`//*[local-name(.)='Document']`);
    const keyInfoNode = parser.parseFromString(keyInfoContent, 'application/xml').firstChild;
    keyInfoNode.setAttribute('Id', keyInfoId);
    targetParentNode.appendChild(keyInfoNode);
    
    // 生成带KeyInfo的待签名XML
    const xmlWithKeyInfo = new XMLSerializer().serializeToString(xmlDoc);
    
    // 创建正式签名实例,添加所有引用
    const sig = new SignedXml({ 
        privateKey: fs.readFileSync(path.join(__dirname, "../../certificates_keys", "BANK0201.key")), 
        publicCert: fs.readFileSync(path.join(__dirname, "../../certificates_keys", "BANK0201_TRANSPORT_TEST.pem")) 
    });
    
    // 引用业务目标元素
    sig.addReference({
        xpath: `//*[local-name(.)='${this.localElemName}']`,
        isEmptyUri: true,
        transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature","http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
        digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256",
    });
    
    // 引用KeyInfo(通过URI直接关联ID,无需XPath)
    sig.addReference({
        uri: `#${keyInfoId}`,
        transforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
        digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
    });
    
    sig.canonicalizationAlgorithm = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
    sig.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
    
    // 一次性完成签名计算
    sig.computeSignature(xmlWithKeyInfo, {
        prefix: "ds",
        location: { reference: `//*[local-name(.)='${this.localElemName}']`, action: "after" },
    });
    
    return sig.getSignedXml();
}

getKey(xmlObj: xmlObject){
    return Object.keys(xmlObj.Document).pop();
}

关键注意点

  • 提前嵌入KeyInfo:通过临时签名实例生成标准KeyInfo内容,手动添加全局唯一ID后嵌入原始XML,确保签名前元素已存在,解决XPath查找失败问题
  • URI替代XPath:对KeyInfo的引用直接使用#ID格式,符合XML-DSig标准,比XPath定位更可靠
  • 单次签名计算:所有引用添加完成后只调用一次computeSignature,避免生成重复签名节点

排查建议

  • 若签名后XML结构不符合预期,需调整KeyInfo的嵌入位置(修改targetParentNode的选择逻辑)
  • 确保KeyInfo的Id属性全局唯一,避免与XML中其他元素ID冲突
  • 检查证书、密钥文件路径是否正确,程序是否有读取权限

内容的提问来源于stack exchange,提问作者Dennis Koech Kipkorir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:34:55