Python 带证书的https上的肥皂水

Python 带证书的https上的肥皂水,python,ssl,https,suds,Python,Ssl,Https,Suds,我在Apache下有带ssl的soap服务,没有ssl的情况下,suds工作得很好。 我有客户端证书(my.crt和user.p12文件)。 我需要如何配置suds客户端,使其能够通过https使用服务 没有证书,我明白了 urllib2.URLError: <urlopen error [Errno 1] _ssl.c:499: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure> ur

我在Apache下有带ssl的soap服务,没有ssl的情况下,suds工作得很好。
我有客户端证书(my.crt和user.p12文件)。
我需要如何配置suds客户端,使其能够通过https使用服务

没有证书,我明白了

urllib2.URLError: <urlopen error [Errno 1] _ssl.c:499: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure>
urllib2.URLError:听起来您想使用客户机证书进行身份验证,而不是一些注释中所述的服务器证书。我也有同样的问题,并且能够为肥皂水编写一个自定义传输。这是适合我的代码

您需要PEM格式的证书才能工作;OpenSSL可以轻松地执行此转换,尽管我不记得确切的语法

import urllib2, httplib, socket
from suds.client import Client
from suds.transport.http import HttpTransport, Reply, TransportError

class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
    def __init__(self, key, cert):
        urllib2.HTTPSHandler.__init__(self)
        self.key = key
        self.cert = cert

    def https_open(self, req):
        #Rather than pass in a reference to a connection class, we pass in
        # a reference to a function which, for all intents and purposes,
        # will behave as a constructor
        return self.do_open(self.getConnection, req)

    def getConnection(self, host, timeout=300):
        return httplib.HTTPSConnection(host,
                                       key_file=self.key,
                                       cert_file=self.cert)

class HTTPSClientCertTransport(HttpTransport):
    def __init__(self, key, cert, *args, **kwargs):
        HttpTransport.__init__(self, *args, **kwargs)
        self.key = key
        self.cert = cert

    def u2open(self, u2request):
        """
        Open a connection.
        @param u2request: A urllib2 request.
        @type u2request: urllib2.Requet.
        @return: The opened file-like urllib2 object.
        @rtype: fp
        """
        tm = self.options.timeout
        url = urllib2.build_opener(HTTPSClientAuthHandler(self.key, self.cert))
        if self.u2ver() < 2.6:
            socket.setdefaulttimeout(tm)
            return url.open(u2request)
        else:
            return url.open(u2request, timeout=tm)

# These lines enable debug logging; remove them once everything works.
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)

c = Client('https://YOUR_URL_HERE',
    transport = HTTPSClientCertTransport('PRIVATE_KEY.pem',
                                         'CERTIFICATE_CHAIN.pem'))
print c
导入urllib2、httplib、套接字 从suds.client导入客户端 从suds.transport.http导入HttpTransport、Reply、TransportError 类HTTPSClientAuthHandler(urllib2.HTTPSHandler): 定义初始化(自我、密钥、证书): urllib2.HTTPSHandler.\uuuu init\uuuuu(self) self.key=key self.cert=证书 def https_打开(自身,请求): #我们传入的不是对连接类的引用,而是 #对函数的引用,出于所有目的, #将表现为构造函数 返回self.do_open(self.getConnection,req) def getConnection(自身、主机、超时=300): 返回httplib.HTTPSConnection(主机, key\u file=self.key, 证书(文件=self.cert) 类HTTPSClientCertTransport(HttpTransport): 定义初始化(self、key、cert、*args、**kwargs): HttpTransport.\uuuuu初始化(self,*args,**kwargs) self.key=key self.cert=证书 def u2open(自我,u2request): """ 打开一个连接。 @param u2request:一个urllib2请求。 @输入请求:urllib2.Requet。 @return:打开的文件,如urllib2对象。 @rtype:fp """ tm=self.options.timeout url=urllib2.build_opener(HTTPSClientAuthHandler(self.key,self.cert)) 如果self.u2ver()<2.6: socket.setdefaulttimeout(tm) 返回url.open(u2request) 其他: 返回url.open(u2request,timeout=tm) #这些行支持调试日志记录;一旦一切正常,移除它们。 导入日志记录 logging.basicConfig(级别=logging.INFO) logging.getLogger('suds.client').setLevel(logging.DEBUG) logging.getLogger('suds.transport').setLevel(logging.DEBUG) c=客户('https://YOUR_URL_HERE', transport=HTTPSClientCertTransport('PRIVATE_KEY.pem', "证书(u CHAIN.pem))) 打印c
另一个解决方法是使用请求库作为传输,它更好地支持ssl。这就是我现在使用的通过https使用SUD访问SOAP服务:-

import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError

class RequestsTransport(HttpAuthenticated):
    def __init__(self, **kwargs):
        self.cert = kwargs.pop('cert', None)
        # super won't work because not using new style class
        HttpAuthenticated.__init__(self, **kwargs)

    def send(self, request):
        self.addcredentials(request)
        resp = requests.post(request.url, data=request.message,
                             headers=request.headers, cert=self.cert)
        result = Reply(resp.status_code, resp.headers, resp.content)
        return result
