recommendations for storage of accepted certificates

Ted Zlatanov tzz at lifelogs.com
Tue Oct 5 12:54:43 CEST 2010


On Mon, 4 Oct 2010 08:17:21 +0200 Nikos Mavrogiannopoulos <nmav at gnutls.org> wrote: 

NM> 2010/10/4 Ted Zlatanov <tzz at lifelogs.com>:
NM> What do you mean by unknown server? Do you mean known but untrusted? In
NM> any case gnutls doesn't provide such facility for any of them. It was
NM> considered to be application specific (now I'm looking for a solution to
NM> that using pkcs11, but wouldn't be available soon).
>> 
>> Sorry for the badly phrased questions.  Yes: I mean I connect to a known
>> server but its certificate is not trusted (I let GnuTLS verify the
>> certificate chain).  Would I just look at the error and ask the user to
>> accept the certificate and retry?  I was hoping to do it during the
>> handshake with a callback function.

NM> You can do it during the handshake. There is a callback function that
NM> provides you with the peer certificate and you can do verification there.

You mean gnutls_certificate_set_verify_function()?  I see the example in
ex-rfc2818.c.  It seems that gnutls_certificate_verify_peers2() is the
one to call.  The example calls gnutls_x509_crt_check_hostname()
explicitly so I guess I will too.  Also, rather than setting the
verify_flags, it seems cleaner to use these priority string options in
Emacs Lisp:

"%VERIFY_ALLOW_SIGN_RSA_MD5" will allow RSA-MD5 signatures in certificate chains.

"%VERIFY_ALLOW_X509_V1_CA_CRT" will allow V1 CAs in chains.

...but all the verify_flags are not available this way.  Can this be
changed or is that intentional?  If it's intentional I'll provide all
those flags as API options since I can imagine we'll need them.

So, my code will do:

1) set verify function to my own emacs_gnutls_verify

2) emacs_gnutls_verify: call verify_peers2

3) emacs_gnutls_verify: verify the host name (this will be optional):

  if (!gnutls_x509_crt_check_hostname (cert, hostname))
    {
      printf ("The certificate's owner does not match hostname '%s'\n",
              hostname);
      return GNUTLS_E_CERTIFICATE_ERROR;
    }

If verify_peers2 passed in (2) and the hostname passed return approved.

4) emacs_gnutls_verify: load $CERTSTORE/HOSTNAME.pem for the current
hostname.  If it's equivalent to the server's presented certificate (is
there a certificate_equals() function or do I just use memcmp?), the
verification passes and return approved.

5) emacs_gnutls_verify: call any user-defined callbacks (some of which
may approve the certificate by default and will return approved)

6) emacs_gnutls_verify: if the certificate failed verification, the
callbacks didn't approve it, and the gnutls-accept-unknown certificate
option is not nil, use it ('ask to ask the user, true (t) to accept
unconditionally, nil to never accept at this stage).  If the certificate
is approved, save it in $CERTSTORE/HOSTNAME.pem and return approved.

7) emacs_gnutls_verify: return unapproved

Does that seem correct logically and sequentially?  Because users can
provide their own callbacks this turns out to be a bit more complicated
than the usual verification flow but I think it's manageable.

Thanks for your help
Ted





More information about the Gnutls-devel mailing list