RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

cmpku.c

Sends a key update request to a CMP server. Contains option for key archival.

/* $Id: cmpku.c,v 1.4 2004/03/02 05:18:35 gsingh Exp $ */
/* cmpku.c
** Copyright (c) 2002-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 can be used to send a key update request to a CMP server.
** The key update request should be for a valid, non-revoked certificate.
** If the cert given for key update is not valid or revoked, the server
** should return an error.  This sample also contains an option to allow
** for key archival, provided that it is supported by the server.
**
** 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 "rsacsp.h"
#include "imdb.h"
#include "cmp.h"
#include "pkixpath.h"
#include "filelog.h"
#include "demoutil.h"
#include "pkiutil.h"
#include "certutil.h"
#include "keyutil.h"
#include "nameutil.h"

/*  The number of service providers registered when the context is
 *  initialized.
 */
#define SP_COUNT 3

#define DEFAULT_CMP_URL "cmptcp://tr4.rsa.com:829"
#define CMP_PROVIDER_NAME "RSA CMP Provider"
#define EE_DB_NAME "Requestor's Database"

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

  CERTC_CTX ctx = NULL;
  SERVICE_HANDLER spTable[SP_COUNT] = {
    {SPT_CRYPTO, "Default CSP", S_InitializeDefaultCSP},
    {SPT_DATABASE, EE_DB_NAME, S_InitializeMemoryDB},
    {SPT_CERT_PATH, "Cert Path Processing Provider", S_InitializePKIXPath}
  };
  
  POINTER spParams[SP_COUNT] = {0};

  SERVICE db = (SERVICE)NULL_PTR;

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

  PKI_CMP_SP_INIT_PARAMS cmpInitParams = {0};
  SERVICE_HANDLER cmpHandler = {
    SPT_PKI, CMP_PROVIDER_NAME, S_InitializeCMP
  };

  PKI_MSG_OBJ pkiMsgObj = (PKI_MSG_OBJ)NULL_PTR;

  PKI_PROTECT_INFO protectInfo = {0};
  CERT_PATH_CTX senderCertCtx = {0};
  LIST_OBJ trustedCerts = (LIST_OBJ)NULL_PTR;
  CERT_OBJ senderCert = (CERT_OBJ)NULL_PTR;
  CERT_FIELDS senderCertFields;
  B_KEY_OBJ senderPvtKey = (B_KEY_OBJ)NULL_PTR;
  
  PKI_SENDER_INFO senderInfo = {0};
  GENERAL_NAME senderGeneralName = {0};

  CERT_OBJ recipientCert = (CERT_OBJ)NULL_PTR;
  CERT_FIELDS recipientCertFields;
  ITEM recipientCertBer = {0};

  PKI_RECIPIENT_INFO recipientInfo = {0};
  GENERAL_NAME recipientGeneralName = {0};

  PKI_KEY_UPDATE_REQ_OBJ keyUpdateReqObj = (PKI_KEY_UPDATE_REQ_OBJ)NULL_PTR;
  PKI_CERT_TEMPLATE_OBJ certTemplateObj = (PKI_CERT_TEMPLATE_OBJ)NULL_PTR;

  B_KEY_OBJ publicKey = (B_KEY_OBJ)NULL_PTR, privateKey = (B_KEY_OBJ)NULL_PTR;
  ITEM publicKeyBer = {0};

  ATTRIBUTES_OBJ controls = (ATTRIBUTES_OBJ)NULL_PTR;

  unsigned int reqIndex;

  PKI_POP_GEN_INFO popGenInfo = {0};

  PKI_MSG_OBJ pkiMsgRespObj = (PKI_MSG_OBJ)NULL_PTR;

  status = RSA_SetOptions (&logParams, argc, argv);
  if (status != 0)
    goto CLEANUP;

  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 failure to register 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
  
  RSA_PrintMessage ("CMP Key Update Request Example\n");
  RSA_PrintMessage ("==============================\n");

  /* Step 1: Register CMP Provider */
  cmpInitParams.initChoice = PKI_CMP_INIT_METHOD_STRUCT;
  
  status = RSA_CreateTransportInfoFieldsPrompt
             (&cmpInitParams.method.initStruct.transport, DEFAULT_CMP_URL);
  if (status != 0)
    goto CLEANUP;

  status = RSA_CmpProfilePrompt (&cmpInitParams.method.initStruct.profile);
  if (status != 0)
    goto CLEANUP;

  status = C_RegisterService (ctx, &cmpHandler, (POINTER)&cmpInitParams,
                              SERVICE_ORDER_LAST);
  if (status != 0)
    goto CLEANUP;

  /* Step 2: Create PKI message object */
  status = C_CreatePKIMsgObject (ctx, &pkiMsgObj);
  if (status != 0)
    goto CLEANUP;

  status = C_SetPKIMsgType (pkiMsgObj, PKI_MSGTYPE_KEY_UPDATE_REQ);
  if (status != 0)
    goto CLEANUP;

  /* Set Message Protection:
   * This code could be modified to use an alternate protection type.
   * The protectInfo will be used when we call C_RequestPKIMsg.
   * The sender's CERT_PATH_CTX contains the cert to be updated, with the
   * existing private key, and here we set it to be explicitly trusted.
   * Since we are using a CERT_PATH_CTX, we need to register a provider
   * of type SPT_CERT_PATH with the current CERTC_CTX.  That was done
   * in this sample during the call to C_InitializeCertC.
   */
  status = C_BindService (ctx, SPT_DATABASE, EE_DB_NAME, &db);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Supply existing cert and private key.\n");
  RSA_PrintMessage ("Do not generate a new cert unless you are doing ");
  RSA_PrintMessage ("negative testing.\n");

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

  status = B_CreateKeyObject (&senderPvtKey);
  if (status != 0)
    goto CLEANUP;
  
  status = RSA_GetCertAndPvtKey (ctx, senderCert, senderPvtKey);
  if (status != 0)
    goto CLEANUP;

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

  status = C_AddCertToList (trustedCerts, senderCert,
                            (unsigned int *)NULL_PTR);
  if (status != 0)
    goto CLEANUP;

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

  status = C_InsertPrivateKey (db, senderCert, senderPvtKey);
  if (status != 0)
    goto CLEANUP;

  /* Because we have set PKI_MSG_PROTECTION_SIGN, note that we must add the
     CMP Signer's cert (CA cert) to the senderDb and a valid chain must exist
     from the CMP Signer's cert to a cert in the trustedCerts list. */
  status = C_SetPKIMsgProtectionType (pkiMsgObj, PKI_MSG_PROTECTION_SIGN);
  if (status != 0)
    goto CLEANUP;

  /* Set Sender:
   * If your server uses an alternate method of identifiying the requestor,
   * modify this code to popluate the PKI_SENDER_INFO with the appropriate
   * information.  Here we use the sender certificate (and matching private
   * key) that was just supplied above.
   */
  status = C_GetCertFields (senderCert, &senderCertFields);
  if (status != 0)
    goto CLEANUP;
  
  /* PKI_ENTITY_ISSUER_SERIAL is currently not supported by the CMP provider.
     as a workaround, use PKI_ENTITY_GENERALNAME_KEYID */
