RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

pkcs12exp.c

Creates a PKCS #12 message to export a password-protected private key and its associated certificates and CRLs.

/* $Id: pkcs12exp.c,v 1.4 2004/03/02 05:18:41 gsingh Exp $ */
/* pkcs12exp.c
** Copyright (c) 2000-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.
**
** Demonstrate how to use Cert-C to create a PKCS #12 message in order
** to export a password-protected private key and its associated certificates
** and CRLs.
**
** 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 "pkixpath.h"
#include "crlstat.h"
#include "fileio.h"
#include "demoutil.h"   /* demo code */
#include "p12util.h"    /* demo code */
#include "certutil.h"   /* demo code */
#include "crlutil.h"    /* demo code */

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

/*  This routine uses C_ExportPKCS12 to export a private key and corresponding
 *  certificate chain.
 */
static int ExportKeyAndCert (CERTC_CTX ctx);

/*  This routine uses C_WriteToPKCS12 to export some arbitrary private keys,
 *  certificates, and CRLs.
 */
static int GeneratePKCS12 (CERTC_CTX ctx);

/*  The number of service providers used in this example.  Here, we must
 *  register a crypto service provider.
 */
#define SP_COUNT 5
#define DATABASE_NAME "PKCS #12 export database"
#define STREAM_SP_NAME "Sample File I/O"

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

  CERTC_CTX ctx = NULL;
  SERVICE_HANDLER spTable[SP_COUNT] = {
    {SPT_CRYPTO, "Crypto Provider", S_InitializeDefaultCSP},
    {SPT_DATABASE, DATABASE_NAME, S_InitializeMemoryDB},
    {SPT_CERT_PATH, "Cert Path Processing Provider", S_InitializePKIXPath},
    {SPT_CERT_STATUS, "Revocation Status Provider", S_InitializeCRLStatus},
    {SPT_IO, STREAM_SP_NAME, S_InitializeFileIO}
  };
  POINTER spParams[SP_COUNT] = {
    NULL, NULL, NULL, NULL, NULL
  };

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

  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 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
  
  RSA_PrintMessage ("PKCS #12 Export Example\n");
  RSA_PrintMessage ("=======================\n");

  for (;;) {
    RSA_PrintMessage ("\nPKCS #12 Export Operations\n");
    RSA_PrintMessage ("  A - Export a private key and certificate chain\n");
    RSA_PrintMessage ("  B - Create an arbitrary PKCS #12 message\n");

    status = RSA_GetCommand (command, sizeof (command),
                             "Enter choice (blank to quit)");
    if (status != 0)
      goto CLEANUP;

    switch (command[0]) {
      case 'a':
      case 'A':
        status = ExportKeyAndCert (ctx);
        break;
      case 'b':
      case 'B':
        status = GeneratePKCS12 (ctx);
        break;
      case '\0':
      case 'q':
      case 'Q':
        goto CLEANUP;
      default:
        RSA_PrintMessage ("Unrecognized Option: %c\n", command[0]);
        status = RSA_DEMO_E_INVALID_PARAMETER;
    }

    if (status != 0)
      RSA_PrintMessage ("Operation not completed.\n");
    else
      RSA_PrintMessage ("Operation successful!\n");
  }
    
CLEANUP:
  if (status != 0)
    RSA_PrintError ("pkcs12exp.c", status);

  C_FinalizeCertC (&ctx);

  return status;
}  /* end main */

/*  This routine uses C_ExportPKCS12 to export a private key and corresponding
 *  certificate chain.
 */
