Monday, January 30, 2017

OpenSSL Parameterized Configuration

This is a common question that comes up in ##openssl with regards to handling openssl req, and here is a strategy to make values in the configuration parameters:
openssl genrsa -out ca.key 2048
echo "
[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = v3_ca

[ req_distinguished_name ]
C                = AB
ST               = CD
L                = EF
O                = G
OU               = HI
CN               = $cn

[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints = CA:true
" > $config_file
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -config $config_file

How to generate a matching CA with a different signing algorithm

This is a bit past it's time ever since Google decided to deprecate SHA-1, but there is still a possibility for a site to need a CA with a different signing algorithm to resolve compatibility with some cryptography in an environment. This in general is not something that should be taken as a recommendation, it is something that needs to be determined is an unfortunate circumstance to support some piece of software or hardware in environment.

The first thing to remember is that a certificate can be simplified to (certificate_info, public_key, signature). The first step of verification is to look through the CA data to match strings of issuer and subject, and then start doing the cryptographic validation. This lookup is the piece you are trying to take advantage of to have differing CA's. This can be seen as part of the following steps:

openssl genrsa -out CA.key 2048
openssl req -new -x509 -key CA.key -out CA.sha256.crt -sha256 -subj /C=AB/ST=CD/L=EF/O=G/OU=HI/CN=JK
openssl req -new -x509 -key CA.key -out CA.sha512.crt -sha512 -subj /C=AB/ST=CD/L=EF/O=G/OU=HI/CN=JK
openssl req -new -x509 -key CA.key -out CA.sha512b.crt -sha512 -subj /C=ED/ST=RFR/L=GHH/O=URU/OU=DS/CN=OL

These openssl req can be done how a CA is normally signed or some other CA signing infrastructure. The particular invocation above was only done for illustration. In the end we have certificates that look like this:
$ find -name 'CA.*.crt'  -print -exec openssl x509 -in {} -subject -noout \;
subject= /C=AB/ST=CD/L=EF/O=G/OU=HI/CN=JK
subject= /C=AB/ST=CD/L=EF/O=G/OU=HI/CN=JK
Then let's sign a new host key with just one certificate from the CA, but using the unique private key for the CA.
openssl genrsa -out host.key
openssl req -new -key host.key -out host.csr
openssl x509 -req -days 3650 -in host.csr -out host.crt -CAkey CA.key -CA CA.sha256.crt  -sha256 -set_serial 2
This is how a normal signing works, and now let's see how it verifies:
$ find -name 'CA.*.crt'  -print -exec openssl verify -CAfile {} host.crt \;
host.crt: OK
host.crt: OK
host.crt: C = QA, ST = WSD, L = ED, O = RGFG, OU = YHJ, CN = UJ
error 20 at 0 depth lookup:unable to get local issuer certificate
As you can see from the steps above we are using the same private key material in CA.key with several certificates, but at the moment of verification we are only using one of the CA certificates to verify the signature of the host certificate. In practice you should be making sure only one is available in an environment as the verification process will generally take the first match- even if it can't process it. There have been instances in the past where a MD5 based certificate and a SHA-1 certificate were in a CA trust store but because of natural ordering the MD5 certificate was selected first and was rejected on policy grounds since MD5 is deprecated.

Wednesday, January 25, 2017

Using openssl s_server and openssl s_client to test client certificates

In openssl's man pages understanding how to invoke openssl s_server to experiment with client certificates can be challenging as there is not enough examples on that man page compared to others. A good understanding of how to setup a CAfile that validates with openssl s_client is helpful here, with the general logic being PEM-format certificates joined in a single file. On Unix this is easy with cat rootCA.crt intermediateCA.crt > caFile.crt, and we will be using this caFile.crt throughout this example. It is expected that this file has enough information to validate both the client and the server.

This is easiest to do with two separate terminals with one terminal running the following command:
openssl s_server -accept 10000 -cert server.crt -key server.key -verify 10 -CAfile caFile.crt

And the other terminal running this command:
openssl s_client -connect localhost:10000 -cert client.crt -key client.key -CAfile caFile.crt

For both commands we are using certificates, and so we need the certificate piece with -cert and the key piece with -key. We had already described that we needed a file containing the CA information to verify certificates (caFile.crt) and this is a required piece for verification on the server side, and on the client side since s_client proceeds whether or not the certificate validates.

-accept indicates what port to listen on, which is reflected in the -connect parameter to s_client but is otherwise uninteresting.

The last and most critical piece is -verify which comes in two versions of -verify and -Verify. Without this parameter s_server does not request a certificate. With -verify it requests a certificate but proceeds if one is not sent (something that I describe as 'want'), and with -Verify it requests a certificate and does not proceed if one is not sent (something that I describe as 'need'). The parameter's value is just the depth of the certificate chain, and this is knowledge you would know from working with the CA where you are generating the certificates. If you aren't worrying about verification chain depth for this testing just pick a big number.

After you have a mutual connection or otherwise, you can type into s_client or s_server and then hit return to send a command as if you had connected with netcat or telnet to a non-TLS port. This is something you can use for other situations, such as sending GET / then hitting return twice to send a HTTP GET request to a remote server.