RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

ocsp_resp_find_key.c

/* $Id: ocsp_resp_find_key.c,v 1.1.2.10 2005/11/16 00:18:03 patrick Exp $ */
/*
 * Copyright (C) 1998-2005 RSA Security Inc.
 *
 * This file shall only be used to demonstrate how to interface to an
 * RSA Security Inc. licensed development product.
 *
 * You have a royalty-free right to use, reproduce and distribute this
 * demonstration file, provided that you agree that RSA Security Inc.
 * has no warranty, implied or otherwise, or liability for this
 * demonstration file (including any modified version).  This software
 * is provided "as is" without warranties or representations of any
 * kind. RSA Security disclaims all conditions and warranties, statutory
 * and otherwise, both express and implied, with respect to the software,
 * its quality and performance, including but not limited to, all
 * implied warranties of merchantability, fitness for a particular
 * purpose, title and noninfringement of third party rights. Without
 * limiting the foregoing, RSA Security does not warrant that the
 * software is error-free or that errors in the product will be
 * corrected. You agree that RSA Security shall not be liable for any
 * direct, indirect, incidental, special, consequential, punitive or
 * other damages whatsoever resulting from your use of this software
 * or any modified version.
 *
 *
 */

/*
 * @file ocsp_resp_find_key.c
 * This sample demonstrates how to use the ResponderID to find the responder's
 * public key and verify the signature of an OCSP response.
 *
 * For example, to:
 *
 * Find the responder's public key and verify an OCSP response:
 *   ocsp_resp_find_key -resp response.bin
 *
 * where: response.bin = The OCSP response file (binary).
 */

#include "r_prod.h"
#include "ocsp_com.h"

#define MAX_BUF_LEN 1024

/* Usage help message. */
static char *ocsp_resp_find_key_usage[] =
{
    "usage: ocsp_resp_find_key [options]\n",
    "where options are:\n",
    " -resp file           - The file containing the OCSP response message.\n",
    " -help                - Print help message.\n",
    " -eg                  - Print some example usages.\n",
    NULL
};

static char *ocsp_resp_find_key_example_usage[] =
{
    "Find the responder's public key and verify an OCSP response:\n",
    "  ocsp_resp_find_key -resp response.bin\n",
    "\n",
    "where: response.bin = The OCSP response file (binary).\n",
    "\n",
    NULL
};

/*
 * Main sample program entry point.
 *
 * @param argc  [In]  The number of arguments typed on the command line.
 * @param argv  [In]  The array of individual arguments from the command line.
 *
 * @returns  R_ERROR_NONE indicates success.
 *           See @ref R_ERROR_IDS for valid values.
 */