然后您可以将suds客户端实例化为:-

headers = {"Content-TYpe" : "text/xml;charset=UTF-8",
           "SOAPAction" : ""}
t = RequestsTransport(cert='/path/to/cert', **credentials)
client = Client(wsdl_uri, location=send_url, headers=headers,
                transport=t))
更新


我们现在正在使用,它在下面使用
请求。

基于@k4ml answer,我只添加了
open()
,它允许使用证书获取WSDL

当尝试获取HTTPS服务背后提供的WSDL(在客户端创建时)时,此方法应修复
suds.transport.TransportError:HTTP错误403:Forbidded

import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError

class RequestsTransport(HttpAuthenticated):
    def __init__(self, **kwargs):
        self.cert = kwargs.pop('cert', None)
        # super won't work because not using new style class
        HttpAuthenticated.__init__(self, **kwargs)

    def open(self, request):
        """
        Fetches the WSDL using cert.
        """
        self.addcredentials(request)
        resp = requests.get(request.url, data=request.message,
                             headers=request.headers, cert=self.cert)
        result = io.StringIO(resp.content.decode('utf-8'))
        return result

    def send(self, request):
        """
        Posts to service using cert.
        """
        self.addcredentials(request)
        resp = requests.post(request.url, data=request.message,
                             headers=request.headers, cert=self.cert)
        result = Reply(resp.status_code, resp.headers, resp.content)
        return result

旁注,我还建议对k4ml的答案进行编辑,但可能需要很长时间才能获得批准。

使用cert+密钥扩展@k4ml解决方案 这将解决以下例外情况:

requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2599)
解决方案:

import requests

from suds.client import Client
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError


class RequestsTransport(HttpAuthenticated):

    def __init__(self, **kwargs):
        self.cert = kwargs.pop('cert', None)
        HttpAuthenticated.__init__(self, **kwargs)

    def send(self, request):
        self.addcredentials(request)
        resp = requests.post(
            request.url,
            data=request.message,
            headers=request.headers,
            cert=self.cert,
            verify=True
        )
        result = Reply(resp.status_code, resp.headers, resp.content)
        return result



t = RequestsTransport(cert=('<your cert.pem path>', 'your key.pem path'))
headers = {"Content-Type": "text/xml;charset=UTF-8", "SOAPAction": ""}
client = Client(wsdl_url, headers=headers, transport=t)
导入请求
从suds.client导入客户端
从suds.transport.http导入HttpAuthenticated
从suds.transport导入回复,TransportError
class RequestsTransport(经HttpAuthenticated认证):
定义初始(自我,**kwargs):
self.cert=kwargs.pop('cert',无)
HttpAuthenticated.\uuuuu init\uuuuuuuuu(self,**kwargs)
def发送(自我,请求):
self.addcredentials(请求)
resp=requests.post(
request.url,
data=request.message,
headers=request.headers,
cert=self.cert,
验证=真
)
结果=回复(响应状态\代码、响应标题、响应内容)
返回结果
t=RequestsTransport(证书=(''‘您的key.pem路径'))
headers={“内容类型”:“text/xml;charset=UTF-8”,“SOAPAction”:“”}
client=client(wsdl\u url,headers=headers,transport=t)

SSL安全功能是自动启用的python 2.7.9+,它破坏了SUD和其他python库。我正在共享一个修补程序,可以修复此问题:

找到suds库并将suds/trasnport/http.py文件中的u2handlers函数替换为以下行:

import ssl
def u2handlers(self):
        """
        Get a collection of urllib handlers.

        @return: A list of handlers to be installed in the opener.
        @rtype: [Handler,...]

        """
        handlers = []
        unverified_context = ssl.create_default_context()
        unverified_context.check_hostname = False
        unverified_context.verify_mode = ssl.CERT_NONE
        unverified_handler = urllib2.HTTPSHandler(context=unverified_context)
        handlers.append(unverified_handler)
        handlers.append(urllib2.ProxyHandler(self.proxy))
        #handlers.append(urllib2.ProxyHandler(self.proxy))
        return handlers 

注意:这不是推荐的方法。

它看起来依赖于,但不支持此类选项。请注意,
urllib2
甚至不验证服务器证书(请参阅文档),如果您真的想使用HTTPS.yep,那么确实需要验证服务器证书,但我可以基于其他python库创建自己的传输,它将使用客户机证书。您推荐哪个库而不是urllib2?这里有一个讨论:非常好的答案。这将是一个使用客户端证书的好例子。需要放在肥皂水里。:-)“听起来您想使用客户端证书进行身份验证,而不是某些注释中所述的服务器证书。”。此代码不会对服务器进行身份验证:本质上,您正在向某个对象发送客户端证书,但尚未验证该对象是什么。无论您是否使用客户端证书,这些都不能对服务器进行身份验证,这应该首先完成。参数“YOUR_KEY_”和“CERT.pem”应该是两个文件名,而不是两个。第一个指向私钥文件,第二个指向证书链文件。两者都是不安全的.pem格式。@willsteel实际上,您可以在同一个pem文件中同时拥有私钥和证书(公钥)——您只需将它们一个接一个地追加。我只有一个