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

如何将带数字签名的XML嵌入另一XML且不使签名失效?

如何确保签名XML插入批量文件后仍有效

我使用Python的signxml模块(enveloped方法)对单个XML事件文件签名,将其插入批量XML文件后签名失效,验证时出现“invalid asymmetric cipher”错误。需要保持当前的输出格式,但让签名在插入后依然有效。

问题原因

enveloped类型的XML签名是基于当前文档的结构和节点上下文计算摘要的,当把已签名的事件XML插入到批量文档中时:

  • 被签名节点的父层级、文档结构发生了变化,验证时无法按照原始签名的上下文正确提取目标节点
  • 不同命名空间下的ID引用可能出现解析问题,导致签名验证时找不到对应的被签名节点

解决方案

1. 调整操作顺序:先插入事件,再签名目标节点

不要先单独签名事件XML,而是先将未签名的事件插入到批量文档的对应位置,再针对最终文档中的目标节点执行签名操作。这样签名是基于最终的文档结构计算的,后续不会因为结构变化导致签名失效。

2. 确保命名空间和ID的正确性

  • 批量文档和事件文档使用了不同的命名空间,查询节点时必须指定完整的命名空间,避免找不到目标节点
  • 确保id属性值在整个最终XML文档中唯一,防止URI引用冲突

修改后的代码实现

from lxml import etree
from signxml import XMLSigner, methods
from cryptography.hazmat.primitives.serialization import pkcs12

# 提取私钥和证书
private_key, cert, _ = pkcs12.load_key_and_certificates(pfx_data, cert_password)

# 加载未签名的事件XML
event_xml_str = '''
<Reinf xmlns="http://www.reinf.esocial.gov.br/schemas/evt4020PagtoBeneficiarioPJ/v2_01_02">
  <EvtPJ id="ID123">
    <cnpj>123456789</cnpj>
  </EvtPJ>
</Reinf>
'''
event_root = etree.fromstring(event_xml_str)

# 加载批量XML
main_xml_str = '''
<Reinf xmlns="http://www.reinf.esocial.gov.br/schemas/envioLoteEventosAssincrono/v1_00_00">
  <lote>
    <eventos>
      <evento id="ID123">
      </evento>
    </eventos>
  </lote>
</Reinf>
'''
main_root = etree.fromstring(main_xml_str)

# 找到批量文档中的<evento>节点(指定命名空间)
evento_ns = "http://www.reinf.esocial.gov.br/schemas/envioLoteEventosAssincrono/v1_00_00"
evento_element = main_root.find(f".//{{{evento_ns}}}evento[@id='ID123']")

if evento_element is not None:
    # 将未签名的事件插入到<evento>节点中
    evento_element.append(event_root)
    
    # 定位最终文档中的<EvtPJ>节点(指定事件的命名空间)
    evt_ns = "http://www.reinf.esocial.gov.br/schemas/evt4020PagtoBeneficiarioPJ/v2_01_02"
    evt_pj_element = main_root.find(f".//{{{evt_ns}}}EvtPJ[@id='ID123']")
    
    # 配置XMLSigner,针对目标节点签名
    signer = XMLSigner(method=methods.enveloped, signature_algorithm="rsa-sha256", digest_algorithm="sha256")
    # 签名时引用目标节点的ID,签名会自动添加到目标节点的父节点中
    signed_main_root = signer.sign(evt_pj_element, key=private_key, cert=[cert], reference_uri="#ID123")
    
else:
    print("未找到匹配ID的<evento>节点")

# 生成最终XML并保存
final_xml = etree.tostring(main_root, pretty_print=False, xml_declaration=False, encoding="utf-8")
with open("xml_final.xml", "wb") as file:
    file.write(final_xml)

效果说明

修改后的代码会先将未签名事件插入批量文档,再基于最终文档中的<EvtPJ>节点生成签名,此时签名的上下文是最终的批量文档结构,插入操作不会破坏签名的有效性,验证时可以正确定位被签名节点并通过校验。

内容的提问来源于stack exchange,提问作者Victor Akahane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:01:17