RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

ocsp.c

Prompts the user to provide a collection of trusted certificates, and other intermediate certificates, which can be used for cert path processing locally. After the cert path is constructed locally, explicitly checks revocation status using OCSP.

/* $Id: ocsp.c,v 1.5 2005/02/25 06:09:30 alockwoo Exp $ */
/* ocsp.c
** Copyright (c) 2001-2003, RSA Security Inc.
**
** This file is used to demonstrate how to interface to an RSA Security
** licensed development product.  You have a royalty-free right to use,
** modify, reproduce and distribute this demonstration file (including
** any modified version), provided that you agree that RSA Security has
** no warranty, implied or otherwise, or liability for this demonstration
** file or any modified version.
**
** This program prompts the user to provide a collection of trusted certs,
** and other intermediate certs, which can be used for cert path processing
** locally.  After the cert path is constructed locally, basically doing
** everything short of revocation checking, then we explicitly check the
** revocation status using OCSP.
**
** For testing unsigned requests, see http://www.valicert.com/ocsp (as one
** example) for more information.
**
** When compiling, define the macro RSA_REQUIRE_FILE_LOG (-D compile
** option, or equivelent) to force the program to return an error code
** if file logging cannot be initialized.  For example, if the file
** containing the log message format strings cannot be located (certc.msg
** or equivalent).
*/

#include "certc.h"
#include "ocsp.h"
#include "imdb.h"
#include "filelog.h"
#include "pkixpath.h"
#include "crlstat.h"
#include "rsacsp.h"
#include "demoutil.h"
#include "certutil.h"
#include "crlutil.h"

#ifdef _MSC_VER
# pragma warning (disable: 171) /* invalid type conversion (often of very similar ptrs) */
#endif

#define SP_COUNT 3
#define IM_DB_NAME "Sample IM Database"

/* Register an OCSP service consisting of one or more OCSP responders with
 * the given Cert-C context.  It also populates the given trustedCerts list
 * with the CA certs using OCSP reponders and the responder certs (the
 * contents of the OCSP_RESPONDER.responderCAs and
 * OCSP_RESPONDER.responderCert fields).  If signed requests are used, the
 * private key and certificate of the signer are added to the given database
 * SERVICE handle.
 */
static int RegisterOcsp (CERTC_CTX ctx, LIST_OBJ trustedCerts, SERVICE db);

/* Using the given Cert-C context and path context, verify that the given
 * certObj is valid.
 */
static int VerifyCert (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx,
                       CERT_OBJ certObj);

/* Using the given Cert-C context and path context, query any cert status
 * providers registered with the Cert-C context for the revocation status
 * of the given certObj.
 */
static int CheckCertStatus (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx,
                            CERT_OBJ certObj);

