Python Zeep 使用接口返回证书加密SOAP请求body解决方案求助
问题核心
你当前使用的Zeep库原生仅支持SOAP消息签名能力,不内置符合W3C XML Encryption规范的请求体加密功能,签名和加密是两类完全独立的WS-Security操作,所以你之前按照签名文档配置的逻辑无法满足接口要求。
可行实现方案
方案1:基于Zeep自定义插件实现加密(适配现有代码)
你可以通过Zeep的插件机制,拦截生成的原始请求XML,调用xmlsec库完成Body加密后再发送,无需重构现有业务代码。
依赖准备
- 系统依赖:先安装libxmlsec1相关组件,Debian/Ubuntu执行
apt install libxmlsec1-dev libxml2-dev libxslt1-dev,CentOS执行yum install xmlsec1-devel libxml2-devel libxslt-devel,Windows可直接安装预编译的xmlsec wheel包 - Python依赖:执行
pip install lxml xmlsec zeep requests
代码实现
首先定义加密插件:
from zeep import Plugin from lxml import etree import xmlsec class SOAPBodyEncryptPlugin(Plugin): def __init__(self, cert_pem): # 加载接口返回的公钥证书 self.cert = xmlsec.Key.from_memory(cert_pem, xmlsec.KeyFormat.PEM, None) def egress(self, envelope, http_headers, operation, binding_options): # 定位SOAP Body节点 soap_body = envelope.find("{http://schemas.xmlsoap.org/soap/envelope/}Body") if not soap_body: soap_body = envelope.find("{http://www.w3.org/2003/05/soap-envelope}Body") # 初始化加密上下文,指定算法为AES256-CBC enc_ctx = xmlsec.EncContext(xmlsec.Transform.AES256_CBC) enc_ctx.key = self.cert # 配置KeyInfo结构匹配接口要求 key_info = xmlsec.template.ensure_key_info(enc_ctx.enc_ctx) sec_token_ref = xmlsec.template.add_security_token_reference(key_info) xmlsec.template.add_reference(sec_token_ref, uri="#EK-{}".format(id(self))) # 加密Body内容 enc_data = xmlsec.template.encrypted_data_create( envelope, xmlsec.Transform.AES256_CBC, type=xmlsec.EncryptionType.CONTENT ) xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) soap_body.append(enc_data) enc_ctx.encrypt(enc_data, soap_body[0]) # 移除原始明文Body内容,只保留加密后的节点 for child in list(soap_body): if child.tag != "{http://www.w3.org/2001/04/xmlenc#}EncryptedData": soap_body.remove(child) return envelope, http_headers
在你原有代码中添加插件即可:
# 原有获取certPEM的逻辑不变 certPEM = response('body')('content') # 初始化加密插件,加入Zeep客户端 encrypt_plugin = SOAPBodyEncryptPlugin(certPEM) client.plugins.append(encrypt_plugin) # 后续请求会自动加密Body client.service.secondRequest(_soapheaders=headers, expectedData=expectedData)
方案2:切换到支持WS-Security加密的原生库
如果不想自行实现加密逻辑,可以切换到suds库搭配suds-xenc扩展,原生支持SOAP Body加密,示例代码如下:
from suds.client import Client from suds.wsse import Security from suds_xenc import Encryption # 初始化客户端 client = Client(url) # 配置加密规则 security = Security() encryption = Encryption( cert=certPEM, algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" ) security.tokens.append(encryption) client.set_options(wsse=security, verify=False) # 直接调用接口即可自动加密请求体 response = client.service.secondRequest(headers, expectedData)
注意事项
- 需确认接口返回的certPEM是标准PEM格式公钥证书,如果返回的是二进制DER格式,需要先转成PEM格式再传入
- 加密后的节点命名空间、ID规则、KeyInfo结构要和接口示例完全对齐,否则会触发证书校验失败
- 测试环境关闭SSL校验的配置不要带到生产环境使用
内容的提问来源于stack exchange,提问作者Petru Tanas
相关产品推荐
相关产品推荐

