2
votes

Question

How can I verify that an X.509 certificate is signed by another certificate using PyOpenSSL or Twisted? I want a client to verify that the received server certificate is the one that signed its client certificate.

I've looked through the PyOpenSSL documentation and can't seem to find anything on how to verify a certificate separately from the establishing the SSL connection.

I found a reference to OpenSSL.crypto:X509.verify() in twisted.internet._sslverify:PublicKey.verifyCertificate() , but the twisted method is commented out (in Twisted 13.0) and the X509 method does not exist (in PyOpenSSL 0.13).

pyOpenSSL has no support for verifying a certificate describes a bug for not being able to manually verify a certificate chain, but I'm not entirely sure if that's what I'm trying to do.

Use Case

Certificates:

  • Generated self-signed CA certificate with openssl.

  • Generated server certificate signed by CA certificate.

  • Generated client certificate signed by server certificate.

Setup:

  • The server is using Twisted's CertificateOptions with its server cert. The CA certs are the CA and server certs to setup a chain where the server cert verifies the received client cert, and the CA cert verifies the server cert (all built-in functionality).

  • The client is also using CertificateOptions for the client cert. The CA certs only contains the CA cert.

This all works fine (both sides verify each other) but I want to perform an additional step:

  • In the client set_verify() callback, verify that the client cert is signed by the server cert.
1
Are you sure you don't just want to set your trusted CA roots properly?Jean-Paul Calderone
My intention was to have specific users (with client certs) connect to a server (has server cert). But the clients would verify they connected to the real server (client verifies own cert is signed by server cert) instead of only trusting the server is signed by the root CA. Is that redundant to have the clients verify the identity of the server that way? Or is it not really possible? Or is there a simpler way?All Workers Are Essential
It's not redundant but it is backwards from the usual approach. There are APIs in pyOpenSSL that will let you set up your client so that it only allows connections to a server using a certificate signed by a (or one of several) certain certificate. There are not APIs in pyOpenSSL for restricting successful connections to those where the server certificate is the one that signed the client certificate. It would not be impossible to arrange this but it would involve first extending pyOpenSSL (or coming up with some creative alternate way to check certificate signatures).Jean-Paul Calderone
Another way to ask my original question is: why don't you verify the server certificate by declaring in your client that the certificate you expect to have signed the server certificate is the only trusted CA root? If you are worried about the "official" CA roots being compromised (or just untrustworthy) then you can still take this approach: just make sure the server certificate is signed by a certificate (and thus private key) you control.Jean-Paul Calderone
I think I'll just verify the server certificate the standard way by ensuring I only trust (a single but self-signed) root CA. Thanks for your help.All Workers Are Essential

1 Answers

0
votes

You should be able to do it with something like written here: http://www.yothenberg.com/validate-x509-certificate-in-python/ which is basically:

  1. load your certificates in PyOpenSSL with load_certificate()
  2. create a X509Store() object
  3. use add_cert() to add your intermediate certificate in the store
  4. create a X509StoreContext() object, initializing it with both your store object and your end certificate
  5. call verify_certificate() on your store context object

In practice, I was unable to make that part, and I think it is for the reasons explained here: https://mail.python.org/pipermail/cryptography-dev/2016-August/000676.html

In short, even in 2016, there still does not seem to be a correct wait to check certificates in PyOpenSSL, which is very sad. Note that the consensus seem to be that if you operate inside a TLS connection, the things are better checked by the connection routine instead of offline through check_certificate()