Saturday, 13 April 2013

Verifying signatures with OpenSSL API

A brief introduction

This is another article where I try to tame OpenSSL API using C++11. This time round I describe a small example showing how to verify signed data programatically. There are many message formats catering for different needs. In this example I show how to verify that the data is not tampered and is sent from a party identified by a PKI certificate. Please refer to my other article to learn how to verify a certificate.

Again I want to emphasize that you should not implement this functionality as you can use openssl tool:

openssl dgst -verify test-key-pub.pem \
  -signature /tmp/signature </tmp/data
This command checks that the data stored in /tmp/data is not tampered. The tool calculates a checksum (a digest) and verifies it with the signature stored in /tmp/signature. The signature has been signed with the private key paired with the public key stored in test-key-pub.pem. If there's a certificate associated with the public key available, it can also be verified to see wether the data hasn't been signed by an intruder in the middle.

As you can see, there's no need to invent the wheel if your requirements are simple enough. Depending on the circumstances this approach might not be sufficient or acceptable, and only then you should come to grips with your own implementation.

Producing or verifying a signature is rather expensive operation as it involves asymmetric cryptography. In practice a digest is produced first (e.g. using SHA1) and then the digest is signed with one of the asymmetric keys. The verification comprises applying the same digest function to the received data and checking whether the signature of that digest "matches" when using the other key of the asymmetric pair. You don't have to worry about these details though as they are hidden behind the OpenSSL API. Hopefully this also allays concerns about the use of the openss dgst command which stands for "digest". The signature is simply another step in the process of digesting data.

Code

Code for this example is available here. There's also a very basic test script provided.

The main three functions we are going to use are EVP_VerifyInit_ex(), EVP_VerifyUpdate(), and EVP_VerifyFinal(). The first two of them are simply aliases (macros) of equivalent "digest" functions. Of course you shouldn't abuse them and better use the macros provided to be explicit about the intentions. Please also note that in general *_ex() versions of OpenSSL API functions are recommended if available as they are more general and allow you to use an engine. If you don't intend to use an engine simply use nullptr.

The "trinity" is a common pattern in the OpenSSL API.

zVZNb9wgEP01PrYyptlkj+02aXuoVCmq2p4qamZtFGwsjHft/PpCwR8Y73Y3WiW5WPAGhpk3jzER3hTtJ0mq/KugwKMkpm2EP0ZJchPH+muAbgZkklELIQc0jELtQUoIrljlg6koS0iVh20F951VJIMAuE8JD9EfjKrcBZesRvwzsCzvj0GrtbXUqut9UNiShqs3/6DEmtvY2hK3vHPz695v6Z3/KEThARJq9ujHuGUuCEfcHyEpSA/irHyYkoRvI7yRQig7KtoNcFOVnnG77e6AdeBGQqlO2eBy3xHeuNC/lDroOV96k66lnnzY50zBfUVSY9lr3YQnuiB2IBW089S02EAUoGRnKLdW7LhwOnvnpvtJdR2UTwrbY8TRlw1+x3z1wKW8nD4O0v9eUaLgmQlAVy/GwCpg4I6VhLP6uTlIrl+Mg3XAQZB72sgdmOVIpw8tUz/1OH575Wa/zMyMS330xGSm1naIoFo0MgXvMioiM1CeQIF6PTEkccISihdoGkAJnCi28/vUEnnujG+C6YiHKt34RRqq1Huw6bhN074z84NmjtDckSUhcPReStJNllVmQX043iHvPuD4eFirY8v1wJ4/qmoowUlCQ3GgtEJI+K1bDtGwQQwYbwk390/fTdwSraP/yPFs0b0aOaH18qU/V04YHfdzITUNf+3T1BTclgurKfx7eZoZhosaU7IJW/xym5s0OU9vaKq2hafHQovDr7jFdTM/Z/e0+cZL9bDZn7F/LTxRR3o6vi/t8vH5j2//Ag==
First you initialise the algorithm, then there's a one or more updates that feed the algorithm with data, and in the end you finalise the algorithm. The update step allows to process "streamed" data, i.e. you feed the algorithm with data as it arrives. If all data is available at once, you can make only one update call. In many situation though you might want to process data in chunks, e.g. when you read a large file or from a network socket.

More details about the API used in this example are available in the manual so there's no point in duplicating them here. If you're off-line and have openssl-devel (or equivalent) package installed (which you should in order to compile this example), you can also use info or man pages. Don't forget to read about EVP_MD_CTX_create() and EVP_MD_CTX_destroy().

Build and test

This is how I build the example on my Fedora 18 laptop:

g++ -std=c++11 -O3 -DNDEBUG signature-verify.cpp \
  -lcrypto -o /tmp/my-verifier
I think that the most frustrating thing about keys, certificates and all this cryptographic stuff is testing. Creating test assets (key material, certificates etc.) can be truly onerous. But this is still not as hard as testing a full production system with real cryptographic material (very often hardware assited), so let's get on with it:
# generate test private key and associated certificate
openssl req -x509 -newkey rsa:2048 \
  -keyout test-key-priv.pem -subj "/CN=FakeSigner" \
  -passout pass:none -out test-cert.pem

# sign some test data
echo -n "test" | tee /tmp/data | \
openssl dgst -sha1 -sign test-key-priv.pem \
  -passin pass:none -out signature
And finally we can run our verifier:
# verify signed data
/tmp/my-verifier test-cert.pem /tmp/data signature
As the steps above are a bit tedious, you can use a test script I provide here. Simply give it the path to the verifier executable as an argument and that's it. It creates a temporary scratch directory where it generates the assets and runs rudimentary tests using the executable provided. As I wanted to keep it dead simple, it doesn't provide any additional options like preserving the scratch directory, setting verbosity level etc. It'll probably evolve in future incarnations once I've got examples in my repository a bit reorganised.
./signature-verify-test.sh /tmp/my-verifier

Happy verifying!

Verifying signatures with OpenSSL

No comments:

Post a Comment