Stjepan Groš

GNUTLS

On this page I'll write about the use of the GNUTLS library. I don't know much about that library yet but it seems to me to be better than OpenSSL. The reason for the statement is simple. I looked into the code of OpenSSL and it's a complete mess. The next problem with OpenSSL is that the documentation is lacking, and in many cases, features are poorly documented, if at all. The one additional plus for GNUTLS is that it allows certificates to be stored in the memory, while OpenSSL (as far as I know) insist for them to be stored in a file. Of course, the license of OpenSSL is more liberal than that of GNUTLS which might be crucial in some cases, but for my purpose that's irrelevant.

GNUTLS manual has already some examples of how to use GNUTLS in your programs but the emphasis in this work is on certificate handling, not on the TLS communication.

The material presented here resulted from my learning of GNUTLS with the intention to replace or supplement OpenSSL in the ikev2 implementation I'm working on. Thus, some decisions on what to do are based on what I need in ikev2, not what's usually needed in the standard TLS code. :)

Prerequisites

In this section I'll describe how to compile the programs that use GNUTLS. Also, I'll describe some helper functions that are not specific to GNUTLS but are necessary for later examples.

The first thing you have to be certain is that GNUTLS is installed. Also development libraries have to be installed. It's not important whether you use packages provided with your distribution, or compile the sources. On this page I'm using GNUTLS packages provided with Fedora 10.

Download this C source. It contains two important parts. The first one are helper functions, and the second one is a simple call to GNUTLS library that we'll use to test whether your environment for development of GNUTLS programs works or not.

To compile the given source issue the following command:

gcc -ggdb -o gnutls0 gnutls0.c `pkg-config --libs --cflags glib-2.0` `pkg-config --libs --cflags gnutls`

Note that in the previous line backticks are used. The after pressing enter you shouldn't receive any message and the prompt should appear again. If any error is printed, than there are several possibilities what went wrong:

You can not continue until the previous command runs without any warnings. When you succeed in that, there will be an executable in the current directory called gnutls0. Start this executable like this:

$ ./gnutls0
$

This executable doesn't do any real work, just initializes GNUTLS library. If you receive any error messages running that command that the most likely problem is that necessary libraries can not be found (libgnutls.so) because they are in some non-standard location. In such cases find the directory where those libraries are and set environment variable to LD_LIBRARY_PATH so that the given directory is searched for the necessary libraries.

Helper functions

In the given source file there are several helper functions I'll describe now. The point is that they are not important for the core functionality I'm trying to describe but we'll use them regularly:

Loading and printing certificate

The first thing we will do is to load a certificate and print it to a standard output. This is done in the following C program. This source is compiled in exactly the same way as the first one (gnutls0.c). Now, if you wish you can compile it and try to run it. It accepts a single argument that is a filename of a certificate that has to be loaded and printed to standard output. You can take any certificate you wish. Furhtermore, there is a certificate repository on your computer with all the trusted CAs on the Internet. But here is a RSA Security's CA certificate for a start.

Now, let us anlyze what has been done in the code. Here, only a content of the main function is shown where core functionality resides:

 1: int main(int argc, char **argv)
 2: {
 3:        gnutls_x509_crt_t crt;
 4:        gnutls_datum_t *crt_file = NULL;
 5:        int ret;
 6:        gnutls_datum_t txt_cert;
 7:
 8:        if (argc != 2) {
 9:                fprintf (stderr, "Usage: %s <certificate>\n", argv[0]);
10:                return 1;
11:        }
12:
13:        if ((crt_file = load_file(argv[1])) == NULL)
14:                return 1;
15:
16:        gnutls_global_init();
17:
18:        gnutls_x509_crt_init(&crt);
19:
20:        ret = gnutls_x509_crt_import(crt, crt_file, GNUTLS_X509_FMT_PEM);
21:        unload_file(crt_file);
22:
23:        if (ret < 0) {
24:                fprintf(stderr, "Error: %s\n", gnutls_strerror(ret));
25:                return 1;
26:        }
27:
28:        gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_FULL, &txt_cert);
29:
30:        gnutls_x509_crt_deinit(crt);
31:
32:        printf("%s\n", txt_cert.data);
33:}

Lines 3 to 6 define necessary local variables, while in the lines 8 to 11 we are checking if the program has been called correctly. Then, in line 13 we use load_file function mentioned in the Prerequisites section to load certificate file into the memory. Then, line 16 initializes GNUTLS library. This call is necessary before any function from GNUTLS can be called. Line 18 initializes the structure where parsed certificate will be stored. The certificate, as stored in the file, is in a special textual format called PEM. Before GNUTLS can use it it has to be converted into internal representation which is represented with a data structure gnutls_x509_crt_t. The actual parsing is performed by the function gnutls_x509_crt_import in line 20 after which don't need certificate from file any more and thus we free it's memory in line 21. In lines 23 to 26 a check is made that the conversion process has been without errors and then in the line 28 a function from GNUTLS is called that converts certificate into ASCII representation. This representation is stored in the memory in the structure of gnutls_datum_t which is then printed to standard output in the line 32. In line 30 we free memory occupied by the internal representation of the certificate as we don't need it any more.

Loading private key and signing data

Loading private key is very simple operation, almost the same as loading certificate. So in this section I'll show you how to load a private key and then how to sign data with the key.

You can download the source for this example and try to compile it. When you start the given example you have to give it two arguments. The first one is the file with a private key in a PEM format. The second argument is a file that you want to be signed with a given key. The signature is written on the standard output.

Let's look into the source of the example:

 1: int main(int argc, char **argv)
 2: {
 3:         gnutls_x509_privkey_t privkey;
 4:         gnutls_datum_t *key_file = NULL;
 5:         gnutls_datum_t *data = NULL;
 6:         char signature[SIGNATURE_MAX_SIZE];
 7:         size_t signature_size = SIGNATURE_MAX_SIZE;
 8:         int ret;
 9: 
10:         if (argc != 3) {
11:                 fprintf (stderr, "Usage: %s  \n", argv[0]);
12:                 return 1;
13:         }
14:
15:         if ((key_file = load_file(argv[1])) == NULL)
16:                 return 1;
17:
18:         gnutls_global_init();
19:
20:         gnutls_x509_privkey_init(&privkey);
21: 
22:         ret = gnutls_x509_privkey_import(privkey, key_file,
23:                         GNUTLS_X509_FMT_PEM);
24:         unload_file(key_file);
25: 
26:         if (ret < 0) {
27:                 fprintf(stderr, "Error: %s\n", gnutls_strerror(ret));
28:                 return 1;
29:         }
30:         if ((data = load_file(argv[2])) == NULL)
31:                 return 1;
32: 
33:         ret = gnutls_x509_privkey_sign_data(privkey, GNUTLS_MAC_SHA1, 0, data,
34:                         signature, &signature_size);
35: 
36:         if (ret < 0) {
37:                 fprintf(stderr, "Error: %s\n", gnutls_strerror(ret));
38:                 return 1;
39:         }
40: 
41:         dump_hex(signature, signature_size);
42: }