The python standard library modules like
http, and the popular third party module
requests all perform certificate validation by default when connecting over HTTPS. Unfortunately they don't expose an API that will let me verify a chain of trust by providing the certificate and the chain outside of a HTTPS connection.
I have a certificate in a file, cert.pem. I have an intermediate certificate in a file, int-cert.pem. I have a root certificate in a file, root-cert.pem. I want a way to validate that cert.pem was issued by int-cert.pem and int-cert.pem was issued by root-cert.pem. OpenSSL provides the
verify command to do this:
# concatenate the certs together into a single file representing the chain of trust $ cat int-cert.pem root-cert.pem > trust.pem $ openssl verify -CAfile trust.pem cert.pem
PyOpenSSL and M2Crypto are wrappers around OpenSSL but they only wrap a subset of the OpenSSL APIs. The cryptography module is a more ambitious project. It uses multiple backends (openssl, commoncrypto) to provide a suite of cryptographic primitives and programmer friendly APIs. The authors consider M2Crypto and PyOpenSSL to be flawed in a number of areas and are trying to create a better, more pythonic, cryptographic library. PyOpenSSL actually requires the cryptography module since version 0.14 as it relies on it for the OpenSSL bindings.
The M2Crypto module doesn't support validating a chain of trust. There were some attempts to patch it by the Fedora and Pulp projects in 2012 but the patch hasn't made it upstream and I couldn't locate the code. It is discussed here.
The PyOpenSSL module does support validating a chain of trust.
I installed PyOpenSSL with pip
$ pip install pyopenssl.
The code below illustrates how to read in the three certificates (in the pem file format) detailed in my scenario above and then validates cert.pem's chain of trust.
from OpenSSL import crypto def verify(): with open('./cert.pem', 'r') as cert_file: cert = cert_file.read() with open('./int-cert.pem', 'r') as int_cert_file: int_cert = int_cert_file.read() with open('./root-cert.pem', 'r') as root_cert_file: root_cert = root_cert_file .read() trusted_certs = (int_cert, root_cert) verified = verify_chain_of_trust(cert, trusted_certs) if verified: print('Certificate verified') def verify_chain_of_trust(cert_pem, trusted_cert_pems): certificate = crypto.load_certificate(crypto.FILETYPE_PEM, cert_pem) # Create and fill a X509Sore with trusted certs store = crypto.X509Store() for trusted_cert_pem in trusted_cert_pems: trusted_cert = crypto.load_certificate(crypto.FILETYPE_PEM, trusted_cert_pem) store.add_cert(trusted_cert) # Create a X590StoreContext with the cert and trusted certs # and verify the the chain of trust store_ctx = crypto.X509StoreContext(store, certificate) # Returns None if certificate can be validated result = store_ctx.verify_certificate() if result is None: return True else: return False
Cover photo of a Female Corynura Bee