Posts tagged ‘openssl’

Validating names in SSL certificates using OpenSSL (0.9.8)

Recently I’ve battled with OpenSSL at work. One thing I needed to do was add name validation to a program that previously hasn’t had it. In an attempt to avoid obvious mistakes I went looking for existing examples for how to do it. I came across some code from Secure Programming.com, it can be found in the code from the book in “/spc-1.1/chapter10/8-unix.c”. Just too bad only a part of the code actually works as advertised. On top of that the working part uses old functions which remain in the API only for backwards compatibility.

In trying to fix up that code I wrote the following little example code for extracting CN and subjectAltName:

#include <stdlib.h>
#include <stdio.h>
 
#include <openssl/pem.h>
#include <openssl/x509v3.h>
 
void getCN( X509 * );
void getSubjectAltName( X509 * );
 
int
main( int argc, char **argv )
{
    FILE *fpem;
    X509 *cert;
 
    if( !( fpem = fopen( argv[1], "r" ))) {
        fprintf( stderr, "Couldn't open the PEM file: %s\n", argv[1] );
        return( EXIT_FAILURE );
    }
 
    if( !( cert = PEM_read_X509( fpem, NULL, NULL, NULL ))) {
        fclose( fpem );
        fprintf( stderr, "Failed to read the PEM file: %s\n", argv[1] );
        return( EXIT_FAILURE );
    }
 
    getCN( cert );
    getSubjectAltName( cert );
 
    fclose( fpem );
    return( EXIT_SUCCESS );
}
 
void
getCN( X509 *cert )
{
    printf( "## %s\n", __PRETTY_FUNCTION__ );
 
    X509_NAME *subjName;
    int idx;
 
    if( !( subjName = X509_get_subject_name( cert )))
        fprintf( stderr, "X509_get_subject_name failed" );
 
    idx = X509_NAME_get_index_by_NID( subjName, NID_commonName, -1 );
    X509_NAME_ENTRY *entry = X509_NAME_get_entry( subjName, idx );
    ASN1_STRING *entryData = X509_NAME_ENTRY_get_data( entry );
    unsigned char *utf8;
    int length = ASN1_STRING_to_UTF8( &utf8, entryData );
    printf( "CN value: %s\n", utf8 );
    printf( "CN length: %d\n", length );
    OPENSSL_free( utf8 );
 
    return;
}
 
void getSubjectAltName( X509 *cert )
{
    printf( "## %s\n", __PRETTY_FUNCTION__ );
 
    GENERAL_NAMES *sANs;
 
    if( !( sANs = X509_get_ext_d2i( cert, NID_subject_alt_name, 0, 0 ))) {
        printf( "No subjectAltName extension\n" );
        return;
    }
 
    int i, numAN = sk_GENERAL_NAME_num( sANs );
    printf( "subjectAltName entries: %d\n", numAN );
    for( i = 0; i < numAN; ++i ) {
        GENERAL_NAME *sAN = sk_GENERAL_NAME_value( sANs, i );
        // we only care about DNS entries
        if( GEN_DNS == sAN->type ) {
            unsigned char *dns;
            ASN1_STRING_to_UTF8( &dns, sAN->d.dNSName );
            printf( "subjectAltName DNS: %s\n", dns );
            OPENSSL_free( dns );
        }
    }
 
    return;
}

Based on this I should be able to finish the patch I’ve been working on.

OpenSSL CA

As I mentioned here I’ve been having problems with my Windows CA. I’ve now managed to get an OpenSSL CA up and running and I’m able to get my Windows box to accept the certs/keys. This is how I did it.

First make sure all tools are available. You’ll need openssl, make, and some way of getting the generated certs/keys from your CA machine to your server, I use SSH for that.

I used this site as inspiration. I changed the policy to the more accepting policy_anything and I changed the description of my CA in root_ca_distinguished_name. After that I ran make init to create the CA cert and key.

$ make init
Generating a 2048 bit RSA private key
........+++
.................................+++
writing new private key to './private/ca-key.pem'
-----

Quite a few new directories and files have been created:

$ tree
.
|-- Makefile
|-- ca-cert.pem
|-- crl
|-- index
|-- newcerts
|-- openssl.cnf
|-- private
|   `-- ca-key.pem
`-- serial

3 directories, 6 files

We’ll convert the CA’s certificate (ca-cert.pem) to a format that Windows can use right away, we’ll need it later:

$ openssl x509 -in ca-cert.pem -outform der -out ca-cert.der

Now it’s time to create a certificate signing request (CSR). To impose some order I created a dir to keep my client-related stuff in, $ mkdir -p client/svc01. After changing into the client dir I create a CSR:

$ openssl req -newkey rsa:2048 -keyout svc01_priv.pem -keyform pem \
    -out svc01.csr -outform pem

A few questions are asked about the cert/key (remember that the common name should be the FQDN of the server), and a password for the private key is required (at least four characters long). The CSR is saved to svc01.csr. Now change back to the CA top directory and copy the CSR here. Then run make sign to issue the certificate. The cert ends up in svc01.cert, move it down to the client directory ($ mv svc01.cert client/svc01/svc01_cert.pem, since I’m using the CA to issue certificates for Windows servers I like to make the file format explicit).

Now that we have the signed certificate, svc01_cert.pem, and the private key, svc01_priv.pem, we need to package them both in a PKCS12 file so that Windows can use them:

$ openssl pkcs12 -export -inkey svc01_priv.pem -in svc01_cert.pem \
    -out svc01_CertNKey_p12.cer

You’ll have to enter the password to unlock the private key, you’ll also have to enter the password to be used for exporting the private key out of the resulting file later on (I like leaving it empty). Copy the file to the Windows machine and change its suffix to .pfx, now you should be able to import the key and cert. In order to use the key you’ll also have to install your CA’s cert (ca-cert.der).