RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

verisign.c

RA to CA CRS PKI sample: An RA on behalf of an end-entity requests a certificate from the VeriSign Onsite Server.

/* $Id: verisign.c,v 1.5 2004/03/02 05:18:39 gsingh Exp $ */
/* verisign.c
** Copyright (c) 1999-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 example has an RA communicating with the CA (OnSite) on behalf of an
** end-entity in order to get an immediate response.  Customers using the
** APIs/SPI in this example must already have access to an OnSite CA, or made
** arrangements with Verisign to access a CRS responder.  More information is
** available at http://onsite.verisign.com  or in the CRS Profile
** Specification document available from Verisign. 
**
** 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 "filelog.h"
#include "rsacsp.h"
#include "imdb.h"
#include "crlstat.h"
#include "pkixpath.h"
#include "crs.h"
#include "demoutil.h"
#include "pkiutil.h"
#include "certutil.h"
#include "crlutil.h"
#include "nameutil.h"
#include "keyutil.h"
#include "p12util.h"

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

/*  Used to locate configuration information  */
#define CRS_PROV_CFG_URL "file:

/*  Name of PKI service provider  */
#define PKI_SP_NAME "Verisign OnSite Transaction Provider"

/*  Name of database associated with RA.  This is where the RA PKCS #12 info
 *  will be placed in InitializePkiProtectInfo.
 */
#define RA_DB_NAME "RA Database"

#define SP_COUNT 5

/*  Obtain requestor subject name and keypair.  The calling application must
 *  destroy the requestor, publicKey, and privateKey objects.
 */
static int GetRequestorInfo (CERTC_CTX ctx, NAME_OBJ *requestor,
                             B_KEY_OBJ *publicKey, B_KEY_OBJ *privateKey);

/*  For this example, we have the RA sending messages to the CA in order to
 *  get an immediate response.  Therefore, we use this procedure to associate
 *  an "RA Database" with the given ctx, containing the credentials necessary
 *  to send a message to the CA, identifying the sender as an RA to which an
 *  immediate approval is possible.  If an end-entity communicates directly
 *  with the CA, the end-entity will have to wait until the request has been
 *  approved.  (Currently, the human RA would go to a web interface to approve
 *  the end-entity request)
 *
 *  This example prompts the user for trusted root certificates and the PKCS
 *  #12 message containing the private key (alternatively, this can be
 *  modified to instead ask for the B_KEY_OBJ and CERT_OBJ if desired).  Since
 *  this PKCS #12 information will be used to send the message to the CA, a
 *  SIGNER_INFO structure with the RA's information is also created for use in
 *  the PKI_MSG_FIELDS.sender field.
 *
 *  Calling function must free sender->signerCertId.id.issuerSerialNumber.
 *  serialNumber.data.
 *
 *  DestroyPkiProtectInfo must be called when pkiProtectInfo is no longer
 *  needed.
 */
static int InitializePkiProtectInfo (CERTC_CTX ctx,
                                     PKI_PROTECT_INFO *pkiProtectInfo,
                                     SIGNER_INFO *sender);

static void DestroyPkiProtectInfo (PKI_PROTECT_INFO *pkiProtectInfo);

/*  Collect name-value pairs specified by the RA for certificate enrollment.
 *  The Verisign documentation (CRS Profile Specification) specifies the info
 *  needed.
 */
static int SetRegInfo (ATTRIBUTES_OBJ regInfo);