#if 0
  senderInfo.senderId.type = PKI_ENTITY_ISSUER_SERIAL;

  senderInfo.senderId.id.issuerSerialNumber.issuerName =
    senderCertFields.issuerName;
  senderInfo.senderId.id.issuerSerialNumber.serialNumber.data =
    senderCertFields.serialNumber.data;
  senderInfo.senderId.id.issuerSerialNumber.serialNumber.len =
    senderCertFields.serialNumber.len;
#else
  senderGeneralName.altNameType = CN_DIRECTORY_NAME;
  senderGeneralName.altName.directoryName = senderCertFields.subjectName;

  senderInfo.senderId.type = PKI_ENTITY_GENERALNAME_KEYID;
  senderInfo.senderId.id.generalNameKeyId.name = &senderGeneralName;
  senderInfo.senderId.id.generalNameKeyId.keyId.data = NULL_PTR;
  senderInfo.senderId.id.generalNameKeyId.keyId.len = 0;
#endif

  /* For simplicity, we set RSA with SHA1 as the signature algorithm.
     If desired, modify to use menuutil functions to prompt user. */
  senderInfo.digestAlgorithmId.algorithmId = DAI_SHA1;
  senderInfo.digestAlgorithmId.algorithmParam = NULL_PTR;
  senderInfo.signatureAlgorithmId.algorithmId = SA_RSA_ENCRYPTION;
  senderInfo.signatureAlgorithmId.algorithmParam = NULL_PTR;

  status = C_SetPKIMsgSender (pkiMsgObj, &senderInfo);
  if (status != 0)
    goto CLEANUP;

  /* Set the Recipient DN, identifying the CA.  If a cert is available,
   * extract the subject name from the CA cert.  Otherwise, prompt the user
   * to supply the DN in a name object.
   */
  RSA_PrintMessage ("Supply the CA certificate to identify the CA as the ");
  RSA_PrintMessage ("recipient.  This cert\nwill be used to envelope a ");
  RSA_PrintMessage ("message for the CA if you are using key archival\nand ");
  RSA_PrintMessage ("will be used to verify signed messages from the CA.\n");
  status = RSA_GetFileToAllocBuffer
             (&recipientCertBer.data, &recipientCertBer.len,
              "Enter name of file containing recipient cert");
  if (status != 0)
    goto CLEANUP;

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

  status = C_SetCertBER (recipientCert, recipientCertBer.data,
                         recipientCertBer.len);
  if (status != 0)
    goto CLEANUP;

  status = C_GetCertFields (recipientCert, &recipientCertFields);
  if (status != 0)
    goto CLEANUP;

  recipientGeneralName.altNameType = CN_DIRECTORY_NAME;
  recipientGeneralName.altName.directoryName = recipientCertFields.subjectName;

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

  /* In addition, we'll just implicitly trust this cert */
  status = C_AddCertToList (trustedCerts, recipientCert,
                            (unsigned int *)NULL_PTR);
  if (status != 0)
    goto CLEANUP;

  recipientInfo.type = PKI_RECIPIENT_GENERALNAME_KEYID;
  recipientInfo.info.generalNameKeyId.name = &recipientGeneralName;

  status = C_SetPKIMsgRecipient (pkiMsgObj, &recipientInfo);
  if (status != 0)
    goto CLEANUP;

  /* Step 3: Create Key Update Request Object */
  status = C_CreatePKIKeyUpdateReqObject (ctx, &keyUpdateReqObj);
  if (status != 0)
    goto CLEANUP;

  /* The private key will be used later to generate the proof of possession */
  RSA_PrintMessage ("Supply replacement keypair for certification.\n");
  status = RSA_GetKeypair (ctx, &publicKey, &privateKey);
  if (status != 0)
    goto CLEANUP;

  /* In this example, we fill in the cert template with the information from
     the existing certificate.  Modify this code if the requirements of your
     server are different. */
  status = C_CreatePKICertTemplateObject (ctx, &certTemplateObj);
  if (status != 0)
    goto CLEANUP;

  status = C_GetPKICertTemplateFromCertObject (senderCert, certTemplateObj);
  if (status != 0)
    goto CLEANUP;

  /* replace the public key in the cert template with the new one */
  status = RSA_GetKeyBer (RSA_DEMO_PUBLIC_KEY, publicKey, &publicKeyBer);
  if (status != 0)
    goto CLEANUP;

  status = C_SetCertTemplatePublicKey (certTemplateObj, &publicKeyBer);
  if (status != 0)
    goto CLEANUP;

  status = C_SetPKICertReqCertTemplate (keyUpdateReqObj, certTemplateObj);
  if (status != 0)
    goto CLEANUP;

  /* Specify an action for C_GeneratePKIMsgProofOfPossession */
  status = C_SetPKICertReqPOPType (keyUpdateReqObj, PKI_POP_SIGNATURE);
  if (status != 0)
    goto CLEANUP;

  /* Since we are replacing an existing certificate, we need to supply the
     oldCertId control, as specified in section 6.5 of RFC 2511 */
  status = C_CreateAttributesObject (&controls);
  if (status != 0)
    goto CLEANUP;

  status = RSA_SetOldCertIdControl (ctx, senderCert, controls);
  if (status != 0)
    goto CLEANUP;

  /* Here, we give the user the option to perform key archival */
  status = RSA_GetCommand (userInput, sizeof (userInput),
                           "Perform key archival (y/n)?");
  if (status != 0)
    goto CLEANUP;

  if (userInput[0] == 'y') {
    PKI_ARCHIVE_OPTION archiveOption = {0};

    archiveOption.choice = ARCHIVE_ENCRYPTED_KEY;
    archiveOption.option.encryptedKey.keyWrapChoice = ENCRYPTED_VALUE;
    archiveOption.option.encryptedKey.pRecipient = &recipientInfo;
    archiveOption.option.encryptedKey.privateKey = privateKey;

    /* The contentEncryptionAlgorithmId field of the recipientInfo was not
       set previously.  This demo could be modified to use
       RSA_ChooseSymEncAlgPrompt in the menuutil.h file, but for now, we do
       3DES for simplicity. */
    recipientInfo.contentEncryptionAlgorithmId.algorithmId = EAI_DES3;
    recipientInfo.contentEncryptionAlgorithmId.algorithmParam = NULL_PTR;

    status = S_AddPKIArchiveOptions (ctx, &archiveOption, controls);
    if (status != 0)
      goto CLEANUP;
  } else
    RSA_PrintMessage ("No key archival.\n");

  status = C_SetPKICertReqControls (keyUpdateReqObj, controls);
  if (status != 0)
    goto CLEANUP;

  /* Step 4: Add cert request to PKI message object */
  status = C_AddPKIMsg (pkiMsgObj, (POINTER)keyUpdateReqObj, &reqIndex);
  if (status != 0)
    goto CLEANUP;

  /* Step 5: Generate Proof of Possession of private key */
  /* See the CMP specification for information on "Proof of Possession (POP)
   * of Private Key".  Here, we indicate that our proof of possession will be
   * the requestor's ability to decrypt the issued cert in the confirmation
   * message.  This is done to avoid having to send the private key to the CA.
   */
  popGenInfo.method = PKI_POP_METHOD_ENCRYPT_CERT;

  status = C_GeneratePKIMsgProofOfPossession (ctx, CMP_PROVIDER_NAME,
                                              pkiMsgObj, reqIndex, privateKey,
                                              &popGenInfo);
  if (status != 0)
    goto CLEANUP;

  /* Step 6: Send Request Message */
  status = C_CreatePKIMsgObject (ctx, &pkiMsgRespObj);
  if (status != 0)
    goto CLEANUP;

  senderCertCtx.pathAlgorithm = PA_X509_V1;
  senderCertCtx.pathOptions = PF_IGNORE_REVOCATION;
  senderCertCtx.trustedCerts = trustedCerts;
  senderCertCtx.policies = ANY_POLICY;
  senderCertCtx.validationTime = PF_VALIDATION_TIME_NOW;
  senderCertCtx.database = db;
  protectInfo.info.protectionCtx = &senderCertCtx;

  RSA_PrintMessage ("Sending Key Update Request...\n");
  status = C_RequestPKIMsg (ctx, CMP_PROVIDER_NAME, pkiMsgObj, &protectInfo,
                            db, pkiMsgRespObj);
  if (status != 0)
    goto CLEANUP;

  /* Add our new private key into the database */
  status = C_InsertPrivateKeyBySPKI (db, &publicKeyBer, privateKey);
  if (status != 0)
    goto CLEANUP;

  /* Step 7: Process Response Message */
  status = RSA_ProcessPkiMsgResp (ctx, pkiMsgRespObj, pkiMsgObj, RSA_DEMO_CMP,
                                  CMP_PROVIDER_NAME, &protectInfo, db);
  if (status != 0)
    goto CLEANUP;

CLEANUP:
  if (status != 0)
    RSA_PrintError ("cmpku.c", status);
  else
    RSA_PrintMessage ("Done!\n");

  RSA_DestroyTransportInfoFields (&cmpInitParams.method.initStruct.transport);

  T_free (publicKeyBer.data);

  B_DestroyKeyObject (&publicKey);
  B_DestroyKeyObject (&privateKey);
  B_DestroyKeyObject (&senderPvtKey);

  C_DestroyAttributesObject (&controls);
  C_DestroyListObject (&trustedCerts);
  C_DestroyCertObject (&senderCert);
  C_DestroyPKICertTemplateObject (&certTemplateObj);
  C_DestroyPKIKeyUpdateReqObject (&keyUpdateReqObj);
  C_DestroyPKIMsgObject (&pkiMsgObj);
  C_DestroyPKIMsgObject (&pkiMsgRespObj);
  C_UnbindService (&db);
  C_FinalizeCertC (&ctx);

  return status;
}  /* end main */

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