RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

ocsp_resp_vfy.c

/* $Id: ocsp_resp_vfy.c,v 1.1.2.19 2005/11/09 07:18:18 clindgre Exp $ */
/*
 * Copyright (C) 1998-2003 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
 * This sample demonstrates operations with OCSP response messages.
 *
 * For example, to:
 *
 * Verify the signature with the issuer certificate and print the message:
 *   ocsp_resp_vfy -resp response.data -issuer ca_cert.data -print
 *
 * Verify the signature of the OCSP response and verify the validity of the
 * certificate :
 *   ocsp_resp_vfy -resp response.data -req request.data -cert cert.data
 *  -issuer ca_cert.data
 *
 * Verify the signature of the OCSP response with the responder's certificate
 * and verify the validity of the certificate at the specified time :
 *   ocsp_resp_vfy -resp response.data -req request.data -cert cert.data
 *  -issuer ca_cert.data -resp_cert resp_cert.data -time 20050101000000Z
 *
 * where: response.data  = The OCSP response file.
 *        ca_cert.data   = The signer's certificate.
 *        request.data   = The OCSP request file.
 *        cert.data      = The certificate to verify.
 *        resp_cert.data = The OCSP resonder's certificate.
 *
 */

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

int ocsp_response_verify_signature(R_LIB_CTX *lib_ctx, R_OCSP_RESP *resp,
    R_CERT *cert, BIO *bio_out);
int ocsp_response_verify_nonce(R_LIB_CTX *lib_ctx, R_OCSP_REQ *req,
    R_OCSP_RESP *resp, BIO *bio_out);
int ocsp_response_revoked_check(R_LIB_CTX *lib_ctx,
    R_OCSP_RESP_ENTRY *resp_entry, char *vfy_time_str, BIO *bio_out);
int ocsp_response_verify_certificate(R_LIB_CTX *lib_ctx, R_OCSP_CTX *ctx,
    R_OCSP_RESP *resp, R_CERT *cert, R_CERT *issuer, char *vfy_time_str,
    BIO *bio_out);

/* Usage help message. */
static char *ocsp_resp_vfy_usage[] =
{
    "usage: ocsp_resp_vfy [options]\n",
    "where options are:\n",
    " -resp file            - The file containing the OCSP response message\n",
    " -req file             - The file containing the OCSP request message\n",
    " -cert file            - A file containing a certificate to check\n",
    " -issuer file          - A file containing the issuer certificate\n",
    " -resp_cert file       - A file containing the responder's certificate\n",
    " -cert_type encoding   - Encoding the certificates - only X509\n",
    "                         (default) supported\n",
#ifdef NO_PEM
    " -cert_form format     - The format of the certificates (BIN only)\n",
#else
    " -cert_form format     - The format of the certificates - one of BIN "
    "(default),\n",
    "                         PEM\n",
    " -time YYYYMMDDHHMMSSZ - Set the verification time rather than use the\n",
    "                         system time\n",
#endif /* NO_PEM */
    " -help                 - Print this usage message.\n",
    " -eg                   - Print some example usages.\n",
    NULL
};