int main (int argc, char *argv[])
{
  int status = 0;
  CERTC_CTX ctx = NULL;
  
  PKI_MSG_OBJ certReq = NULL, certResp = NULL;
  PKI_MSG_FIELDS msgFields;
  PKI_CERTREQ_FIELDS creqFields;
  PKI_CERTRESP_FIELDS certRespFields;
  
  B_KEY_OBJ publicKey = NULL, privateKey = NULL;
  ITEM publicBer = {NULL, 0}, reqBin = {NULL, 0}, respBin = {NULL, 0};
  ITEM certDer = {NULL, 0};
  PKI_PROTECT_INFO pkiProtectInfo = {{NULL}};
  
  SERVICE_HANDLER spTable[SP_COUNT] = {
    {SPT_CRYPTO, "Default CSP", S_InitializeDefaultCSP},
    {SPT_CERT_STATUS, "Cert Status Provider", S_InitializeCRLStatus},
    {SPT_CERT_PATH, "Path Provider", S_InitializePKIXPath},
    {SPT_PKI, PKI_SP_NAME, S_InitializeCRS},
    {SPT_DATABASE, RA_DB_NAME, S_InitializeMemoryDB}
  };
  POINTER spParams[SP_COUNT];

  PKI_CRS_INIT_PARAMS crsInitParams = {(unsigned char *)CRS_PROV_CFG_URL};  

  FILE_LOG_PARAMS logParams = {NULL, NULL};
  SERVICE_HANDLER logHandler = {
    SPT_LOG, "Default File Log", S_InitializeFileLog
  };

  spParams[0] = NULL;
  spParams[1] = NULL;
  spParams[2] = NULL;
  spParams[3] = (POINTER)&crsInitParams;
  spParams[4] = NULL;

  /* To guarantee unused fields are set to 0, and to facilitate cleanup */
  T_memset ((POINTER)&creqFields, 0, sizeof (PKI_CERTREQ_FIELDS));

  /* Dynamically-allocated memory will be placed here later.  For defensive
   * programming purposes, we set it to NULL so that if an error occurs before
   * it is set, we will not free random memory.
   */
  msgFields.sender.signerCertId.id.issuerSerialNumber.serialNumber.data = NULL;

  status = RSA_SetOptions (&logParams, argc, argv);
  if (status != 0)
    goto CLEANUP;
  
  RSA_PrintMessage ("CRS Provider Demonstration\n");
  RSA_PrintMessage ("==========================\n");

  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_CreatePKIMsgObject (ctx, &certReq);
  if (status != 0)
    goto CLEANUP;

  status = C_GetPKIMsgFields (certReq, &msgFields);
  if (status != 0)
    goto CLEANUP;

  /* just to be absolutely sure... */
  IgnoreAll (msgFields.flags);

  msgFields.msgType = PKI_MSGTYPE_CERTREQ;
  ClearIgnoreFlag (msgFields.flags, PKI_MSGFLAGS_IGNORE_MSGTYPE);

  SetMsgWrapType (msgFields.flags, PKI_MSGFLAGS_WRAP_SIGN);

  status = InitializePkiProtectInfo (ctx, &pkiProtectInfo,
                                     &msgFields.sender);
  if (status != 0)
    goto CLEANUP;

  ClearIgnoreFlag (msgFields.flags, PKI_MSGFLAGS_IGNORE_SENDER);

  status = C_SetPKIMsgFields (certReq, &msgFields);
  if (status != 0)
    goto CLEANUP;

  status = C_GetPKICertRequestFields (certReq, &creqFields);
  if (status != 0)
    goto CLEANUP;

  IgnoreAll (creqFields.flags);
  
  /* For our RA-signed request, we only need provide a subject name and
   * public key of the end-entity.
   */
  status = GetRequestorInfo (ctx, &creqFields.certTemplate.subjectName,
                             &publicKey, &privateKey);
  if (status != 0)
    goto CLEANUP;

  ClearIgnoreFlag (creqFields.flags,
                   PKI_CERTREQFLAGS_IGNORE_TEMPLATE_SUBJECTNAME);

  status = RSA_GetKeyBer (RSA_DEMO_PUBLIC_KEY, publicKey, &publicBer);
  if (status != 0)
    goto CLEANUP;

  creqFields.certTemplate.publicKey.data = publicBer.data;
  creqFields.certTemplate.publicKey.len = publicBer.len;
  ClearIgnoreFlag (creqFields.flags,
                   PKI_CERTREQFLAGS_IGNORE_TEMPLATE_PUBLICKEY);

  RSA_PrintMessage ("Supply regInfo for certificate enrollment.  You ");
  RSA_PrintMessage ("will have to refer\nto the Verisign CRS Profile ");
  RSA_PrintMessage ("Specification - for example, section 3.1.1\nwhere ");
  RSA_PrintMessage ("name-value pairs are given so an example name ");
  RSA_PrintMessage ("would be corp_company\nand value might be Acme Inc.");
  RSA_PrintMessage ("  This information is filled in by the RA.\n");

  status = C_CreateAttributesObject (&creqFields.regInfo);
  if (status != 0)
    goto CLEANUP;

  status = SetRegInfo (creqFields.regInfo);
  if (status != 0)
    goto CLEANUP;

  ClearIgnoreFlag (creqFields.flags, PKI_CERTREQFLAGS_IGNORE_REGINFO);

  status = C_SetPKICertRequestFields (certReq, &creqFields);
  if (status != 0)
    goto CLEANUP;

  status = C_GeneratePKIProofOfPossession (ctx, PKI_SP_NAME, certReq,
                                           privateKey, NULL);
  if (status != 0)
    goto CLEANUP;

  status = C_CreatePKIMsgObject (ctx, &certResp);
  if (status != 0)
    goto CLEANUP;

  status = C_RequestPKICert (ctx, PKI_SP_NAME, certReq, &pkiProtectInfo,
                             NULL, certResp);
  if (status != 0)
    goto CLEANUP;

  status = C_GetPKICertResponseFields (certResp, &certRespFields);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Certificate Request Status\n");
  status = RSA_PrintPkiStatusInfo (&certRespFields.statusInfo);
  if (status != 0)
    goto CLEANUP;

  if (certRespFields.flags & PKI_CERTRESPFLAGS_IGNORE_CERT) {
    RSA_PrintMessage ("Certificate not returned.\n");
    status = RSA_DEMO_E_INVALID_PARAMETER;
    goto CLEANUP;
  }

  RSA_PrintMessage ("Resulting Certificate\n");
  status = RSA_PrintCertObject (certRespFields.cert);

  status = C_GetCertDER (certRespFields.cert, &certDer.data, &certDer.len);
  if (status != 0)
    goto CLEANUP;

  status = RSA_WriteDataToFile (certDer.data, certDer.len,
                                "Enter name of file to store cert binary");

CLEANUP:
  if (status != 0)
    RSA_PrintError ("verisign.c", status);

  T_free (publicBer.data);
  T_free (reqBin.data);
  T_free (respBin.data);
  T_free (msgFields.sender.signerCertId.id.issuerSerialNumber.serialNumber.
          data);

  DestroyPkiProtectInfo (&pkiProtectInfo);
  B_DestroyKeyObject (&publicKey);
  B_DestroyKeyObject (&privateKey);
  C_DestroyAttributesObject (&creqFields.regInfo);
  C_DestroyPKIMsgObject (&certReq);
  C_DestroyPKIMsgObject (&certResp);
  C_FinalizeCertC (&ctx);
  
  return status;
} /* end main */

