How to use client certificate authentication with Suds
This is related to my last post. I was finally able to communicate to an SSL-enabled SOAP service with my computer behind a proxy but then ran into another problem: certificate-based authentication.
Suds actually doesn’t support certificate authentication directly, but fortunately someone created a custom transport for it here.
I tried his code but then ran into connection issues again, I had to modify it a little bit to include the proxy settings in the transport. Here’s the slightly modified code, I basically just added a proxy handler to his transport:
import urllib2 as u2 from suds.client import Client from suds.transport.http import HttpTransport, Reply, TransportError import httplib class HTTPSClientAuthHandler(u2.HTTPSHandler): def __init__(self, key, cert): u2.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, proxy_settings=None, *args, **kwargs): HttpTransport.__init__(self, *args, **kwargs) self.key = key self.cert = cert self.proxy_settings = proxy_settings 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 https_client_auth_handler = HTTPSClientAuthHandler(self.key, self.cert) # Add a proxy handler if the proxy settings is specified. # Otherwise, just use the HTTPSClientAuthHandler. if self.proxy_settings: proxy_handler = u2.ProxyHandler(self.proxy_settings) url = u2.build_opener(proxy_handler, https_client_auth_handler) else: url = u2.build_opener(https_client_auth_handler) url = u2.build_opener() if self.u2ver() < 2.6: socket.setdefaulttimeout(tm) return url.open(u2request) else: return url.open(u2request, timeout=tm) # Test # if __name__ == '__main__': key= r'D:\key_nopass.pem' cert = r'D:\cert.pem' proxy_settings = {'https': 'http://user:password@host:port'} transport = HTTPSClientCertTransport(key, cert, proxy_settings) service_url = 'https://services.domain.com/test/hello.wsdl' client = Client(service_url, transport=transport) print client
His code also only supports certificates/keys in PEM format so you may need to convert your client certificate. In our case, we were issued a PKCS#12 certificate and we had to extract the key and certificate to PEM format using OpenSSL:
Extract the key:
openssl.exe pkcs12 -nocerts -in ClientCert.p12 -out key.pem
Extract the certificate:
openssl.exe pkcs12 -clcerts -nokeys -in ClientCert.p12 -out cert.pem<br>
You may also want to remove the passphrase from the key:
openssl.exe rsa -in key.pem -out key_nopass.pem
Tags: howto, python, security, tech, software development, networking