Friday, October 23, 2009


Since 2008, MD5 is considered broken. Consequently GnuTLS refuses signatures of certificates if they were made with MD5. That's why since my latest Debian upgrade, msmtp, which uses libgnutls, has not been able to establish a checked TLS session with the SMTP server.

This means, the SMTP server needs new certificates. So I started investigating how this can be done. First thing I noticed is, that there are two SSL/TLS implementations in the open source world. First the above mentioned GnuTLS and second the honorable OpenSSL. Why is this?
Wikipedia knows the story. The OpenSSL article states:
The OpenSSL license requires the phrase "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit" to appear in advertising material and any redistributions [...]. Due to this restriction it is incompatible with the GPL.
And the GnuTLS article says:
GnuTLS was initially created to allow applications of the GNU project to use secure protocols such as TLS. Although OpenSSL already existed, OpenSSL's license is not compatible with the GPL.
I don't know any further details but I hardly believe that the OpenSSL was not asked to change their license. Must be important to them to have this little sentence included everywhere. Anyway, we now have the same functionality implemented and distributed twice.

I decided to use GnuTLS without having much arguments for that. I went through the documentation and here is how it worked. I created a new root certificate authority and new certificates for Postfix, Apache, lighttpd and ejabberd.

First of all we have to get a grip on all those abbreviations:
  • X.509 is the standard behind the whole thing with certificates, signatures and the trust model of TLS/SSL.
  • DER stands for Distinguished Encoding Rules. It is an encoding which is used for certificates.
  • PEM stands for Privacy-enhanced Electronic Mail. It is a file format which puts a simple header and a simple footer around the base64 encoded DER certificate or key.
  • .cer and .crt are common file extension for certificate files.
  • .key is a common file extension for private keys.
There are some other abbreviations, too, but we do not need them. So, first thing we have to do is to generate a new private key, the root of the chain of trust. In common terms this root is called the root certificate authority (CA). The key is generated with this command:

certtool --generate-privkey > ca.key.pem

If it takes too long help the system to gather entropy by using the keyboard, moving the mouse or causing some network traffic.

Next we need a certificate, which is the public key that we can pass around. As there is no other authority which validates the certificate, we have to sign the signature ourselves. This is done with (using zsh):

certtool --generate-self-signed  --outfile ca.crt.pem \
--load-privkey ca.key.pem
--template =(
    echo "cn = your name"; 
    echo "ca"; 
    echo "cert_signing_key";
    echo "expiration_days = 3650";

cn is the common name of the certificate owner, ca indicates that this is a CA certificate, cert_signing_key states that this key will be used to sign other certificates and finally expiration_days defines how long this certificate is valid.

Now we have two files, the secret ca.key.pem and the public ca.crt.pem. Next we want to create a certificate for the server. First thing to do, again is to create a secret key, the identity of the server.

certtool --generate-privkey >

Next we create a corresponding certificate and sign it with the root CA:

certtool --generate-certificate \
--outfile \
--load-privkey \
--load-ca-certificate ca.crt.pem \
--load-ca-privkey ca.key.pem \
--template =(
    echo "organization = community"; 
    echo "cn ="; 
    echo "tls_www_server"; 
    echo "encryption_key"; 
    echo "expiration_days = 730";

organization is - as far as I can tell - an arbitrary string identifying the organization of the subject. cn again is the common name and must match the domain name of the server, because the domain name is how clients identify the server. I am not sure about tls_www_server, but I think this flag indicates that the certificate will be used by a TLS server, rather than a client. encryption_key is a flag which indicates that this certificate will be used to encrypt data. I didn't had much time to play around with the various options. This is the set that worked for me, maybe there are better choices.

Next we have to configure the server to use the given key and certificate file. In case of Postfix this are the options smtpd_tls_cert_file and smtpd_tls_key_file. In contrast to that lighttpd wants a single file with both information. Due to the PEM file format this is no problem. We simply have to cat both files into a single new one.

Finally we want to distribute the certificate of our root CA through a authenticated channel to the users of the clients. They can import it in order to make an automated authentication possible. They can also check the validity of the certificates of the services manually with:

certtool --verify-chain \
  --infile =(cat ca.crt.pem)

Some clients like firefox just present the certificate's fingerprint to the user if the corresponding root CA is not imported. For this and other cases it is convenient for the users to have this fingeprint at hand. The fingerprint of a certificate can be retrieved with:

certtool --certificate-info \
  --infile \
  | grep -A 1 fingerprint

Again is must be transmitted to them over an authenticated channel, otherwise the whole chain of trust is undercut.

Update: Now, two years later, the certicates have expired. To set a new expiration date, you can do the following:

certtool --update-certificate \
--load-certificate \
--outfile \
--load-ca-privkey ca.key.pem \
--load-ca-certificate ca.crt.pem

No comments:

Post a Comment