static int GetRequestorInfo (CERTC_CTX ctx, NAME_OBJ *requestor,
                             B_KEY_OBJ *publicKey, B_KEY_OBJ *privateKey)
{
  int status = 0;
  ITEM publicBer = {NULL, 0}, privateBer = {NULL, 0};

  *publicKey = NULL;
  *privateKey = NULL;

  status = C_CreateNameObject (requestor);
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetNameObject (*requestor, "requestor subject");
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Supply the requestor's keypair\n");

  /* Prompt the user for the BER-encoded public key.  If the user enters a
   * blank, a new keypair will be generated on the fly and publicBer will be
   * {NULL, 0}.  Otherwise, if the user supplies a public key, a private key
   * must also be supplied.
   */
  status = RSA_GetFileToAllocBuffer
           (&publicBer.data, &publicBer.len,
            "Enter name of file containing public key BER (blank to create)");
  if (status == RSA_DEMO_E_CANCEL)
    status = RSA_GenerateKeypair (ctx, publicKey, privateKey);
  else if (status == 0) 
    /* user supplied public key, must supply private key also */
    status = RSA_GetFileToAllocBuffer
             (&privateBer.data, &privateBer.len,
            "Enter name of file containing private key BER (blank to cancel)");
  if (status != 0) /* if one of the previous calls failed */
    goto CLEANUP;

  if (publicBer.data != NULL) {
    /* RSA_GenerateKeypair was not called in this case */
    status = B_CreateKeyObject (publicKey);
    if (status != 0)
      goto CLEANUP;

    status = B_CreateKeyObject (privateKey);
    if (status != 0)
      goto CLEANUP;

    status = RSA_SetKeyBer (RSA_DEMO_PUBLIC_KEY, *publicKey, publicBer);
    if (status != 0)
      goto CLEANUP;

    status = RSA_SetKeyBer (RSA_DEMO_PRIVATE_KEY, *privateKey, privateBer);
    if (status != 0)
      goto CLEANUP;
  }

CLEANUP:
  if (status != 0) {
    C_DestroyNameObject (requestor);
    B_DestroyKeyObject (publicKey);
    B_DestroyKeyObject (privateKey);
    RSA_PrintError ("GetRequestorInfo", status);
  }

  if (privateBer.data != NULL) {
    T_memset (privateBer.data, 0, privateBer.len);
    T_free (privateBer.data);
  }

  T_free (publicBer.data);

  return status;
} /* end GetRequestorInfo */