int main (int argc, char *argv[])
{
  int status = 0;

  CERTC_CTX ctx = NULL;
  SERVICE_HANDLER spTable[SP_COUNT];
  POINTER spParams[SP_COUNT];

  CERT_PATH_CTX pathCtx;
  LIST_OBJ trustedCerts = NULL, certList = NULL;
  SERVICE db = NULL;

  ITEM certToValidateBer = {NULL, 0};
  CERT_OBJ certToValidate = NULL;

  FILE_LOG_PARAMS logParams = {NULL, NULL};
  SERVICE_HANDLER logHandler = {
    SPT_LOG, "Default File Log", S_InitializeFileLog
  };
  
  /* Set up RSA_* demoutil options */
  status = RSA_SetOptions (&logParams, argc, argv);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("OCSP Example\n");
  RSA_PrintMessage ("============\n");
  
  spTable[0].type = SPT_DATABASE;
  spTable[0].name = IM_DB_NAME;
  spTable[0].Initialize = S_InitializeMemoryDB;
  spTable[1].type = SPT_CERT_PATH;
  spTable[1].name = "Cert Path Processing Provider";
  spTable[1].Initialize = S_InitializePKIXPath;
  spTable[2].type = SPT_CRYPTO;
  spTable[2].name = "BSAFE Crypto-C";
  spTable[2].Initialize = S_InitializeDefaultCSP;

  spParams[0] = NULL;
  spParams[1] = NULL;
  spParams[2] = NULL;

  status = C_InitializeCertC (spTable, spParams, SP_COUNT, &ctx);
  if (status != 0)
    goto CLEANUP;

  /* Attempt to initialize file logging, but unless RSA_REQUIRE_FILE_LOG is
   * defined, treat it as a non-fatal condition.
   */
  status = C_RegisterService (ctx, &logHandler, (POINTER)&logParams,
                              SERVICE_ORDER_FIRST);
#ifdef RSA_REQUIRE_FILE_LOG
  if (status != 0)
    goto CLEANUP;
#endif

  status = C_CreateListObject (&trustedCerts);
  if (status != 0)
    goto CLEANUP;

  /* Add roots and intermediate certs to database */
  status = C_BindService (ctx, SPT_DATABASE, IM_DB_NAME, &db);
  if (status != 0)
    goto CLEANUP;

  /* Prompt the user for the OCSP Responder information and register it with
     the Cert-C context. */
  status = RegisterOcsp (ctx, trustedCerts, db);
  if (status != 0)
    goto CLEANUP;

  /* Obtain other intermediate certs */
  status = C_CreateListObject (&certList);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nOptionally supply other relevant certificates, such ");
  RSA_PrintMessage ("as those needed to\nconstruct the certificate chain.\n");
  status = RSA_AddCertsToListPrompt (ctx, certList);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nOther certificates\n");
  status = RSA_PrintCertList (certList);
  if (status != 0)
    goto CLEANUP;

  status = C_InsertCertList (db, trustedCerts);
  if (status != 0)
    goto CLEANUP;

  status = C_InsertCertList (db, certList);
  if (status != 0)
    goto CLEANUP;
 
  /* The use of PF_IGNORE_REVOCATION here simply says that OCSP should not
     be used when C_BuildCertPath is called.  We will explicitly use an OCSP
     request later to check the cert status. */
  pathCtx.pathAlgorithm = PA_PKIX;
  pathCtx.pathOptions = PF_IGNORE_REVOCATION;
  pathCtx.trustedCerts = trustedCerts;
  pathCtx.policies = ANY_POLICY;
  pathCtx.validationTime = 0;
  pathCtx.database = db;

  for (;;) {
    /* Obtain cert to chain to a trusted root */
    status = RSA_GetFileToAllocBuffer
             (&certToValidateBer.data, &certToValidateBer.len,
              "Enter name of file containing cert to validate (blank to end)");
    if (status == RSA_DEMO_E_CANCEL)
      break;
    else if (status != 0)
      goto CLEANUP;

    status = C_CreateCertObject (&certToValidate, ctx);
    if (status != 0)
      goto CLEANUP;

    status = C_SetCertBER (certToValidate, certToValidateBer.data,
                           certToValidateBer.len);
    if (status != 0)
      goto CLEANUP;

    /* Check the validity locally first... */
    status = VerifyCert (ctx, &pathCtx, certToValidate);
    if (status != 0) {
      RSA_PrintMessage ("Cert validation failed when using PA_PKIX.\n");
      RSA_PrintMessage ("Trying PA_X509_V1...\n");
    
      pathCtx.pathAlgorithm = PA_X509_V1;

      status = VerifyCert (ctx, &pathCtx, certToValidate);
      if (status != 0)
        goto CLEANUP;
    }

    RSA_PrintMessage ("\nCert validated locally.\n");

    /* Now do the time-consuming OCSP request... */
    RSA_PrintMessage ("Checking revocation status...\n");
    status = CheckCertStatus (ctx, &pathCtx, certToValidate);
    if (status != 0)
      goto CLEANUP;

    T_free (certToValidateBer.data);
    certToValidateBer.data = NULL;

    C_DestroyCertObject (&certToValidate);
  }

CLEANUP:
  if (status != 0)
    RSA_PrintError ("ocsp", status);

  T_free (certToValidateBer.data);

  C_DestroyListObject (&trustedCerts);
  C_DestroyListObject (&certList);
  C_DestroyCertObject (&certToValidate);
  C_UnbindService (&db);
  C_FinalizeCertC (&ctx);

  return status;
}  /* end main */

