Validate SSL certificates with Python Validate SSL certificates with Python python python

Validate SSL certificates with Python


I have added a distribution to the Python Package Index which makes the match_hostname() function from the Python 3.2 ssl package available on previous versions of Python.

http://pypi.python.org/pypi/backports.ssl_match_hostname/

You can install it with:

pip install backports.ssl_match_hostname

Or you can make it a dependency listed in your project's setup.py. Either way, it can be used like this:

from backports.ssl_match_hostname import match_hostname, CertificateError...sslsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv3,                      cert_reqs=ssl.CERT_REQUIRED, ca_certs=...)try:    match_hostname(sslsock.getpeercert(), hostname)except CertificateError, ce:    ...


You can use Twisted to verify certificates. The main API is CertificateOptions, which can be provided as the contextFactory argument to various functions such as listenSSL and startTLS.

Unfortunately, neither Python nor Twisted comes with a the pile of CA certificates required to actually do HTTPS validation, nor the HTTPS validation logic. Due to a limitation in PyOpenSSL, you can't do it completely correctly just yet, but thanks to the fact that almost all certificates include a subject commonName, you can get close enough.

Here is a naive sample implementation of a verifying Twisted HTTPS client which ignores wildcards and subjectAltName extensions, and uses the certificate-authority certificates present in the 'ca-certificates' package in most Ubuntu distributions. Try it with your favorite valid and invalid certificate sites :).

import osimport globfrom OpenSSL.SSL import Context, TLSv1_METHOD, VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, OP_NO_SSLv2from OpenSSL.crypto import load_certificate, FILETYPE_PEMfrom twisted.python.urlpath import URLPathfrom twisted.internet.ssl import ContextFactoryfrom twisted.internet import reactorfrom twisted.web.client import getPagecertificateAuthorityMap = {}for certFileName in glob.glob("/etc/ssl/certs/*.pem"):    # There might be some dead symlinks in there, so let's make sure it's real.    if os.path.exists(certFileName):        data = open(certFileName).read()        x509 = load_certificate(FILETYPE_PEM, data)        digest = x509.digest('sha1')        # Now, de-duplicate in case the same cert has multiple names.        certificateAuthorityMap[digest] = x509class HTTPSVerifyingContextFactory(ContextFactory):    def __init__(self, hostname):        self.hostname = hostname    isClient = True    def getContext(self):        ctx = Context(TLSv1_METHOD)        store = ctx.get_cert_store()        for value in certificateAuthorityMap.values():            store.add_cert(value)        ctx.set_verify(VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, self.verifyHostname)        ctx.set_options(OP_NO_SSLv2)        return ctx    def verifyHostname(self, connection, x509, errno, depth, preverifyOK):        if preverifyOK:            if self.hostname != x509.get_subject().commonName:                return False        return preverifyOKdef secureGet(url):    return getPage(url, HTTPSVerifyingContextFactory(URLPath.fromString(url).netloc))def done(result):    print 'Done!', len(result)secureGet("https://google.com/").addCallback(done)reactor.run()


PycURL does this beautifully.

Below is a short example. It will throw a pycurl.error if something is fishy, where you get a tuple with error code and a human readable message.

import pycurlcurl = pycurl.Curl()curl.setopt(pycurl.CAINFO, "myFineCA.crt")curl.setopt(pycurl.SSL_VERIFYPEER, 1)curl.setopt(pycurl.SSL_VERIFYHOST, 2)curl.setopt(pycurl.URL, "https://internal.stuff/")curl.perform()

You will probably want to configure more options, like where to store the results, etc. But no need to clutter the example with non-essentials.

Example of what exceptions might be raised:

(60, 'Peer certificate cannot be authenticated with known CA certificates')(51, "common name 'CN=something.else.stuff,O=Example Corp,C=SE' does not match 'internal.stuff'")

Some links that I found useful are the libcurl-docs for setopt and getinfo.