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

Python Zeep调用SOAP服务时遇PermissionError 13权限拒绝问题求助

Zeep客户端调用SOAP服务时PEM证书权限拒绝问题

当前Python实现代码

from requests import Session
from zeep import Client
from zeep.settings import Settings
from zeep.transports import Transport
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives import serialization
import tempfile

session = Session()
session.verify = True
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(
    open(pfx_cert_path, "rb").read(), cert_psw_b)
# 修正原代码语法错误:补全括号并导入tempfile
key_tmpfile = tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/")
key_tmpfile.write(
    private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption(),
    )
)
key_tmpfile.flush()
cert_tmpfile=tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/")
cert_tmpfile.write(
    certificate.public_bytes(serialization.Encoding.PEM),
)
cert_tmpfile.flush()
session.cert = cert_tmpfile.name, key_tmpfile.name

settings = Settings(raw_response=True)
soap_client = Client(wsdl_url, transport=Transport(session=session), settings=settings)

SOAP调用方式

soap_client.service.UploadMethod({"body_field":"body_value","_soapheaders":{"head_field":"head_value"}})

错误栈信息

File "C:\Users\USERA\Anaconda3\envs\env\lib\site-packages\urllib3\connection.py", line 419, in connect
    self.sock = ssl_wrap_socket(
  File "C:\Users\USERA\Anaconda3\envs\env\lib\site-packages\urllib3\util\ssl_.py", line 413, in ssl_wrap_socket
if keyfile and key_password is None and _is_key_file_encrypted(keyfile):
  File "C:\Users\USERA\Anaconda3\envs\env\lib\site-packages\urllib3\util\ssl_.py", line 472, in _is_key_file_encrypted
with open(key_file, "r") as f:
PermissionError: [Errno 13] Permission denied:

已成功实现的C#代码

CLIENT client = new CLIENT(); // Project -> Add Service Reference -> Advanced -> Add Web Reference -> https://service_domain/service.asmx -> Rename "CLIENT"

string cert_path = "C:/PATH/Certificates/PKCS12_Credential.pfx";
string cert_psw_path = "C:/PATH/Certificates/psw_pfx.txt";
X509Certificate2 certificate = new X509Certificate2(cert_path, File.OpenText(cert_psw_path).ReadLine());
client.ClientCertificates.Add(certificate);
client.Login("", "");  // 加载SOAP头
string xml_body = "C:/PATH/Body.txt";
client.UploadMethod(File.ReadAllText(xml_body));

解决方法

方案1:直接传递证书字节(无需临时文件)

绕过requests.cert仅支持文件路径的限制,通过自定义SSL上下文直接加载内存中的证书和密钥:

from requests import Session, adapters
from zeep import Client
from zeep.settings import Settings
from zeep.transports import Transport
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives import serialization
import ssl

session = Session()
session.verify = True

# 加载PFX证书
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(
    open(pfx_cert_path, "rb").read(), cert_psw_b)

# 创建自定义SSL上下文
ssl_context = ssl.create_default_context()
# 直接传入证书和私钥的字节数据
ssl_context.load_cert_chain(
    certfile=None,
    keyfile=None,
    cert_data=certificate.public_bytes(serialization.Encoding.PEM),
    key_data=private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
)
# 添加额外证书(如果有)
for cert in additional_certificates:
    ssl_context.load_verify_locations(cadata=cert.public_bytes(serialization.Encoding.PEM))

# 给session绑定自定义SSL上下文
session.mount("https://", adapters.HTTPAdapter(ssl_context=ssl_context))

settings = Settings(raw_response=True)
soap_client = Client(wsdl_url, transport=Transport(session=session), settings=settings)

方案2:调整临时文件权限与生命周期

如果必须使用临时文件,需确保文件权限足够且不被提前回收:

import tempfile
import os
import stat

# 创建临时文件时设置delete=False,避免自动删除
key_tmpfile = tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/", delete=False)
# 设置文件全局可读权限(Windows下适配)
os.chmod(key_tmpfile.name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
key_tmpfile.write(
    private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption(),
    )
)
key_tmpfile.close()

cert_tmpfile = tempfile.NamedTemporaryFile(dir="C:/Users/USERA/Anaconda3/envs/env/", delete=False)
os.chmod(cert_tmpfile.name, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
cert_tmpfile.write(certificate.public_bytes(serialization.Encoding.PEM))
cert_tmpfile.close()

session.cert = cert_tmpfile.name, key_tmpfile.name

# 调用服务后手动清理临时文件
try:
    response = soap_client.service.UploadMethod({"body_field":"body_value","_soapheaders":{"head_field":"head_value"}})
finally:
    os.unlink(key_tmpfile.name)
    os.unlink(cert_tmpfile.name)

方案3:降级urllib3版本

部分新版本urllib3在Windows下处理临时文件权限存在兼容问题,可尝试降级到1.26.x稳定版本:

pip install urllib3==1.26.15

内容的提问来源于stack exchange,提问作者LNRD.CLL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 01:22:06