next up previous contents
Next: Example 2: Verification Up: OpenSSL and EVP Previous: Some general functions   Contents


Example 1: Signing

So we have an engine and a private key. Now it's time to do some signing. We'll just blindly try to read from and write to the given files. As in our PKCS examples, correct handling of these files is left as an excercise to the reader.

void
sign_data_evp(ENGINE *engine,
              EVP_PKEY *key,
              FILE *data_file,
              FILE *signature_file)
{
     unsigned char *data;
     int data_len;

     unsigned char *sig;
     int sig_len;

     int rv;

     EVP_MD_CTX *ctx = EVP_MD_CTX_create();

     sig = malloc(EVP_PKEY_size(key));
     sig_len = EVP_PKEY_size(key);

     rv = EVP_SignInit_ex(ctx, EVP_sha1(), NULL);
     check_ssl_rv("EVP_SignInit_ex", rv, 1);

     data = malloc(1024);
     data_len = fread(data, 1, 1024, data_file);
     while (data_len > 0) {
          rv = EVP_SignUpdate(ctx, data, data_len);
          check_ssl_rv("EVP_SignUpdate", rv, 1);
          data_len = fread(data, 1, 1024, data_file);
     }

     rv = EVP_SignFinal(ctx, sig, &sig_len,  key);
     check_ssl_rv("EVP_SignFinal", rv, 1);

     if (sig_len > 0) {
          fwrite(sig, sig_len, 1, signature_file);
     }

     EVP_MD_CTX_destroy(ctx);
     free(sig);
     free(data);
}

To perform a signing operation, we first need to create a context. The EVP_MD_CTX_create function both allocates memory for that, and initializes it.

We can use EVP_PKEY_size function to figure out the size the signature will have.

As with most cryptographic operations, signing takes three steps; initialization, data feeding, and finalization. In EVP_SignInit_ex we set up the context with a digest function and possibly a specific engine. In this case, we do not specify a specific engine, OpenSSL can figure out what to use from the key we give it.

We specify that we want to use SHA1 hashing by using EVP_sha1, which returns the identifier for the SHA1 algorithm. In the verification method later, we'll use a different approach for this.

If the operation has been initialized successfully, we read our data file in chunks of 1024 bytes, and feed it to the signer by using EVP_SignUpdate.

When we are done feeding our data, we call EVP_SignFinal, which finalizes the operation and creates the signature.

If this worked and we have a signature, we write it to the specified signature file.

Finally, we need to clean our context, by calling EVP_MD_CTX_destroy.

Now to put it all together, we'll create a simple main:

int
main(int argc, char **argv)
{
     ENGINE *engine;
     const char *engine_name = "pkcs11";


     if (argc < 4) {
          printf("Usage: evp_example1 <key id>");
          printf(" <data file> <signature_file>\n");
          exit(1);
     }

     initialize();
     engine = select_engine(engine_name);

     sign_data(engine, argv[1], argv[2], argv[3]);

     clean_engine(engine);
     clean_up();

     return EXIT_SUCCESS;
}

That's it! Compile it with -lcrypto (or your locally needed compilation options), and run it with $OPENSSL\_CONF$ set to your configuration file, and you can create a signature.

With the signature written out, let's see whether we can verify it again.


next up previous contents
Next: Example 2: Verification Up: OpenSSL and EVP Previous: Some general functions   Contents
Written by Jelte Jansen
© NLnet Labs, May 13, 2008
jelte@nlnetlabs.nl