/* Examples of usage. */
static char *ocsp_resp_vfy_example_usage[] =
{
   "Verify the signature with the issuer certificate and print the message:\n",
   "  ocsp_resp_vfy -resp response.data -issuer ca_cert.data -print\n",
   "\n",
   "Verify the signature of the OCSP response and verify the validity of the\n",
   "certificate :\n",
   "  ocsp_resp_vfy -resp response.data -req request.data -cert cert.data\n",
   " -issuer ca_cert.data\n",
   "\n",
   "Verify the signature of the OCSP response with the responder's "
   "certificate\n",
   "and verify the validity of the certificate at the specified time :\n",
   "  ocsp_resp_vfy -resp response.data -req request.data -cert cert.data\n",
   " -issuer ca_cert.data -resp_cert resp_cert.data -time 20050101000000Z\n",
   "\n",
   "where: response.data  = The OCSP response file.\n",
   "       ca_cert.data   = The signer's certificate.\n",
   "       request.data   = The OCSP request file.\n",
   "       cert.data      = The certificate to verify.\n",
   "       resp_cert.data = The OCSP resonder's certificate.\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       *ctx       = NULL;
    R_OCSP_RESP      *resp      = NULL;
    R_OCSP_REQ       *req       = NULL;
    R_CERT_CTX       *cert_ctx  = NULL;
    R_CERT           *cert      = NULL;
    R_CERT           *issuer    = NULL;
    R_CERT           *resp_cert = NULL;
    BIO              *bio_out   = NULL;
    char             *resp_file;
    char             *req_file;
    char             *cert_file;
    char             *issuer_file;
    char             *resp_cert_file;
    char             *cert_type;
    char             *cert_form;
    unsigned char    *resp_data = NULL;
    unsigned int      resp_len;
    unsigned char    *req_data  = NULL;
    unsigned int      req_len;
    unsigned int      num_used;
    int               print_msg;
    char             *vfy_time  = NULL;
    R_OID             signing;
    R_OID            *oid;


    /* Set the defaults */
    resp_file      = NULL;
    req_file       = NULL;
    cert_file      = NULL;
    issuer_file    = NULL;
    resp_cert_file = NULL;
    cert_type      = "X509";
    cert_form      = "BIN";
    print_msg      = 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)
            {
                goto bad;
            }
            resp_file = *(++argv);
        }
        /* The OCSP request message file. */
        else if (Strcmp(*argv, "-req") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            req_file = *(++argv);
        }
        /* The certificate file. */
        else if (Strcmp(*argv, "-cert") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            cert_file = *(++argv);
        }
        /* The issuer certificate file. */
        else if (Strcmp(*argv, "-issuer") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            issuer_file = *(++argv);
        }
        /* The responder certificate file. */
        else if (Strcmp(*argv, "-resp_cert") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            resp_cert_file = *(++argv);
        }
        /* The certificate type. */
        else if (Strcmp(*argv, "-cert_type") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            cert_type = *(++argv);
        }
        /* The certificate format. */
        else if (Strcmp(*argv, "-cert_form") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            cert_form = *(++argv);
        }
        /* Print out the details of the message. */
        else if (Strcmp(*argv, "-print") == 0)
        {
            print_msg = 1;
        }
        else if (Strcmp(*argv, "-time") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            vfy_time = *(++argv);
        }
        /* Display the usage information. */
        else if (Strcmp(*argv, "-help") == 0)
        {
            goto bad;
        }
        /* Display examples of usage information. */
        else if (Strcmp(*argv,"-eg") == 0)
        {
            char **egp;
            for (egp = ocsp_resp_vfy_example_usage; (*egp) != NULL; egp++)
            {
                BIO_printf(bio_out, *egp);
            }
            goto end;
        }
        /* Unknown option. */
        else
        {
            BIO_printf(bio_out, "Unknown option %s\n", *argv);
            goto bad;
        }
        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");
        goto bad;
    }
    if (issuer_file == NULL)
    {
        BIO_printf(bio_out, "An issuer certificate file is required\n");
        goto bad;
    }
    if ((req_file != NULL) && (cert_file == NULL))
    {
        BIO_printf(bio_out, "A certificate file to verify is required\n");
        goto bad;
    }

    /* If you entered an invalid command line option, display the help menu. */
    if (0)
    {
        char **pp;
bad:
        for (pp = ocsp_resp_vfy_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_out, *pp);
        }
        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. Load the certificates.
     * The load function creates a certificate context and reads the
     * certificate from the file.
     * When the responer has not specified a certificate (that is when the
     * OCSP signing is not delegated), the responder's certificate is the
     * issuer certificate.
     *************************************************************************/
    /* Load the certificate that is being checked. */
    if ((ret = load_certificate(bio_out, lib_ctx, cert_file, cert_type,
        cert_form, &cert_ctx, &cert)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Certificate being checked failed\n");
        goto end;
    }

    /* Load the issuer certificate. */
    if ((ret = load_certificate(bio_out, lib_ctx, issuer_file, cert_type,
        cert_form, &cert_ctx, &issuer)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Issuer certificate failed\n");
        goto end;
    }

    /* If the responder certificate is specified, load it. */
    if (resp_cert_file == NULL)
    {
        resp_cert = issuer;
    }
    else
    {
        if ((ret = load_certificate(bio_out, lib_ctx, resp_cert_file, cert_type,
            cert_form, &cert_ctx, &resp_cert)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Responder certificate failed\n");
            goto end;
        }

        /* Ensure that the OCSP signing extended key usage extension is
        * present. */
        if (!R_CERT_is_info_present(resp_cert, R_CERT_INFO_EXTENDED_KEY_USAGE))
        {
            BIO_printf(bio_out,
                "No OCSP signing delegation extension present\n");
            ret = R_ERROR_FAILED;
            goto end;
        }

        /* Create an OID object containing the OCSP signing OID. */
        if ((ret = R_OID_init_with_constant(&signing, R_OID_OCSP_SIGNING)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Unable to initialize R_OID structure\n");
            goto end;
        }
        oid = &signing;

        /* Check for the OCSP signing extended key usage. */
        if (!R_CERT_test_extended_key_usage(resp_cert, &oid, 1))
        {
            BIO_printf(bio_out,
                "No OCSP signing delegation extension present\n");
            ret = R_ERROR_FAILED;
            goto end;
        }
    }

    /*************************************************************************
     * Step 3. 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, &ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_OCSP_CTX_new failure\n");
        goto end;
    }

    /*************************************************************************
     * Step 4. Create an OCSP response object from the response file.
     * Load the data from the file. Use the binary data to create an
     * OCSP response object with the OCSP context.
     *************************************************************************/
    /*
     * Read the data from 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;
    }

    /*
     * Load the OCSP message buffer into an OCSP response message object.
     */
    if ((ret = R_OCSP_RESP_from_binary(ctx, R_FLAG_SHARE_DATA,
        resp_len, resp_data, &num_used, &resp)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_OCSP_RESP_from_binary failure\n");
        goto end;
    }

    /*************************************************************************
     * Step 5. Create an OCSP request object from the request file.
     * Load the data from the file. Use the binary data to create an
     * OCSP request object with the OCSP context.
     *************************************************************************/
    if (req_file != NULL)
    {
        /*
         * Read the data from the OCSP request message file into the buffer.
         * The OCSP request message is in binary form, and copied to a
         * buffer pointed to by req_data.
         */
        if ((ret = data_from_file(bio_out, req_file, &req_data, &req_len)) !=
            R_ERROR_NONE)
        {
            goto end;
        }

        /*
         * Load the OCSP message buffer into an OCSP request message object.
         */
        if ((ret = R_OCSP_REQ_from_binary(ctx, R_FLAG_SHARE_DATA,
            req_len, req_data, &num_used, &req)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "R_OCSP_REQ_from_binary failure\n");
            goto end;
        }
    }

    /*************************************************************************
     * Step 6. If requested, print the OCSP response message.
     *************************************************************************/
    if (print_msg)
    {
        if ((ret = R_OCSP_RESP_write(resp, bio_out, R_FORMAT_TEXT, NULL)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Failed to write OCSP response message\n");
            goto end;
        }
    }

    /*************************************************************************
     * Step 7. Verify the OCSP response message with the responder's
     * certificate.
     *************************************************************************/
    if ((ret = ocsp_response_verify_signature(lib_ctx, resp, resp_cert,
        bio_out)) != R_ERROR_NONE)
    {
        goto end;
    }

    /*************************************************************************
     * Step 8. Verify that the nonce from the request matches the nonce in
     * the response.
     * If a nonce was specified in the request, the same nonce must appear
     * in the response.
     *************************************************************************/
    /* Check that there is a request to extract a nonce from. */
    if (req != NULL)
    {
        if ((ret = ocsp_response_verify_nonce(lib_ctx, req, resp, bio_out)) !=
            R_ERROR_NONE)
        {
            goto end;
        }
    }

    /*************************************************************************
     * Step 9. Verify the certificate with the OCSP response message.
     *************************************************************************/
    if ((ret = ocsp_response_verify_certificate(lib_ctx, ctx, resp, cert,
        issuer, vfy_time, bio_out)) != R_ERROR_NONE)
    {
        goto end;
    }

end:

    /*************************************************************************
     * Step 10. Clean up.
     * If there is an output stream, report errors using both the error 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 (req_data != NULL)
    {
        Free(req_data);
    }
    if (resp_data != NULL)
    {
        Free(resp_data);
    }
    if (req != NULL)
    {
        R_OCSP_REQ_free(req);
    }
    if (resp != NULL)
    {
        R_OCSP_RESP_free(resp);
    }
    if (ctx != NULL)
    {
        R_OCSP_CTX_free(ctx);
    }
    if ((resp_cert != issuer) && (resp_cert != NULL))
    {
        R_CERT_free(resp_cert);
    }
    if (issuer != NULL)
    {
        R_CERT_free(issuer);
    }
    if (cert != NULL)
    {
        R_CERT_free(cert);
    }
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }
    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }

    return(R_ERROR_EXIT_CODE(ret));
}

/*
 * Verify the signature in the OCSP response with the responder's certificate.
 * If a key usage is specified, ensure that the certificate can be used for
 * digital signatures.
 *
 * @param   lib_ctx  [In]  The library context.
 * @param   resp     [In]  The OCSP response.
 * @param   cert     [In]  The responder's response.
 * @param   bio_out  [In]  The I/O stream to write information to.
 * @return  #R_ERROR_NONE indicates success.<br>
 *          See @ref R_ERROR_IDS for valid values.
 */
int ocsp_response_verify_signature(R_LIB_CTX *lib_ctx, R_OCSP_RESP *resp,
    R_CERT *cert, BIO *bio_out)
{
    int ret;
    R_PKEY *pkey = NULL;
    int is_verified;
    R_EXT *no_check_ext = NULL;
    int ext_nid = R_EXT_ID_OCSP_NOCHECK;


    /*************************************************************************
     * Step 1. Check that the certificate can be used for digital signatures.
     * If there is a key usage, check it for the digital signatures bit.
     *************************************************************************/
     if (R_CERT_is_info_present(cert, R_CERT_INFO_KEY_USAGE))
     {
        if (!R_CERT_test_key_usage(cert, R_CERT_KEY_USAGE_DIGITAL_SIGNATURE))
        {
            BIO_printf(bio_out,
                "The key is not to be used for digital signatures\n");
            ret = R_ERROR_FAILED;
            goto end;
        }
    }

    /*************************************************************************
     * Step 2. Check that the certificate is valid.
     * Check for the extension indicating that there is no need to verify
     * the validity. If no such extension is present, perform checks as per
     * the local security policy (for example the CRL validation).
     *************************************************************************/
    /* Create an extension. */
    if ((ret = R_EXT_new(lib_ctx, R_RES_FLAG_DEF, &no_check_ext)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to create an extension\n");
        goto end;
    }

    /* Set the OCSP No Check numeric identifier in the extension object. */
    if ((ret = R_EXT_set_info(no_check_ext, R_EXT_INFO_ID, &ext_nid)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to set the OID into the extension\n");
        goto end;
    }

    /* Attempt to get the OCSP No Check extension from the certificate.
     * If the return value is R_ERROR_NONE then you do not need to perform
     * further checks.
     */
    ret = R_CERT_get_info(cert, R_CERT_INFO_EXTENSION_BY_OID, no_check_ext);
    if (ret == R_ERROR_NOT_FOUND)
    {
        /* Perform checks as per the local security policy. */
    }
    else if (ret != R_ERROR_NONE)
    {
        BIO_printf(bio_out,
            "Unable to get an extension from the certificate\n");
        goto end;
    }

    /*************************************************************************
     * Step 3. Obtain the public key from the certificate.
     *************************************************************************/
    if ((ret = R_CERT_public_key_to_R_PKEY(cert, R_FLAG_SHARE_DATA, &pkey)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to retrieve public key from certificate\n");
        goto end;
    }

    /*************************************************************************
     * Step 4. Verify the signature.
     * If the verification process fails, calling verify will return an error.
     * The is_verified parameter contains the return code that indicates
     * whether the public key verified the signature in the OCSP response
     * message.
     *************************************************************************/
    if ((ret = R_OCSP_RESP_verify(resp, pkey, &is_verified)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Signature verification process failed\n");
        goto end;
    }
    /*
     * Check that the signature verified.
     */
    if (!is_verified)
    {
        BIO_printf(bio_out, "Signature failed verification\n");
        ret = R_ERROR_FAILED;
        goto end;
    }
    BIO_printf(bio_out, "Signature verified!\n");

end:
    /*************************************************************************
     * Step 5. Clean up.
     *************************************************************************/
    if (pkey != NULL)
    {
        R_PKEY_free(pkey);
    }
    if (no_check_ext != NULL)
    {
        R_EXT_free(no_check_ext);
    }

    return(ret);
}


/*
 * Ensure that if there is a nonce in the request that there is a nonce in the
 * response and that the nonce in the response has the same data as the nonce
 * in the request.
 *
 * @param   lib_ctx  [In]  The library context.
 * @param   req      [In]  The OCSP request.
 * @param   resp     [In]  The OCSP response.
 * @param   bio_out  [In]  The I/O stream to write information to.
 * @return  #R_ERROR_NONE indicates success.<br>
 *          See @ref R_ERROR_IDS for valid values.
 */
int ocsp_response_verify_nonce(R_LIB_CTX *lib_ctx, R_OCSP_REQ *req,
    R_OCSP_RESP *resp, BIO *bio_out)
{
    int ret = R_ERROR_NONE;
    R_ITEM nonce_req;
    R_ITEM nonce_resp;

    /*************************************************************************
     * Step 1. Get the nonce from the request.
     * If the request does not contain a nonce, the call will return
     * R_ERROR_NOT_FOUND. In this case there is no data to verify.
     * If the call returns any other error value (other than R_ERROR_NONE)
     * then there is something wrong with the request message.
     * If no error value is returned, the request nonce was found.
     *************************************************************************/
    ret = R_OCSP_REQ_get_info(req, R_OCSP_REQ_INFO_NONCE, &nonce_req);
    if (ret == R_ERROR_NOT_FOUND)
    {
        /* There is no nonce in the request, and therefore no data with
         * which to verify.
         */
        ret = R_ERROR_NONE;
        goto end;
    }
    if (ret != R_ERROR_NONE)
    {
        /* An error occurred when extracting the nonce. */
        BIO_printf(bio_out, "Unable to extract nonce from OCSP request\n");
        goto end;
    }

    /*************************************************************************
     * Step 2. Get the nonce from the response.
     * If the response does not contain a nonce, the call will return
     * R_ERROR_NOT_FOUND. In this case the response is missing the
     * required nonce.
     * If the call returns any other error value (other than R_ERROR_NONE)
     * then there is something wrong with the response message.
     * If no error value is returned, the response nonce was found.
     *************************************************************************/
    ret = R_OCSP_RESP_get_info(resp, R_OCSP_RESP_INFO_NONCE, &nonce_resp);
    if (ret == R_ERROR_NOT_FOUND)
    {
        /* No nonce was found. The nonce should have been present. */
        BIO_printf(bio_out, "Nonce missing from response\n");
        goto end;
    }
    else if (ret != R_ERROR_NONE)
    {
        /* An error occurred when extracting the nonce. */
        BIO_printf(bio_out, "Unable to extract nonce from OCSP response\n");
        goto end;
    }

    /*************************************************************************
     * Step 3. Compare the nonces from the request and response.
     * The data of the nonces in the request and response must be identical
     * for the verification process to pass.
     *************************************************************************/
    if ((nonce_req.len != nonce_resp.len) ||
        (Memcmp(nonce_req.data, nonce_resp.data, nonce_req.len) != 0))
    {
        /* Nonces do not match - error. */
        BIO_printf(bio_out,
            "Nonce from request does not match nonce in response\n");
        ret = R_ERROR_FAILED;
    }

end:
    return(ret);
}

/*
 * Verify the certificate from the response.
 * The status is checked; and if the certificate is revoked, the time
 * is checked.
 *
 * @param   lib_ctx       [In]  The library context.
 * @param   ctx           [In]  The OCSP context.
 * @param   resp          [In]  The OCSP response.
 * @param   cert          [In]  The certificate to be checked.
 * @param   issuer        [In]  The issuer certificate for the certificate to be
 *                              checked.
 * @param   vfy_time_str  [In]  The time of verification of the certificate.
 * @param   bio_out       [In]  The I/O stream to write information to.
 * @return  #R_ERROR_NONE indicates success.<br>
 *          See @ref R_ERROR_IDS for valid values.
 */
int ocsp_response_verify_certificate(R_LIB_CTX *lib_ctx, R_OCSP_CTX *ctx,
    R_OCSP_RESP *resp, R_CERT *cert, R_CERT *issuer, char *vfy_time_str,
    BIO *bio_out)
{
    int ret;
    R_OCSP_RESP_ENTRY *resp_entry = NULL;
    R_OCSP_CERT_STATUS status;

    /*************************************************************************
     * Step 1. Get the entry for the certificate from the response.
     *************************************************************************/
    /* Create a response entry. */
    if ((ret = R_OCSP_RESP_ENTRY_new(ctx, R_RES_FLAG_DEF,
        &resp_entry)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to create response entry\n");
        goto end;
    }

    /* Get the OCSP response entry for the certificate. */
    if ((ret = R_OCSP_RESP_find_entry_by_cert(resp, cert, NULL, resp_entry)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "No entry in response for certificate\n");
        goto end;
    }

    /*************************************************************************
     * Step 2. Determine the status of the certificate.
     * Check the revocation time to verify whether the certificate was
     * revoked at the verification time.
     *************************************************************************/
    /* Get the status from the response entry. */
    if ((ret = R_OCSP_RESP_ENTRY_get_info(resp_entry,
        R_OCSP_RESP_ENTRY_INFO_CERT_STATUS, &status)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to get certificate status\n");
        goto end;
    }

    /* Check the status. */
    if (status == R_OCSP_CERT_STATUS_GOOD)
    {
        BIO_printf(bio_out, "Certificate status: Valid!\n");
    }
    else if (status == R_OCSP_CERT_STATUS_UNKNOWN)
    {
        BIO_printf(bio_out, "Certificate status: Not Known\n");
    }
    else
    {
        BIO_printf(bio_out, "Certificate status: Revoked\n");

        ret = ocsp_response_revoked_check(lib_ctx, resp_entry, vfy_time_str,
            bio_out);
    }

end:
    /*************************************************************************
     * Step 3. Clean up.
     *************************************************************************/
    if (resp_entry != NULL)
    {
        R_OCSP_RESP_ENTRY_free(resp_entry);
    }

    return(ret);
}

/*
 * Check the time of revocation for the certificate from the OCSP response
 * entry.
 *
 * @param   lib_ctx       [In]  The library context.
 * @param   resp_entry    [In]  The OCSP response entry.
 * @param   vfy_time_str  [In]  The time of verification of the certificate.
 * @param   bio_out       [In]  The I/O stream to write information to.
 * @return  #R_ERROR_NONE indicates success.<br>
 *          See @ref R_ERROR_IDS for valid values.
 */
int ocsp_response_revoked_check(R_LIB_CTX *lib_ctx,
    R_OCSP_RESP_ENTRY *resp_entry, char *vfy_time_str, BIO *bio_out)
{
    int ret;
    R_OCSP_REVOCATION_REASON reason;
    R_TIME_CTX *time_ctx = NULL;
    R_TIME *rev_time = NULL;
    R_TIME *vfy_time = NULL;
    int time_cmp;
    char time_str[R_TIME_SHRF_SIZE];
    unsigned int len;

    /*************************************************************************
     * Step 1. Retrieve the revocation reason (if available).
     *************************************************************************/
    ret = R_OCSP_RESP_ENTRY_get_info(resp_entry,
        R_OCSP_RESP_ENTRY_INFO_REVOCATION_REASON, &reason);
    if (ret != R_ERROR_NOT_FOUND)
    {
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Unable to get revocation reason\n");
            goto end;
        }
        else
        {
            BIO_printf(bio_out, "Reason for revocation: %d\n", reason);
        }
    }

    /*************************************************************************
     * Step 2. Create a time context with which to create time objects.
     *************************************************************************/
    if ((ret = R_TIME_CTX_new(lib_ctx, R_RES_FLAG_DEF, &time_ctx)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to create a time context\n");
        goto end;
    }

    /*************************************************************************
     * Step 3. Check the revocation time to determine whether the certificate
     * has been revoked.
     * Retrieve the revocation into a time object and display the time.
     *************************************************************************/
    /* Create a new time object for the revocation time. */
    if ((ret = R_TIME_new(time_ctx, &rev_time)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to create a new time object\n");
        goto end;
    }

    /* Get the revocation time. */
    if ((ret = R_OCSP_RESP_ENTRY_get_info(resp_entry,
        R_OCSP_RESP_ENTRY_INFO_REVOCATION_TIME, rev_time)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to get revocation time\n");
        goto end;
    }

    /* Convert the time to a string. */
    if ((ret = R_TIME_export(rev_time, R_TIME_EXTERNAL_FORMAT_SHRF,
        (unsigned char *)time_str, &len, sizeof(time_str))) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to convert revocation time to string\n");
        goto end;
    }
    BIO_printf(bio_out, "Revocation time: %s\n", time_str);

    /*************************************************************************
     * Step 4. Create a time object with the verification time.
     * If a time was not specified, use the current time.
     * Display the verification time.
     *************************************************************************/
    /* Create a new time object for the verification time. */
    if ((ret = R_TIME_new(time_ctx, &vfy_time)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to create a new time object\n");
        goto end;
    }

    /* If no verification time string is specified, take the current time. */
    if (vfy_time_str == NULL)
    {
        if ((ret = R_TIME_time(vfy_time)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Unable to deteremint the current time\n");
            goto end;
        }
    }
    /* Import the verification time string. */
    else
    {
        if ((ret = R_TIME_import(vfy_time, R_TIME_EXTERNAL_FORMAT_GT,
            (unsigned char *)vfy_time_str, Strlen(vfy_time_str))) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Unable to import time string\n");
            goto end;
        }
    }

    /* Convert the time to a string. */
    if ((ret = R_TIME_export(vfy_time, R_TIME_EXTERNAL_FORMAT_SHRF,
        (unsigned char *)time_str, &len, sizeof(time_str))) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to convert verification time to string\n");
        goto end;
    }
    BIO_printf(bio_out, "Verification time: %s\n", time_str);


    /*************************************************************************
     * Step 5. Compare the revocation time with the time at which the
     * verification is taking place.
     *************************************************************************/
    if ((ret = R_TIME_cmp(rev_time, vfy_time, &time_cmp)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Unable to compare with the revocation time\n");
        goto end;
    }
    if (time_cmp > 0)
    {
        BIO_printf(bio_out, "Certificate is not revoked yet\n");
    }
    else
    {
        BIO_printf(bio_out, "Certificate is revoked\n");
    }

end:
    /*************************************************************************
     * Step 6. Clean up.
     *************************************************************************/
    if (vfy_time != NULL)
    {
        R_TIME_free(vfy_time);
    }
    if (rev_time != NULL)
    {
        R_TIME_free(rev_time);
    }
    if (time_ctx != NULL)
    {
        R_TIME_CTX_free(time_ctx);
    }

    return(ret);
}


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