next up previous contents
Next: Deciding whether to use Up: OpenSSL and EVP Previous: Example 1: Signing   Contents

Example 2: Verification

This example looks a lot like the previous one. Of course we will be reading a signature instead of writing it, use some other function calls, and we'll use the public instead of the private key.

int
verify_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 result;
     int rv;

     EVP_MD_CTX *ctx = EVP_MD_CTX_create();

     const EVP_MD *md = EVP_get_digestbyname("SHA1");

     if (!md) {
          fprintf(stderr, "Error creating message digest");
          fprintf(stderr, " object, unknown name?\n");
          ERR_print_errors_fp(stderr);
          exit(1);
     }

     rv = EVP_VerifyInit_ex(ctx, md, NULL);
     check_ssl_rv("EVP_VerifyInit_ex", rv, 1);

     data = malloc(EVP_MD_size(md));
     data_len = fread(data, 1, EVP_MD_size(md), data_file);
     while (data_len > 0) {
          EVP_VerifyUpdate(ctx, data, data_len);
          data_len = fread(data, 1, 1024, data_file);
     }

     sig = malloc(512);
     sig_len = fread(sig, 1, 512, signature_file);

     result = EVP_VerifyFinal(ctx, sig, sig_len,  key);

     EVP_MD_CTX_destroy(ctx);

     free(data);
     free(sig);

     return result;
}

You might have noticed that the first few lines of code after the declarations are different than in the signer. We are using a different method to specify a digest algorithm here. Remember when we called OpenSSL_add_all_digests? This is why we did it; now we can specify a digest by its name as a string. For this example, this might not be too useful. But when you do not use a fixed algorithm, this gives your user more flexibility; it could technically even specify an algorithm that isn't known when your application was developed.

When we have a message digest structure, we use this to call EVP_VerifyInit_ex, which works a lot like EVP_SignInit_ex. After initialization, we feed it the same data as before.

Now we read the signature. Again we assume 512 bytes or less (since that is the size a signature from a 4096-bit key would have).

Then we pass the signature we read to the verification operation with EVP_VerifyFinal. If the signature verifies this call returns 1. If the signature is bogus it returns 0, and if it cannot perform the verification for any other reason it returns -1.

int
verify_data(ENGINE *engine,
            const char *key_id,
            const char *data_file_name,
            const char *signature_file_name)
{
     FILE *data_file;
     FILE *signature_file;
     EVP_PKEY *public_key = get_public_key(engine, key_id);
     int result;

     if (!public_key) {
          printf("no public key\n");
     } else {
          data_file = fopen(data_file_name, "r");
          if (!data_file) {
          fprintf(stderr, "Error opening %s: %s\n",
                  data_file_name,
                  strerror(errno));
                  exit(errno);
          }
          signature_file = fopen(signature_file_name, "r");
          if (!signature_file) {
               fprintf(stderr, "Error opening %s: %s\n",
                       signature_file_name,
                       strerror(errno));
               exit(errno);
          }

          result = verify_data_evp(engine,
                                   public_key,
                                   data_file,
                                   signature_file);

          fclose(data_file);
          fclose(signature_file);

          EVP_PKEY_free(public_key);
     }

     return result;
}

This is mostly the same as the sign_data function.

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

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

     initialize();

     engine = select_engine(engine_name);

     rv = verify_data(engine, argv[1], argv[2], argv[3]);
     check_ssl_rv("Verify data", rv, 1);

     printf("Signature verified: success\n");

     clean_engine(engine);
     clean_up();

     return EXIT_SUCCESS;
}

And there we have it. Using this application with the data file you used and the signature file created in example 1 should print the message 'Signature verified: success'.

Encryption and decryption using assymetric cryptography works roughly the same way, see $EVP\_EncryptInit(3SSL)$ for more documentation on functions that perform these operations.


next up previous contents
Next: Deciding whether to use Up: OpenSSL and EVP Previous: Example 1: Signing   Contents
Written by Jelte Jansen
© NLnet Labs, May 13, 2008
jelte@nlnetlabs.nl