Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating client certificates in PyOpenSSL

I'm writing an app that requires a cert to be installed in the client browser. I've found this in the PyOpenSSL docs for the "Context" object but I can't see anything about how the callback is supposed to validate the cert, only that it should, somehow.

   set_verify(mode, callback)
      Set the verification flags for this Context object to mode and
      specify that callback should be used for verification callbacks.
      mode should be one of VERIFY_NONE and VERIFY_PEER. If
      VERIFY_PEER is used, mode can be OR:ed with
      VERIFY_FAIL_IF_NO_PEER_CERT and VERIFY_CLIENT_ONCE to further
      control the behaviour. callback should take five arguments: A
      Connection object, an X509 object, and three integer variables,
      which are in turn potential error number, error depth and return
      code. callback should return true if verification passes and
      false otherwise.

I'm telling the Context object where my (self signed) keys are (see below) so I guess I don't understand why that's not enough for the library to check if the cert presented by the client is a valid one. What should one do in this callback function?

class SecureAJAXServer(PlainAJAXServer):
    def __init__(self, server_address, HandlerClass):
        BaseServer.__init__(self, server_address, HandlerClass)
        ctx = SSL.Context(SSL.SSLv23_METHOD)
        ctx.use_privatekey_file ('keys/server.key')
        ctx.use_certificate_file('keys/server.crt')
        ctx.set_session_id("My_experimental_AJAX_Server")
        ctx.set_verify( SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT | SSL.VERIFY_CLIENT_ONCE, callback_func )
        self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type))
        self.server_bind()
        self.server_activate()

Caveat: Coding for fun here, def not a pro so if my Q reveals my total lameness, naivety and/or fundamental lack of understanding when it comes to SSL please don't be too rough!

Thanks :)

Roger

like image 222
Roger Heathcote Avatar asked Feb 01 '12 02:02

Roger Heathcote


People also ask

How do you check the validity of a client certificate?

The certificate has to be validated against its signing authority This is accomplished by verifying the signature on the certificate with the signing authority's public key. In addition, certificate revocation lists (CRLs) are checked to ensure the cert hasn't been blacklisted.

How do I verify a certificate in Python?

Certification holders may now have others easily verify their certification status by using a unique certificate verification code. The code can be found in the top right-hand corner on all digital certificates issued by the Python Institute.

What is client certificate authentication?

What Does Client Authentication Certificate Mean? A client authentication certificate is a certificate used to authenticate clients during an SSL handshake. It authenticates users who access a server by exchanging the client authentication certificate.

What does the client certificate used for authentication could not be validated mean?

Either the client did not send the certificate for some reason or else the client did not have a certificate issued by a CA that was also trusted by IIS server. If the client sends a certificate which is not mutually trusted by both client and the server you may see this error.


1 Answers

In the OpenSSL documentation for set_verify(), the key that you care about is the return code:

callback should take five arguments: A Connection object, an X509 object, and three integer variables, which are in turn potential error number, error depth and return code. callback should return true if verification passes and false otherwise.

There is a a full working example that shows more or less what you want to do: When are client certificates verified?

Essentially you can ignore the first 4 arguments and just check the value of the return code in the fifth argument like so:

from OpenSSL.SSL import Context, Connection, SSLv23_METHOD
from OpenSSL.SSL import VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE

class SecureAJAXServer(BaseServer):
    def verify_callback(connection, x509, errnum, errdepth, ok):
        if not ok:
            print "Bad Certs"
        else:
            print "Certs are fine"
        return ok

    def __init__(self, server_address, HandlerClass):
        BaseServer.__init__(self, server_address, HandlerClass)
        ctx = Context(SSLv23_METHOD)
        ctx.use_privatekey_file ('keys/server.key')
        ctx.use_certificate_file('keys/server.crt')
        ctx.set_session_id("My_experimental_AJAX_Server")
        ctx.set_verify( VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT | VERIFY_CLIENT_ONCE, verify_callback )
        self.socket = Connection(ctx, socket.socket(self.address_family, self.socket_type))
        self.server_bind()
        self.server_activate()

Note: I made one other change which is from OpenSSL.SSL import ... to simplify your code a bit while I was testing it so you don't have the SSL. prefix in front of every import symbol.

like image 167
aculich Avatar answered Sep 27 '22 22:09

aculich