Suds over https with cert

前端 未结 5 1691
醉话见心
醉话见心 2020-12-07 15:47

I have soap service under Apache with ssl, suds works greate without ssl.
I have client certificate (my.crt and user.p12 files).
How I need to configure suds clien

相关标签:
5条回答
  • 2020-12-07 16:21

    SSL security feature is auto enabled python 2.7.9+ which breaks suds and other python libraries. I am sharing a patch which can fix this:

    Locate you suds library and replace u2handlers function in suds/trasnport/http.py file with following line:

    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 
    

    Note: It's not a recommended way of doing it.

    0 讨论(0)
  • 2020-12-07 16:27

    Extending @k4ml solution, using cert + key This will solve exceptions like:

    requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2599)
    

    Solution:

    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)
    
    0 讨论(0)
  • 2020-12-07 16:29

    It sounds like you want to authenticate using a client certificate, not a server certificate as was stated in some of the comments. I had the same issue and was able to write a custom transport for SUDS. Here's the code that works for me.

    You'll need your certificates in PEM format for this to work; OpenSSL can easily perform this conversion, though I don't remember the exact syntax.

    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
    
    0 讨论(0)
  • 2020-12-07 16:36

    Another workaround is to use requests library as transport which has better support for ssl. This is what I'm using now to access SOAP services through https using suds:-

    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
    

    And then you can instantiate suds client as:-

    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))
    

    Update

    We're now using Zeep, which use requests underneath.

    0 讨论(0)
  • 2020-12-07 16:43

    Based on @k4ml answer, I've only added the open() which allows to fetch the WSDL using the certificate.

    This method should fix the suds.transport.TransportError: HTTP Error 403: Forbidden when trying to fetch a WSDL (at Client creation) served behind a HTTPS service.

    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
    

    Side note, I've also made a suggested edit to k4ml's answer, but it can take ages before it gets approved.

    0 讨论(0)
提交回复
热议问题