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

如何通过Windows证书库证书认证Zeep SOAP客户端?

Zeep调用SOAP接口时SignatureVerificationFailed错误排查与解决

问题详情

将.pfx证书转换为Zeep要求的.pem格式后,通过BinarySignature传递证书和密钥给Client对象,调用接口时触发错误:

zeep.exceptions.SignatureVerificationFailed

跟踪到Zeep的signature.py中_verify_envelope_with_key函数,发现是代码找不到SOAP Header导致抛出异常:

header = envelope.find(QName(soap_env, "Header"))
if header is None:
    raise SignatureVerificationFailed()

已知目标WSDL端点包含<sp:HttpsToken RequireClientCertificate="false"/>,尝试关闭验证但无效,怀疑是否需要直接从Windows证书库加载.pfx,避免转换时丢失CA相关数据。

当前使用的核心代码:

def setup_client():
    session = Session()
    session.verify = False
    session.cert = (CERT_PATH, KEY_PATH)
    transport = Transport(session=session)

    settings = Settings(strict=False, xml_huge_tree=True)

    wsse = BinarySignature(certfile=CERT_PATH, key_file=KEY_PATH)

    client = Client(WSDL_URL, transport=transport, settings=settings, wsse=wsse)

    return client

def main():
    load_dotenv()
    client = setup_client()
    response = client.service.Logout(sessionId=0)

补充:能正常拉取WSDL信息,但无法发送业务请求。

解决方案

1. 检查.pfx转.pem的完整性

转换时必须确保导出完整的证书链(包括根CA和中间证书),否则签名验证会缺少信任依据。使用openssl转换的正确命令:

# 导出带完整链的证书
openssl pkcs12 -in your_cert.pfx -out cert.pem -nokeys
# 导出未加密的私钥
openssl pkcs12 -in your_cert.pfx -out key.pem -nocerts -nodes

打开生成的cert.pem,确认里面有多段-----BEGIN CERTIFICATE-----/-----END CERTIFICATE-----块(对应完整证书链)。

2. 移除WSSE签名配置(如果服务端不需要)

WSDL中的<sp:HttpsToken RequireClientCertificate="false"/>说明服务端可能仅要求HTTPS层面的客户端证书验证,不需要WSSE签名。此时添加BinarySignature反而会让Zeep强制验证响应签名,但服务端可能未返回带签名的SOAP Header,导致报错。修改后的代码:

def setup_client():
    session = Session()
    session.verify = False  # 生产环境建议指定可信CA证书,不要直接关闭
    session.cert = (CERT_PATH, KEY_PATH)
    transport = Transport(session=session)

    settings = Settings(strict=False, xml_huge_tree=True)

    # 去掉wsse参数,仅保留HTTPS客户端证书配置
    client = Client(WSDL_URL, transport=transport, settings=settings)

    return client

3. 直接从Windows证书库加载.pfx

如果转换.pem确实丢失了数据,可以用pywin32和cryptography直接读取Windows证书存储中的证书,避免转换环节:

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import win32cryptography

def load_cert_from_windows(thumbprint):
    # 从当前用户个人证书存储加载
    store = win32cryptography.CertOpenSystemStore(None, "MY")
    cert_ctx = win32cryptography.CertFindCertificateInStore(
        store,
        win32cryptography.X509_ASN_ENCODING | win32cryptography.PKCS_7_ASN_ENCODING,
        0,
        win32cryptography.CERT_FIND_SHA1_HASH,
        bytes.fromhex(thumbprint.replace(":", "")),
        None
    )
    # 导出带私钥的PFX数据
    pfx_data = win32cryptography.PFXExportCertStoreEx(
        store,
        "",
        "",
        None,
        win32cryptography.EXPORT_PRIVATE_KEYS | win32cryptography.PFX_EXPORT_INCLUDE_ROOT
    )
    # 解析PFX
    pfx = serialization.pkcs12.load_pkcs12(
        pfx_data,
        password=None,  # 证书有密码则传入对应字符串
        backend=default_backend()
    )
    # 转为PEM格式字符串
    cert_pem = pfx.cert.public_bytes(encoding=serialization.Encoding.PEM)
    key_pem = pfx.private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
    return cert_pem, key_pem

# 在setup_client中使用
cert_pem, key_pem = load_cert_from_windows("你的证书SHA1指纹")
wsse = BinarySignature(certfile=cert_pem, key_file=key_pem)

4. 排查服务端响应结构

如果确实需要WSSE签名,开启Zeep调试日志,查看服务端返回的XML是否包含<soap:Header>:

import logging
logging.basicConfig(level=logging.DEBUG)

如果响应中没有Header,要么是服务端不支持WSSE签名,要么需要联系服务端调整返回格式。也可以自定义签名验证类跳过Header检查,但这属于临时 hack,不推荐生产环境使用。


内容的提问来源于stack exchange,提问作者Russell Rozenbaum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 01:27:21