signxml签名无效但xmlsec有效,求差异原因及适配方案
解决signxml与xmlsec签名不一致的问题
关键差异分析
你遇到的验证失败,核心源于两个签名结构的差异:
- 缺失Issuer详情标签:xmlsec生成的签名在
KeyInfo节点中包含了证书的Issuer信息,而signxml默认不会输出该字段 - Reference节点数量不同:xmlsec生成的签名包含两个
Reference(分别指向SOAP Body和WSSE Security头),你的signxml代码仅生成一个指向主体的Reference
调整signxml配置的具体方案
1. 生成多Reference的WSSE风格签名
你的xmlsec代码采用的是WSSE(Web Services Security)规范的签名逻辑,需要同时对SOAP Body和Security头进行签名。调整signxml代码如下:
from signxml import XMLSigner, methods from lxml import etree # 假设xml_element是你的SOAP Envelope节点 soap_env = xml_element security_ns = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" wsse_prefix = "wsse" # 创建WSSE Security头并插入到SOAP Header中 security_header = etree.SubElement( soap_env.find("{http://schemas.xmlsoap.org/soap/envelope/}Header"), f"{{{security_ns}}}Security", nsmap={wsse_prefix: security_ns} ) # 初始化签名器,对齐算法配置 signer = XMLSigner( method=methods.enveloped, signature_algorithm="rsa-sha1", digest_algorithm='sha1', c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315' ) signer.namespaces = {None: signer.namespaces['ds']} # 给需要签名的节点添加唯一ID body_id = "Body-SigId" security_id = "Security-SigId" soap_body = soap_env.find("{http://schemas.xmlsoap.org/soap/envelope/}Body") soap_body.set(f"{{{security_ns}}}Id", body_id) security_header.set(f"{{{security_ns}}}Id", security_id) # 添加两个Reference,对应xmlsec的签名逻辑 signer.references = [ {"uri": f"#{body_id}", "digest_algorithm": "sha1"}, {"uri": f"#{security_id}", "digest_algorithm": "sha1"} ] # 将签名节点插入到Security头内 signed_root = signer.sign( soap_env, key=self.chave, cert=self.certificado, reference_uri=None, insert_reference=security_header )
2. 添加Issuer详情标签
需要让signxml在KeyInfo中输出证书的Issuer和Serial信息,修改签名时的参数配置:
from cryptography.x509 import load_pem_x509_certificate from cryptography.hazmat.backends import default_backend # 从证书中提取Issuer和Serial信息 cert_obj = load_pem_x509_certificate(self.certificado, default_backend()) issuer_name = cert_obj.issuer.rfc4514_string() serial_number = cert_obj.serial_number # 签名时指定keyinfo_options,添加X509IssuerSerial signed_root = signer.sign( soap_env, key=self.chave, cert=self.certificado, reference_uri=None, insert_reference=security_header, keyinfo_options={ "X509IssuerSerial": {"issuer_name": issuer_name, "serial_number": serial_number}, "X509Certificate": True # 保留证书内容,和xmlsec对齐 } )
3. 额外注意事项
- 命名空间完全对齐:确保WSSE、ds等命名空间的前缀和URI与xmlsec生成的完全一致,避免因命名空间差异导致验证失败
- 确认C14N算法:确保xmlsec使用的规范化算法和signxml配置的
http://www.w3.org/TR/2001/REC-xml-c14n-20010315完全相同 - 签名节点位置:xmlsec默认将签名放在WSSE Security头内,signxml通过
insert_reference参数指定插入位置,确保结构一致
内容的提问来源于stack exchange,提问作者Krish V
相关产品推荐
相关产品推荐

