python-3.x 在Soap客户端方法调用期间权限被拒绝13,

w6lpcovy  于 4个月前  发布在  Python
关注(0)|答案(1)|浏览(82)

我正在尝试调用在以下.PEM验证的zeep客户端会话中启动的服务:

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

session = Session()
session.verify = True
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(
    open(pfx_cert_path, "rb").read(), cert_psw_b)
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-Body和一个SOAP-Header,它们都是动态值。我的做法类似于:soap_client.service.UploadMethod({"body_field":"body_value","_soapheaders":{"head_field":"head_value"}})这会产生以下错误队列,当urllib 3以读取模式访问临时文件时,这些错误以Permission Denied结束。

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:


我尝试过不同的位置。我也尝试过使用Suds客户端或直接依赖请求来进行soap调用,但我无法加载.pem证书和密钥。目前我设法在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("", "");  // Which loads the soap headers
    string xml_body = "C:/PATH/Body.txt";
    client.UploadMethod(File.ReadAllText(xml_body));


有没有人知道如何解决这个问题?也许是通过改变urllib版本,或者使用一种替代方法,直接将证书作为字节串而不是路径文件传递?
先谢了。

a5g8bdjr

a5g8bdjr1#

在Windows上尝试访问tempfile.NamedTemporaryFile在Python中创建的临时文件时,似乎遇到了权限问题。这是用户在Windows上使用此函数时遇到的常见问题,因为操作系统不允许多个进程打开同一文件。urllib 3库以读取模式打开文件,这将导致PermissionError,因为临时文件在Python代码中仍然打开。
要解决此问题,您可以避免创建临时文件并直接从内存加载证书和密钥。这可以通过使用load_cert_chain()方法来完成,该方法接受证书和密钥的二进制数据,而不是文件路径。
您可以使用private_bytespublic_bytes方法将私钥和证书转换为字符串,将其解码为UTF-8,然后使用ssl.SSLContext()将字符串提供给ssl上下文。下面,请找到一个不使用tempfile的示例实现,使用Zeep的低级内置支持来提供自定义传输:

import ssl
from zeep import Client, Settings, Plugin
from zeep.transports import Transport

from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives import serialization

private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(
    open(pfx_cert_path, "rb").read(), cert_psw_b)

context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

context.load_cert_chain(
    certfile=certificate.public_bytes(serialization.Encoding.PEM).decode('utf-8'), 
    keyfile=private_key.private_bytes(
        encoding=serialization.Encoding.PEM, 
        format=serialization.PrivateFormat.PKCS8, 
        encryption_algorithm=serialization.NoEncryption()
    ).decode('utf-8'))

settings = Settings(strict=True, xml_huge_tree=True)
client = Client(wsdl_url, 
                plugins=[MyLoggingPlugin()],
                transport=Transport(session=session, timeout=10, operation_timeout=10, ssl_context=context), 
                settings=settings)

response = client.service.MyMethod()

字符串
请注意,ssl_context参数在创建Transport对象时使用。这将允许您传递包含证书和密钥的SSL上下文,以与Web服务通信。
另外,请记住将wsdl_urlMyMethod()替换为您自己的Web服务详细信息。请注意,session也需要正确初始化和配置。
这种方法应该可以避免您面临的PermissionError问题,因为它绕过了创建临时文件的需要,并将证书和密钥直接加载到SSL上下文中。请记住,要确保这些字符串的安全,因为它们将包含非常敏感的密钥和证书数据。

相关问题