static int RegisterOcsp (CERTC_CTX ctx, LIST_OBJ trustedCerts, SERVICE db)
{
  int status = 0;
  unsigned int i = 0;
  char userInput[RSA_DEMO_MAX_LINE_LEN];

  LIST_OBJ destList = NULL;
  ITEM ocspUrl = {NULL, 0};

  REVOKE_OCSP_INIT_PARAMS ocspInitParams;
  OCSP_RESPONDER *responderList = NULL, *currentResponder = NULL;
  CERT_OBJ responderCert = NULL;
  ITEM responderCertBer = {NULL, 0};

  ALGORITHM_IDENTIFIER sigAlgId = {0, NULL};
  CERT_OBJ signerCert = NULL;
  B_KEY_OBJ signerPvtKey = NULL;
  LIST_OBJ extraCerts = NULL;
  POINTER ptr;

  SERVICE_HANDLER ocspHandler = {
    SPT_CERT_STATUS, "Cert Revocation Status Provider", S_InitializeOCSP
  };

  /* Start with a clean slate... */
  T_memset ((POINTER)&ocspInitParams, 0, sizeof (ocspInitParams));

  status = C_CreateListObject (&destList);
  if (status != 0)
    goto CLEANUP;

  /* Supply information about OCSP responder */
  status = RSA_GetCommand (userInput, sizeof (userInput),
                           "Enter OCSP responder URL");
  if (status != 0)
    goto CLEANUP;

  /* if we arrive here, we want to add a responder */
  ocspInitParams.initChoice = REVOKE_OCSP_INIT_METHOD_STRUCT;
  ocspInitParams.method.initStruct.numResponders++;

  ptr = T_realloc ((POINTER)responderList,
                     ocspInitParams.method.initStruct.numResponders *
                     sizeof (OCSP_RESPONDER));
  if (ptr == NULL_PTR) {
    status = RSA_DEMO_E_ALLOC;
    goto CLEANUP;
  }
  responderList = (OCSP_RESPONDER *) ptr;

  currentResponder = responderList + 
                     (ocspInitParams.method.initStruct.numResponders - 1);
  T_memset ((POINTER)currentResponder, 0, sizeof (OCSP_RESPONDER));

  ocspInitParams.method.initStruct.responders = responderList;
  
  ocspUrl.data = (POINTER)userInput;
  ocspUrl.len = T_strlen (userInput);

  status = C_AddItemToList (destList, &ocspUrl, NULL);
  if (status != 0)
    goto CLEANUP;

  /* Optionally obtain the responder's certificate.  This cert is used to
     validate signatures on responses from the responder. */
  status = RSA_GetFileToAllocBuffer
             (&responderCertBer.data, &responderCertBer.len,
              "Optionally supply responder certificate binary");
  if (status == 0) {
    status = C_CreateCertObject (&responderCert, ctx);
    if (status != 0)
      goto CLEANUP;

    status = C_SetCertBER (responderCert, responderCertBer.data,
                           responderCertBer.len);
    if (status != 0)
      goto CLEANUP;

    status = C_AddCertToList (trustedCerts, responderCert, NULL);
    if (status != 0)
      goto CLEANUP;
  } else if (status == RSA_DEMO_E_CANCEL) {
    /* responderCert remains NULL */
    status = 0;
  } else
    goto CLEANUP;

  /* Obtain the CA certificates affiliated with this responder */
  RSA_PrintMessage ("\nSupply trusted CA certificates which use %s\n",
                    ocspUrl.data);
  RSA_PrintMessage ("as the OCSP responder.\n");
  status = RSA_AddCertsToListPrompt (ctx, trustedCerts);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nTrusted Certs\n");
  status = RSA_PrintCertList (trustedCerts);
  if (status != 0)
    goto CLEANUP;

  /* Supply information used to sign requests sent to OCSP server.
     This information is server-specific. */
  currentResponder->profile = REVOKE_OCSP_PROFILE_GENERIC;
  currentResponder->flags = 0;  /* use default */
  currentResponder->transport.destList = destList;
  currentResponder->transport.proxyList = NULL;
  currentResponder->digestAlgorithm = DAI_SHA1;
  currentResponder->responderCert = responderCert;
  currentResponder->responderCAs = trustedCerts;
  currentResponder->timeTolerance = 0;
  currentResponder->extraRequestExtensions = NULL;
  currentResponder->dbName = IM_DB_NAME;

  /* Check if requests to this responder should be signed */
  RSA_PrintMessage ("\nSpecify signature algorithm if requests should be");
  RSA_PrintMessage (" signed.\nEnter blank for unsigned requests.\n");
  status = RSA_ChooseSignatureAlgorithmPrompt (&sigAlgId);
  if (status != 0 && status != RSA_DEMO_E_CANCEL)
    goto CLEANUP;
  else if (status == RSA_DEMO_E_CANCEL) { /* unsigned request */
    currentResponder->signer.signatureAlgorithm = SA_UNDEFINED;
    currentResponder->signer.cert = NULL;
    currentResponder->signer.extraRequestCerts = NULL;
  } else { /* signature algorithm specified */
    currentResponder->signer.signatureAlgorithm = sigAlgId.algorithmId;

    status = B_CreateKeyObject (&signerPvtKey);
    if (status != 0)
      goto CLEANUP;

    status = C_CreateCertObject (&signerCert, ctx);
    if (status != 0)
      goto CLEANUP;

    RSA_PrintMessage ("Supply cert and private key to use to sign OCSP ");
    RSA_PrintMessage ("requests.\n");
    status = RSA_GetCertAndPvtKey (ctx, signerCert, signerPvtKey);
    if (status != 0)
      goto CLEANUP;

    status = C_InsertCert (db, signerCert);
    if (status != 0)
      goto CLEANUP;

    status = C_InsertPrivateKey (db, signerCert, signerPvtKey);
    if (status != 0)
      goto CLEANUP;

    currentResponder->signer.cert = signerCert;

    status = C_CreateListObject (&extraCerts);
    if (status != 0)
      goto CLEANUP;

    RSA_PrintMessage ("Optionally supply additional certs to send to ");
    RSA_PrintMessage ("the responder.\n");
    status = RSA_AddCertsToListPrompt (ctx, extraCerts);
    if (status != 0)
      goto CLEANUP;

    currentResponder->signer.extraRequestCerts = extraCerts;
  }

  status = C_RegisterService (ctx, &ocspHandler, (POINTER)&ocspInitParams,
                              SERVICE_ORDER_FIRST);

CLEANUP:
  if (status != 0)
    RSA_PrintError ("RegisterOcsp", status);

  B_DestroyKeyObject (&signerPvtKey);
  C_DestroyListObject (&extraCerts);
  C_DestroyListObject (&destList);

  /* each responder will either have a responderCert and signer CERT_OBJ
     which we created or NULL */
  for (i = 0; i < ocspInitParams.method.initStruct.numResponders; i++) {
    C_DestroyCertObject (&responderList[i].responderCert);
    C_DestroyCertObject (&responderList[i].signer.cert);
  }

  T_free ((POINTER)responderList);

  return status;
}  /* end RegisterOcsp */