int main(int argc, char **argv)
{
    int               ret       = R_ERROR_NONE;
    R_LIB_CTX        *lib_ctx   = NULL;
    R_OCSP_CTX       *ocsp_ctx  = NULL;
    R_OCSP_RESP      *ocsp_resp = NULL;
    BIO              *bio_out   = NULL;
    char             *resp_file = NULL;
    unsigned char    *resp_data = NULL;
    unsigned int      resp_len;
    unsigned int      num_bytes;
    int               print_usage = 0;
    int               print_text = 0;
    char            **pp;

    R_TITEM           responder_id;
    R_CERT_NAME      *responder_name = NULL;
    unsigned int      num_certs;
    unsigned int      cert_index;
    R_CERT           *current_cert = NULL;
    R_CERT_NAME      *current_name = NULL;
    int               found_match = 0;

    unsigned char    *name_string = NULL;

    R_OCSP_RESP_ENTRY *ocsp_resp_entry = NULL;
    R_EXT *extension = NULL;

    /* The public key in the certificate, used to verify the signature. */
    R_PKEY *public_key = NULL;
    int is_verified = 0;

    /*
     * Create a BIO to stdout. BIOs are the Basic Input/Output mechanism
     * provided by RSA. These are recommended for all input and output from
     * applications.
     */
    if ((bio_out = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto end;
    }

    /* Skip the program name. */
    argc--;
    argv++;

    /*
     * Parse the command line parameters.
     */
    while (argc >= 1)
    {
        /* The OCSP response message file. */
        if (Strcmp(*argv, "-resp") == 0)
        {
            if (--argc < 1)
            {
                print_usage = 1;
                goto end;
            }
            resp_file = *(++argv);
        }
        /* Display the usage information. */
        else if (Strcmp(*argv, "-help") == 0)
        {
            print_usage = 1;
            goto end;
        }
        /* print the example usage */
        else if (Strcmp(*argv,"-eg") == 0)
        {
            char **egp;
            for (egp = ocsp_resp_find_key_example_usage; (*egp) != NULL; egp++)
            {
                BIO_printf(bio_out, *egp);
            }
            goto end;
        }
        /* Unknown option. */
        else
        {
            BIO_printf(bio_out, "Unknown option %s\n", *argv);
            print_usage = 1;
            goto end;
        }
        argc--;
        argv++;
    }

    /* Check that the parameters provide all the data needed for performing
     * verification operations.
     */
    if (resp_file == NULL)
    {
        BIO_printf(bio_out, "Input file required\n");
        print_usage = 1;
        goto end;
    }


    /*************************************************************************
     * Step 1. Create the library context.
     * Retrieve the default resource list to provide access to all
     * configurable aspects of the library.
     *************************************************************************/
    if ((ret = PRODUCT_LIBRARY_NEW(PRODUCT_DEFAULT_RESOURCE_LIST(),
        R_RES_FLAG_DEF, &lib_ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Library new failure\n");
        goto end;
    }

    /*************************************************************************
     * Step 2. Create a new OCSP context.
     * Create an R_OCSP_CTX with which to create the OCSP response object.
     *************************************************************************/
    if ((ret = R_OCSP_CTX_new(lib_ctx, R_RES_FLAG_DEF, &ocsp_ctx)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_OCSP_CTX_new failure\n");
        goto end;
    }

    /*************************************************************************
     * Step 3. Create an OCSP response object from the response file.
     * Load the data from the file, and use the binary data to create an
     * OCSP response object with the OCSP context.
     *************************************************************************/
    /*
     * Read the data from an OCSP response message file into the buffer.
     * The OCSP response message is in binary form, and copied to a
     * buffer pointed to by resp_data.
     */
    if ((ret = data_from_file(bio_out, resp_file, &resp_data, &resp_len)) !=
        R_ERROR_NONE)
    {
        goto end;
    }
     /*
      * Create an OCSP response object from the binary data.
      */
    if ((ret = R_OCSP_RESP_from_binary(ocsp_ctx, R_FLAG_SHARE_DATA,
        resp_len, resp_data, &num_bytes, &ocsp_resp)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_OCSP_RESP_from_binary failure\n");
        goto end;
    }

    /*************************************************************************
     * (Optional) Print out OCSP response information.
     *************************************************************************/
    if (print_text) {
        if ((ret = R_OCSP_RESP_write(ocsp_resp, bio_out, R_FORMAT_TEXT,
            NULL)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "R_OCSP_RESP_write failure\n");
            goto end;
        }
        BIO_printf(bio_out, "\n");
    }

    /*************************************************************************
     * The following is the structure of an OCSP response.
       ResponseData ::= SEQUENCE {
          version              [0] EXPLICIT Version DEFAULT v1,
          responderID              ResponderID,
          producedAt               GeneralizedTime,
          responses                SEQUENCE OF SingleResponse,
          responseExtensions   [1] EXPLICIT Extensions OPTIONAL }

       ResponderID ::= CHOICE {
          byName               [1] Name,
          byKey                [2] KeyHash }

       KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
           (excluding the tag and length fields)
     *************************************************************************/

    /*************************************************************************
     * Step 4. Retrieve the responderID in ResponseData.
     *************************************************************************/
    if ((ret = R_OCSP_RESP_get_info(ocsp_resp,
        R_OCSP_RESP_INFO_RESPONDER_ID,
        &responder_id)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_OCSP_RESP_get_info failure getting \
R_OCSP_RESP_INFO_RESPONDER_ID, \n");
        goto end;
    }

    /*************************************************************************
     * Step 5. Retrieve the certificates in a BasicOCSPResponse.

       BasicOCSPResponse       ::= SEQUENCE {
          tbsResponseData      ResponseData,
          signatureAlgorithm   AlgorithmIdentifier,
          signature            BIT STRING,
          certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
     *************************************************************************/

    /* Get the number of certificates in a BasicOCSPResponse. */
    num_certs = (unsigned int)-1;
    if ((ret = R_OCSP_RESP_get_info(ocsp_resp,
        R_OCSP_RESP_INFO_CERTIFICATE_COUNT, &num_certs)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_OCSP_RESP_get_info failure getting \
R_OCSP_RESP_INFO_CERTIFICATE_COUNT\n");
        goto end;
    }

    BIO_printf(bio_out, "%d certificates in this BasicOCSPResponse.\n\n",
        num_certs);

    switch (responder_id.type)
    {
    case R_OCSP_RESPONDER_ID_TYPE_KEY_HASH:
        /* This sample does not handle OCSP responses that contain the
           ResponderID as a KeyHash, because it is more common for the
           ResponderID to be a Name. To add this functionality, go through
           each certificate, get the public key, and calculate the SHA-1 hash
           of the public key (excluding the tag and length fields).
           Then compare the calculated hash to the ResponderID. */
        BIO_printf(bio_out, "This sample does not handle OCSP responses that");
        BIO_printf(bio_out, "contain the ResponderID as a KeyHash.\n");
        goto end;
        break;

    case R_OCSP_RESPONDER_ID_TYPE_NAME:
        responder_name = (R_CERT_NAME *)responder_id.data;

        /**********************************************************************
         * Step 6. Check each certificate until you find a matching name.
         *********************************************************************/
        for (cert_index = 0; cert_index < num_certs; cert_index++)
        {
            R_INDEXED_INFO indexed_info;
            indexed_info.index = cert_index;

            /* Get the current certificate. */
            if ((ret = R_OCSP_RESP_get_info(ocsp_resp,
                R_OCSP_RESP_INFO_CERTIFICATE, &indexed_info)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out, "R_OCSP_RESP_get_info failure getting \
R_OCSP_RESP_INFO_CERTIFICATE\n");
                goto end;
            }

            current_cert = (R_CERT *)(indexed_info.data);

            /* Retrieve the subject name from the certificate. */
            if ((ret = R_CERT_subject_name_to_R_CERT_NAME(current_cert,
                R_FLAG_SHARE_DEFAULT,
                &current_name)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out,"R_CERT_subject_name_to_R_CERT_NAME \
failure\n");
                goto end;
            }

            /* Compare the responder name with the subject name from the
               certificate. */
            if (R_CERT_NAME_is_equal(responder_name, current_name))
            {
                BIO_printf(bio_out,"Found a matching certificate at index \
%d\n", cert_index);
                found_match = 1;
                break;
            }

        } /* end for */

        if (!found_match) {
            BIO_printf(bio_out,"Did not find a matching certificate.\n");
            ret = R_ERROR_NOT_FOUND;
            goto end;
        }

        /* Print out the subject name of the matching certificate. */
        if ((name_string = (unsigned char *)Malloc(MAX_BUF_LEN)) == NULL)
        {
            BIO_printf(bio_out, "Malloc failure allocating memory for name \
string\n");
            ret = R_ERROR_ALLOC_FAILURE;
            goto end;
        }

        ret = R_CERT_NAME_to_string(current_name, MAX_BUF_LEN,
            (char *)name_string);

        BIO_printf(bio_out, "  Subject name: \"%s\"\n\n", name_string);

        /**********************************************************************
         * Step 7. Retrieve the public key from the certificate.
         *********************************************************************/
        if ((ret = R_CERT_public_key_to_R_PKEY(current_cert,
            R_FLAG_SHARE_DEFAULT,
            &public_key)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out,"R_CERT_public_key_to_R_PKEY failure\n");
            goto end;
        }

        /* Clean up. */
        if (current_name != NULL)
        {
            R_CERT_NAME_free(current_name);
            current_name = NULL;
        }
        if (name_string != NULL)
        {
            Free(name_string);
            name_string = NULL;
        }
        break; /* end case R_OCSP_RESPONDER_ID_TYPE_NAME */

    default:
        break;
    } /* end switch */

    /*************************************************************************
     * Step 8. Verify the signature of the OCSP response.
     *************************************************************************/
    if ((ret = R_OCSP_RESP_verify(ocsp_resp,
        public_key, &is_verified)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out,"R_OCSP_RESP_verify failure\n");
        goto end;
    }

    BIO_printf(bio_out,"Signature of OCSP response ");
    if (is_verified)
    {
        BIO_printf(bio_out,"verifies.\n");
    }
    else
    {
        BIO_printf(bio_out,"does not verify.\n");
    }


end:
    /* Display the help menu if an invalid command line option was entered. */
    if (print_usage)
    {
        for (pp = ocsp_resp_find_key_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_out, *pp);
        }
    }

    /*************************************************************************
     * Step 9. Clean up.
     * Report errors using both the error number and the string
     * representation. Destroy the dynamically allocated
     * objects, and return an exit code.
     *************************************************************************/

    if ((ret != R_ERROR_NONE) && (bio_out != NULL))
    {
        BIO_printf(bio_out, "ERROR: (%d) %s\n", ret,
            R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret));
    }
    if (public_key != NULL)
    {
        R_PKEY_free(public_key);
    }
    if (responder_name != NULL)
    {
        R_CERT_NAME_free(responder_name);
    }
    if (current_name != NULL)
    {
        R_CERT_NAME_free(current_name);
    }
    if (name_string != NULL)
    {
        Free(name_string);
    }
    if (resp_data != NULL)
    {
        Free(resp_data);
    }
    if (extension != NULL)
    {
        R_EXT_free(extension);
    }
    if (ocsp_resp_entry != NULL)
    {
        R_OCSP_RESP_ENTRY_free(ocsp_resp_entry);
    }
    if (ocsp_resp != NULL)
    {
        R_OCSP_RESP_free(ocsp_resp);
    }
    if (ocsp_ctx != NULL)
    {
        R_OCSP_CTX_free(ocsp_ctx);
    }
    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }
    return(R_ERROR_EXIT_CODE(ret));
}

Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 072-001001-2100-001-000 - 2.1