Search code examples
pythonpyopenssl

How to verify certificate signature in pyOpenSSL?


I have two certificates, a root.crt that was used to sign client.crt.

I want to verify that the client.crt was indeed signed by root.key.

Using openssl on terminal, it works like this:

$ openssl verify -CAfile root.crt client.crt  
> client.crt: OK  

However using pyOpenSSL - following the documentation and this blog post - I tried something like this:

client_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, file('client.crt').read())

root_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, file('root.crt').read())  

store = OpenSSL.crypto.X509Store()  
store.add_cert(root_cert)  

ctx = OpenSSL.crypto.X509StoreContext(store, client_cert)
ctx.verify_certificate()  

I get this error:

    > X509StoreContextError: [2, 1, 'unable to get issuer certificate']

What am I missing?


Solution

  • The issue is that my root.crt is not really root, but a chain of certificates:

    -----BEGIN CERTIFICATE----- 
    ...
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE----- 
    ...
    -----END CERTIFICATE-----
    

    And OpenSSL.crypto.load_certificate just loads the first one.

    The solution is to extract all certificates in the chain file and add them to the X509Store.

    The code solution looks like this:

    _PEM_RE = re.compile(b'-----BEGIN CERTIFICATE-----\r?.+?\r?-----END CERTIFICATE-----\r?\n?', re.DOTALL)
    
    
    def parse_chain(chain):
        # returns a list of certificates
        return [c.group() for c in _PEM_RE.finditer(chain)]
    
    
    client_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, file('server.crt').read())
    
    store = OpenSSL.crypto.X509Store()
    for cert in parse_chain(file('root.crt').read()):
        store.add_cert(OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
    
    ctx = OpenSSL.crypto.X509StoreContext(store, client_cert)
    ctx.verify_certificate()
    

    Adapted from https://github.com/hynek/pem/blob/master/src/pem/_core.py#L115