static int ExportKeyAndCert (CERTC_CTX ctx)
{
  int status = 0;
  char userInput[RSA_DEMO_MAX_LINE_LEN];
  char filename[RSA_DEMO_MAX_LINE_LEN];

  CERT_OBJ certObj = NULL;
  CERT_FIELDS certFields;
  NAME_OBJ subjectName = NULL;

  B_KEY_OBJ privateKey = NULL;
  LIST_OBJ trustedCerts = NULL, crlList = NULL;

  unsigned int numCrls = 0;
  SERVICE database = NULL;
  CERT_PATH_CTX pathCtx;

  int encryptionAlg = 0, macDigest = 0;
  int iterationCount = 0, exportOptions = 0;
  ITEM macPassword = {NULL, 0}, encPassword = {NULL, 0};

  T_memset ((POINTER)&pathCtx, 0, sizeof (pathCtx));

  RSA_PrintMessage ("Supply the keypair to enclose in the PKCS #12 message ");
  RSA_PrintMessage ("in the form of a\nbinary BER-encoded X.509 certificate");
  RSA_PrintMessage (" and a binary PKCS #8 PrivateKeyInfo.\n");

  status = C_BindService (ctx, SPT_DATABASE, DATABASE_NAME, &database);
  if (status != 0)
    goto CLEANUP;

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

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

  /* Collect private key and corresponding certificate for PKCS #12 message */
  status = RSA_GetCertAndPvtKey (ctx, certObj, privateKey);
  if (status != 0)
    goto CLEANUP;

  status = C_InsertCert (database, certObj);
  if (status != 0)
    goto CLEANUP;

  status = C_InsertPrivateKey (database, certObj, privateKey);
  if (status != 0)
    goto CLEANUP;

  status = C_GetCertFields (certObj, &certFields);
  if (status != 0)
    goto CLEANUP;

  subjectName = certFields.subjectName;

  /* Collect other certificates and CRLs for the PKCS #12, which should be
     placed in a CERT_PATH_CTX. */
  status = C_CreateListObject (&trustedCerts);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("If you don't want to export an entire chain, just ");
  RSA_PrintMessage ("supply the subject's\ncert (or uppermost cert ");
  RSA_PrintMessage ("in the chain) as a trusted certificate.\n");

  RSA_PrintMessage ("Supply trusted certificate(s).\n");
  status = RSA_AddCertsToListPrompt (ctx, trustedCerts);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Optionally supply other certificates necessary for");
  RSA_PrintMessage (" path validation.\n");
  status = RSA_AddCertsToDbPrompt (ctx, database);
  if (status != 0)
    goto CLEANUP;

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

  RSA_PrintMessage ("Optionally supply CRLs necessary for path validation.\n");
  status = RSA_AddCrlsToListPrompt (ctx, crlList);
  if (status != 0)
    goto CLEANUP;

  status = C_InsertCRLList (database, crlList);
  if (status != 0)
    goto CLEANUP;

  pathCtx.pathAlgorithm = PA_X509_V1;
  
  status = C_GetListObjectCount (crlList, &numCrls);
  if (status != 0)
    goto CLEANUP;

  /* Of course, your application can choose to always require CRLs... */
  if (numCrls == 0)
    pathCtx.pathOptions = PF_IGNORE_REVOCATION;
  else
    pathCtx.pathOptions = 0;

  pathCtx.trustedCerts = trustedCerts;
  pathCtx.policies = ANY_POLICY;
  pathCtx.validationTime = PF_VALIDATION_TIME_NOW;
  pathCtx.database = database;

  status = RSA_Pkcs12EncryptionAlgPrompt (&encryptionAlg);
  if (status != 0)
    goto CLEANUP;

  status = RSA_Pkcs12FormatOptionsPrompt (&exportOptions);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Using SHA-1 HMAC.  Recommended iteration count ");
  RSA_PrintMessage ("is 1024.\n");

  macDigest = DAI_SHA1;

  status = RSA_GetInteger (&iterationCount,
                           "Specify an integer for the iteration count");
  if (status == RSA_DEMO_E_CANCEL)
    iterationCount = PKCS12_DEFAULT_ITERATIONS;
  else if (status != 0)
    goto CLEANUP;

  status = RSA_GetCommand (filename, sizeof (filename),
                           "Enter name of file to store PKCS #12 binary");
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetCommand (userInput, sizeof (userInput),
                           "Supply MAC password for message integrity");
  if (status != 0)
    goto CLEANUP;

  status = RSA_FormatPkcs12Password (userInput, &macPassword);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Supply password to protect PKCS #12 contents\n");
  status = RSA_GetCommand (userInput, sizeof (userInput),
                           "(blank to use the same password)");
  if (T_strlen (userInput) == 0) {
    T_memset ((POINTER)userInput, 0, sizeof (userInput));

    status = C_ExportPKCS12 (ctx, subjectName, &pathCtx, encryptionAlg,
                             macDigest, iterationCount, exportOptions,
                             filename, &macPassword);
    if (status != 0)
      goto CLEANUP;
  } else if (status != 0) {
    goto CLEANUP;
  } else {
    status = RSA_FormatPkcs12Password (userInput, &encPassword);
    if (status != 0)
      goto CLEANUP;

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

    exportOptions |= PKCS12_USE_TWO_PASSWORDS;

    status = C_ExportPKCS12 (ctx, subjectName, &pathCtx, encryptionAlg,
                             macDigest, iterationCount, exportOptions,
                             filename, &macPassword, &encPassword);
    if (status != 0)
      goto CLEANUP;
  }

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

  T_memset (macPassword.data, 0, macPassword.len);
  T_memset (encPassword.data, 0, encPassword.len);
  T_free (macPassword.data);
  T_free (encPassword.data);

  C_DestroyCertObject (&certObj);
  B_DestroyKeyObject (&privateKey);
  C_DestroyListObject (&trustedCerts);
  C_DestroyListObject (&crlList);
  C_UnbindService (&database);

  return status;
}  /* end ExportKeyAndCert */