static int VerifyCert (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx,
                       CERT_OBJ certObj)
{
  int status = 0;
  LIST_OBJ certPath = NULL, crlList = NULL, crlCerts = NULL;

  status = C_CreateListObject (&certPath);
  if (status != 0)
    goto CLEANUP;

  status = C_CreateListObject (&crlList);
  if (status != 0)
    goto CLEANUP;

  status = C_CreateListObject (&crlCerts);
  if (status != 0)
    goto CLEANUP;

  status = C_BuildCertPath (ctx, pathCtx, (POINTER)certObj,
                            certPath, crlList, crlCerts, NULL);
  if (status != 0)
    goto CLEANUP;

  /* Print out information about validated cert */
  RSA_PrintMessage ("\nChain of certificates to trusted root:\n");
  status = RSA_PrintCertList (certPath);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nCRLs needed to verify chain:\n");
  status = RSA_PrintCrlList (crlList);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nAdditional certs needed to validate CRLs:\n");
  status = RSA_PrintCertList (crlCerts);
  if (status != 0)
    goto CLEANUP;

CLEANUP:
  if (status != 0)
    RSA_PrintError ("VerifyCert", status);

  C_DestroyListObject (&certPath);
  C_DestroyListObject (&crlList);
  C_DestroyListObject (&crlCerts);

  return status;
} /* end VerifyCert */

static int CheckCertStatus (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx,
                            CERT_OBJ certObj)
{
  int status = 0;
  CERT_REVOCATION revStatus;

  status = C_CheckCertRevocation (ctx, pathCtx, certObj, &revStatus);
  if (status != 0)
    goto CLEANUP;

  switch (revStatus.status) {
    case CERT_REVOKED:
      RSA_PrintMessage ("Cert Revoked!\n");
      break;
    case CERT_NOT_REVOKED:
      RSA_PrintMessage ("Cert Valid!\n");
      break;
    case CERT_REVOCATION_UNKNOWN:
      RSA_PrintMessage ("Revocation unknown.\n");
      break;
    default:
      RSA_PrintMessage ("Invalid status value = %d\n", revStatus.status);
  }

CLEANUP:
  if (status != 0)
    RSA_PrintError ("CheckCertStatus", status);

  if (revStatus.evidenceType == CRE_OCSP)
    C_DestroyOCSPEvidence ((OCSP_EVIDENCE **)&revStatus.evidence);
  else if (revStatus.evidenceType == CRE_CRL)
    C_DestroyCRLEvidence ((CRL_EVIDENCE **)&revStatus.evidence);

  return status;
} /* end CheckCertStatus */

Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 067-001001-2720-001-000 - 2.7.2