static int InitializePkiProtectInfo (CERTC_CTX ctx,
                                     PKI_PROTECT_INFO *pkiProtectInfo,
                                     SIGNER_INFO *sender)
{
  int status = 0;
  char userInput[RSA_DEMO_MAX_LINE_LEN], password[RSA_DEMO_MAX_LINE_LEN];

  SERVICE db = NULL;
  ITEM passwordItem = {NULL, 0};
  LIST_OBJ trustedRoots = NULL, certList = NULL;

  ITEM raCertBer = {NULL, 0};
  CERT_OBJ raCert = NULL;
  CERT_FIELDS raCertFields;

  NAME_OBJ issuerCopy = NULL;
  ITEM nameBer = {NULL, 0};
  unsigned char *serialNumber = NULL;

  pkiProtectInfo->info.protectionCtx =
    (CERT_PATH_CTX *)T_malloc (sizeof (CERT_PATH_CTX));
  if (pkiProtectInfo->info.protectionCtx == NULL) {
    status = RSA_DEMO_E_ALLOC;
    goto CLEANUP;
  }

  T_memset ((POINTER)pkiProtectInfo->info.protectionCtx,
            0, sizeof (CERT_PATH_CTX));

  status = C_BindService (ctx, SPT_DATABASE, RA_DB_NAME, &db);
  if (status != 0)
    goto CLEANUP;

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

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

  RSA_PrintMessage ("\nSupply trusted root certificates.  Normally, for the ");
  RSA_PrintMessage ("purposes of this example,\nit should only be one ");
  RSA_PrintMessage ("certificate.\n");
  status = RSA_AddCertsToListPrompt (ctx, trustedRoots);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nTrusted Roots\n");
  status = RSA_PrintCertList (trustedRoots);
  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;

  RSA_PrintMessage ("\nOptionally supply additional CRLs for chaining.\n");
  status = RSA_AddCrlsToDbPrompt (ctx, db);
  if (status != 0)
    goto CLEANUP;

  /*  For simplicity, take PKCS #12.  Can use other methods if need be.  */
  status = RSA_GetCommand
           (userInput, sizeof (userInput),
            "\nEnter name of PKCS #12 binary file containing RA private key");
  if (status != 0)
    goto CLEANUP;

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

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

  status = RSA_GetCommand (password, sizeof (password),
                           "Enter password to decrypt private key");
  if (status != 0)
    goto CLEANUP;

  status = RSA_FormatPkcs12Password (password, &passwordItem);
  if (status != 0)
    goto CLEANUP;

  status = C_ImportPKCS12 (ctx, userInput, &passwordItem, db, 0);
  if (status != 0)
    goto CLEANUP;

  pkiProtectInfo->info.protectionCtx->pathAlgorithm = PA_X509_V1;
  pkiProtectInfo->info.protectionCtx->pathOptions = PF_IGNORE_REVOCATION;
  pkiProtectInfo->info.protectionCtx->trustedCerts = trustedRoots;
  pkiProtectInfo->info.protectionCtx->policies = ANY_POLICY;
  pkiProtectInfo->info.protectionCtx->validationTime = 0;
  pkiProtectInfo->info.protectionCtx->database = db;

  status = RSA_GetFileToAllocBuffer
           (&raCertBer.data, &raCertBer.len,
            "Enter name of file containing RA certificate");
  if (status != 0)
    goto CLEANUP;

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

  status = C_SetCertBER (raCert, raCertBer.data, raCertBer.len);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("RA Certificate\n");
  RSA_PrintCertInfo (raCert);

  status = C_GetCertFields (raCert, &raCertFields);
  if (status != 0)
    goto CLEANUP;

  /* make sure the RA cert is in the database */
  status = C_SelectCertByIssuerSerial (db, raCertFields.issuerName,
                                       &raCertFields.serialNumber,
                                       certList);
  if (status != 0)
    goto CLEANUP;

  /* provider documentation says not to set signedAttributes and
   * unsignedAttributes field of SIGNER_INFO.
   */

  sender->signerCertId.type = ISSUER_SERIAL;

  /* make a copy of the RA issuer name */
  status = C_CreateNameObject (&issuerCopy);
  if (status != 0)
    goto CLEANUP;

  status = C_GetNameDER (raCertFields.issuerName, &nameBer.data, &nameBer.len);
  if (status != 0)
    goto CLEANUP;

  status = C_SetNameBER (issuerCopy,
                         nameBer.data, nameBer.len);
  if (status != 0)
    goto CLEANUP;

  sender->signerCertId.id.issuerSerialNumber.issuerName = issuerCopy;
  
  /* make a copy of the RA serial number */
  serialNumber = T_malloc (raCertFields.serialNumber.len);
  if (serialNumber == NULL) {
    status = RSA_DEMO_E_ALLOC;
    goto CLEANUP;
  }

  T_memcpy (serialNumber, raCertFields.serialNumber.data,
            raCertFields.serialNumber.len);

  sender->signerCertId.id.issuerSerialNumber.serialNumber.data =
    serialNumber;
  sender->signerCertId.id.issuerSerialNumber.serialNumber.len =
    raCertFields.serialNumber.len;
  /* hard-coded for simplicity */
  sender->digestAlgorithmId.algorithmId = DAI_MD5;
  sender->digestAlgorithmId.algorithmParam = NULL;
  sender->signatureAlgorithmId.algorithmId = SA_RSA_ENCRYPTION;
  sender->signatureAlgorithmId.algorithmParam = NULL;

CLEANUP:
  if (status != 0) {
    T_free (serialNumber);
    C_DestroyNameObject (&issuerCopy);
    DestroyPkiProtectInfo (pkiProtectInfo);
    RSA_PrintError ("InitializePkiProtectInfo", status);
  }

  C_DestroyListObject (&certList);

  T_memset (passwordItem.data, 0, passwordItem.len);
  T_free (passwordItem.data);

  T_memset ((POINTER)password, 0, sizeof (password));

  T_free (raCertBer.data);
  C_DestroyCertObject (&raCert);

  return status;
}  /* end InitializePkiProtectInfo */