/*  This routine uses C_WriteToPKCS12 to export some arbitrary private keys,
 *  certificates, and CRLs.
 */
static int GeneratePKCS12 (CERTC_CTX ctx)
{
  int status = 0;
  char userInput[RSA_DEMO_MAX_LINE_LEN];
  char filename[RSA_DEMO_MAX_LINE_LEN];

  LIST_OBJ pkcs12Bags = NULL;
  STREAM outputStream = NULL;

  int encryptionAlg = 0, macDigest = 0;
  int iterationCount = 0, exportOptions = 0;
  ITEM macPassword = {NULL, 0}, encPassword = {NULL, 0};

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

  /* Prompt the user to supply the PKCS #12 contents */
  status = RSA_Pkcs12BagInfoPrompt (ctx, pkcs12Bags);
  if (status != 0)
    goto CLEANUP;

  status = RSA_Pkcs12EncryptionAlgPrompt (&encryptionAlg);
  if (status != 0)
    goto CLEANUP;

  status = RSA_Pkcs12FormatOptionsPrompt (&exportOptions);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Using SHA-1 HMAC.  Recommended iteration count ");
  RSA_PrintMessage ("is 1024.\n");

  macDigest = DAI_SHA1;

  status = RSA_GetInteger (&iterationCount,
                           "Specify an integer for the iteration count");
  if (status == RSA_DEMO_E_CANCEL)
    iterationCount = PKCS12_DEFAULT_ITERATIONS;
  else if (status != 0)
    goto CLEANUP;

  status = RSA_GetCommand (filename, sizeof (filename),
                           "Enter name of file to store PKCS #12 binary");
  if (status != 0)
    goto CLEANUP;

  status = C_OpenStream (ctx, STREAM_SP_NAME, (POINTER)filename,
                         IO_WRONLY|IO_CREAT|IO_TRUNC|IO_BINARY, &outputStream);
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetCommand (userInput, sizeof (userInput),
                           "Supply MAC password for message integrity");
  if (status != 0)
    goto CLEANUP;

  status = RSA_FormatPkcs12Password (userInput, &macPassword);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("Supply password to protect PKCS #12 contents\n");
  status = RSA_GetCommand (userInput, sizeof (userInput),
                           "(blank to use the same password)");
  if (T_strlen (userInput) == 0) {
    T_memset ((POINTER)userInput, 0, sizeof (userInput));

    status = C_WriteToPKCS12 (ctx, pkcs12Bags, encryptionAlg, macDigest,
                              iterationCount, exportOptions, &macPassword,
                              outputStream);
    if (status != 0)
      goto CLEANUP;
  } else if (status != 0) {
    goto CLEANUP;
  } else {
    status = RSA_FormatPkcs12Password (userInput, &encPassword);
    if (status != 0)
      goto CLEANUP;

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

    exportOptions |= PKCS12_USE_TWO_PASSWORDS;

    status = C_WriteToPKCS12 (ctx, pkcs12Bags, encryptionAlg, macDigest,
                              iterationCount, exportOptions, &macPassword,
                              outputStream, &encPassword);
    if (status != 0)
      goto CLEANUP;
  }

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

  T_memset (macPassword.data, 0, macPassword.len);
  T_memset (encPassword.data, 0, encPassword.len);
  T_free (macPassword.data);
  T_free (encPassword.data);

  C_CloseStream (outputStream);
  C_DestroyListObject (&pkcs12Bags);

  return status;
}  /* end GeneratePKCS12 */

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