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