static void DestroyPkiProtectInfo (PKI_PROTECT_INFO *pkiProtectInfo)
{
  if (pkiProtectInfo->info.protectionCtx == NULL)
    return;

  if (pkiProtectInfo->info.protectionCtx->trustedCerts != NULL)
    C_DestroyListObject (&pkiProtectInfo->info.protectionCtx->trustedCerts);

  if (pkiProtectInfo->info.protectionCtx->policies != ANY_POLICY)
    C_DestroyListObject (&pkiProtectInfo->info.protectionCtx->policies);

  if (pkiProtectInfo->info.protectionCtx->database != NULL)
    C_UnbindService (&pkiProtectInfo->info.protectionCtx->database);

  if (pkiProtectInfo->info.protectionCtx != NULL)
    T_free ((POINTER)pkiProtectInfo->info.protectionCtx);

  pkiProtectInfo->info.protectionCtx = NULL;
}  /* end DestroyPkiProtectInfo */

static int SetRegInfo (ATTRIBUTES_OBJ regInfo)
{
  int status = 0;

  RSA_DEMO_REG_INFO_ENTRY regInfoFields[] = {
    {"common_name", NULL, VT_UTF8_STRING},
    {"mail_email", NULL, VT_UTF8_STRING},
    {"mail_firstName", NULL, VT_UTF8_STRING},
    {"mail_lastName", NULL, VT_UTF8_STRING},
    {"corp_company", "RSA Security Inc.", VT_UTF8_STRING},
    {"org_unit", "Public", VT_UTF8_STRING},
    {"cert_type", "end-user", VT_UTF8_STRING},
    {"authenticate", "YES", VT_UTF8_STRING},
    {"embed_email", "yes", VT_UTF8_STRING},
    {"challenge", "password", VT_UTF8_STRING}
  };

  status = RSA_RegInfoPrompt
           (regInfo, regInfoFields,
            sizeof (regInfoFields) / sizeof (RSA_DEMO_REG_INFO_ENTRY));
  if (status != 0)
    RSA_PrintError ("CreateRegInfo", status);

  return status;
}  /* end CreateRegInfo */

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