RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

p12memio.c

This file takes in a PKCS #12 message and parses it using the sample in-memory I/O provider.

/* $Id: p12memio.c,v 1.6 2004/03/02 05:18:41 gsingh Exp $ */
/* p12memio.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 file takes in a PKCS #12 message and parses it using the sample
** in-memory I/O provider.
**
** 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 "memio.h"
#include "demoutil.h"
#include "p12util.h"
#include "certutil.h"
#include "crlutil.h"
#include "keyutil.h"
#include "dbutil.h"

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

#define SP_COUNT 2
#define DB_NAME "In-memory DB"
#define MEM_IO_NAME "Memory IO"
#define OUTPUT_MEM_IO_NAME "Output Memory IO"

/*  Given a LIST_OBJ of PKCS12_BAGs, parse the contents and place them in the
 *  given database SERVICE.
 */
static int PopulateDb (CERTC_CTX ctx, LIST_OBJ p12Bags, SERVICE db);

/*  Takes in a SERVICE which should be bound to a database provider instance
 *  and outputs any certs, CRLs, and private keys to files.
 */
static int DumpDbContents (SERVICE db);

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

int main (int argc, char *argv[])
{
  int status = 0;
  char password[RSA_DEMO_MAX_LINE_LEN];
  ITEM passwordItem = {NULL, 0};

  CERTC_CTX ctx = NULL;
  SERVICE db = NULL;
  LIST_OBJ p12Bags = NULL;

  STREAM outputStream = (STREAM)NULL_PTR;

  SERVICE_HANDLER spTable[SP_COUNT] = {
    {SPT_CRYPTO, "Default CSP", S_InitializeDefaultCSP},
    {SPT_DATABASE, DB_NAME, S_InitializeMemoryDB}
  };

  POINTER spParams[SP_COUNT] = {NULL, NULL};

  ITEM pfxItem = {NULL, 0}, outputPfxItem = {NULL, 0};

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

  SERVICE_HANDLER memioHandler = {
    SPT_IO, MEM_IO_NAME, S_InitializeMemoryIO
  };

  SERVICE_HANDLER outputMemioHandler = {
    SPT_IO, OUTPUT_MEM_IO_NAME, S_InitializeMemoryIO
  };

  status = RSA_SetOptions (&logParams, argc, argv);
  if (status != 0)
    goto CLEANUP;
  
  RSA_PrintMessage ("PKCS #12 Example\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 = RSA_GetFileToAllocBuffer
             (&pfxItem.data, &pfxItem.len,
              "Enter name of file containing PKCS #12 binary");
  if (status != 0)
    goto CLEANUP;

  status = C_RegisterService (ctx, &memioHandler, (POINTER)&pfxItem,
                              SERVICE_ORDER_FIRST);
  if (status != 0)
    goto CLEANUP;

  status = C_RegisterService (ctx, &outputMemioHandler,
                              (POINTER)&outputPfxItem, SERVICE_ORDER_FIRST);
  if (status != 0)
    goto CLEANUP;

  status = C_BindService (ctx, SPT_DATABASE, DB_NAME, &db);
  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_OpenStream (ctx, MEM_IO_NAME, NULL_PTR, 0,
                         &outputStream);
  if (status != 0)
    goto CLEANUP;

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

  status = C_ReadFromPKCS12 (ctx, outputStream, &passwordItem, 0, p12Bags);
  if (status != 0)
    goto CLEANUP;

  status = PopulateDb (ctx, p12Bags, db);
  if (status != 0)
    goto CLEANUP;

  status = DumpDbContents (db);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nGenerating a new PFX with the same contents...\n");

  /* Given a list of PKCS #12 bags, try to write a PKCS #12 to the memory
     I/O provider. */
  status = GeneratePKCS12 (ctx, p12Bags);
  if (status != 0)
    goto CLEANUP;

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

  T_memset ((unsigned char *)password, 0, sizeof (password));
  T_memset (passwordItem.data, 0, passwordItem.len);
  T_free (passwordItem.data);
  T_free (pfxItem.data);
  C_DestroyListObject (&p12Bags);
  C_CloseStream (outputStream);
  C_UnbindService (&db);
  C_FinalizeCertC (&ctx);

  return status;
}  /* end main */

static int PopulateDb (CERTC_CTX ctx, LIST_OBJ p12Bags, SERVICE db)
{
  int status = 0;
  unsigned int numBags = 0, i = 0;
  PKCS12_BAG *p12Bag = NULL;

  status = C_GetListObjectCount (p12Bags, &numBags);
  if (status != 0)
    goto CLEANUP;

  for (i = 0; i < numBags; i++) {
    status = C_GetListObjectEntry (p12Bags, i, (POINTER *)&p12Bag);
    if (status != 0)
      goto CLEANUP;

    switch (p12Bag->type) {
      case PKCS12_KEY_BAG_TYPE:
        RSA_PrintMessage ("Key bag detected.\n");

        if (p12Bag->content.keyContent.cert != (CERT_OBJ)NULL_PTR) {
          status = C_InsertPrivateKey (db, p12Bag->content.keyContent.cert,
                                       p12Bag->content.keyContent.key);
          if (status != 0)
            goto CLEANUP;

          status = C_InsertCert (db, p12Bag->content.keyContent.cert);
          if (status != 0)
            goto CLEANUP;
        } else {
          status = RSAUTIL_InsertPrivateKey (ctx, db,
                                             p12Bag->content.keyContent.key);
          if (status != 0)
            goto CLEANUP;
        }
        break;
      case PKCS12_CERT_BAG_TYPE:
        RSA_PrintMessage ("Cert bag detected.\n");

        status = C_InsertCert (db, p12Bag->content.certContent);
        if (status != 0)
          goto CLEANUP;

        break;
      case PKCS12_CRL_BAG_TYPE:
        RSA_PrintMessage ("CRL bag detected.\n");

        status = C_InsertCRL (db, p12Bag->content.crlContent);
        if (status != 0)
          goto CLEANUP;

        break;
      case PKCS12_SECRET_BAG_TYPE:
        RSA_PrintMessage ("Secret bag detected.\n");

        RSA_PrintBuf ("Secret Content Type",
                      p12Bag->content.secretContent.type.data,
                      p12Bag->content.secretContent.type.len);
        RSA_PrintBuf ("Secret Content Value",
                      p12Bag->content.secretContent.value.data,
                      p12Bag->content.secretContent.value.len);

        break;
      default:
        RSA_PrintMessage ("Unrecognized bag type.\n");
    }
  }

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

  return status;
}  /* end PopulateDb */

static int DumpDbContents (SERVICE db)
{
  int status = 0;
  DB_ITERATOR iterator = NULL;  

  LIST_OBJ certList = NULL, crlList = NULL;
  ITEM certBer = {NULL, 0}, crlBer = {NULL, 0}, privateKeyBer = {NULL, 0};
  unsigned int certCount = 0, crlCount = 0, i = 0;

  CERT_OBJ certObj = NULL;
  CRL_OBJ crlObj = NULL;
  B_KEY_OBJ privateKeyObj = NULL;

  /* Create containers for certs, CRLs, and private keys */
  status = C_CreateListObject (&certList);
  if (status != 0)
    goto CLEANUP;

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

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

  /* Populate the containers with the database contents.  Note that if
   * C_SelectFirstCert is unsuccessful or when C_SelectNextCert is
   * unsuccessful, iterator is destroyed and set to NULL by Cert-C.
   * On the status==E_NOT_FOUND condition, we do not need to set status to 0
   * before continuing because it is subsequently set before its value is
   * checked again.  If that is not the case, the application may want to set
   * status to 0 before continuing.
   */
  status = C_SelectFirstCert (db, &iterator, certList);
  if (status == E_NOT_FOUND)
    RSA_PrintMessage ("No certificates present.\n");
  else if (status != 0)
    goto CLEANUP;
  else
    for (;;) {
      status = C_SelectNextCert (&iterator, certList);
      if (status == E_NOT_FOUND)
        break;
      else if (status != 0)
        goto CLEANUP;
    }

  /* save the certs to files */
  status = C_GetListObjectCount (certList, &certCount);
  if (status != 0)
    goto CLEANUP;

  for (i = 0; i < certCount; i++) {
    status = C_GetListObjectEntry (certList, i, (POINTER *)&certObj);
    if (status != 0)
      goto CLEANUP;

    status = C_GetCertDER (certObj, &certBer.data, &certBer.len);
    if (status != 0)
      goto CLEANUP;

    RSA_PrintMessage ("\nCertificate #%u\n", i+1);
    status = RSA_PrintCertInfo (certObj);
    if (status != 0)
      goto CLEANUP;

    status = RSA_WriteDataToFile
             (certBer.data, certBer.len,
              "Enter name of file to store cert binary");
    if (status == RSA_DEMO_E_CANCEL)
      status = 0;  /* don't force user to save cert */
    else if (status != 0)
      goto CLEANUP;
  }
  
  /* extract CRLs */
  status = C_SelectFirstCRL (db, &iterator, crlList);
  if (status == E_NOT_FOUND)
    RSA_PrintMessage ("No CRLs present.\n");
  else if (status != 0)
    goto CLEANUP;
  else
    for (;;) {
      status = C_SelectNextCRL (&iterator, crlList);
      if (status == E_NOT_FOUND)
        break;
      else if (status != 0)
        goto CLEANUP;
    }

  /* save the CRLs to files */
  status = C_GetListObjectCount (crlList, &crlCount);
  if (status != 0)
    goto CLEANUP;

  for (i = 0; i < crlCount; i++) {
    status = C_GetListObjectEntry (crlList, i, (POINTER *)&crlObj);
    if (status != 0)
      goto CLEANUP;

    status = C_GetCRLDER (crlObj, &crlBer.data, &crlBer.len);
    if (status != 0)
      goto CLEANUP;

    RSA_PrintMessage ("\nCRL #%u\n", i+1);
    status = RSA_PrintCrlInfo (crlObj);
    if (status != 0)
      goto CLEANUP;

    status = RSA_WriteDataToFile
             (crlBer.data, crlBer.len,
              "Enter name of file to store CRL binary");
    if (status == RSA_DEMO_E_CANCEL)
      status = 0;  /* don't force user to save CRL */
    else if (status != 0)
      goto CLEANUP;
  }  

  /* save private keys to files */
  status = C_SelectFirstPrivateKey (db, &iterator, privateKeyObj);
  if (status == E_NOT_FOUND) {
    RSA_PrintMessage ("No private keys present.\n");
    status = 0;
  }
  else if (status != 0)
    goto CLEANUP;
  else
    for (i = 0;; i++) {
      status = RSA_GetKeyBer (RSA_DEMO_PRIVATE_KEY, privateKeyObj,
                              &privateKeyBer);
      if (status != 0)
        goto CLEANUP;

      RSA_PrintMessage ("\nPrivate Key #%u\n", i+1);
      status = RSA_WriteDataToFile
               (privateKeyBer.data, privateKeyBer.len,
                "Enter name of file to store private key binary");
      if (status == RSA_DEMO_E_CANCEL)
        status = 0;  /* don't force user to save key */
      if (status != 0)
        goto CLEANUP;

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

      status = C_SelectNextPrivateKey (&iterator, privateKeyObj);
      if (status == E_NOT_FOUND) {
        status = 0;
        break;
      }
      else if (status != 0)
        goto CLEANUP;
    }
  
CLEANUP:
  if (status != 0)
    RSA_PrintError ("DumpDbContents", status);

  T_memset (privateKeyBer.data, 0, privateKeyBer.len);
  T_free (privateKeyBer.data);
  B_DestroyKeyObject (&privateKeyObj);
  C_FreeIterator (&iterator);
  C_DestroyListObject (&certList);
  C_DestroyListObject (&crlList);
  
  return status;
}  /* end DumpDbContents */

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

  unsigned char outputBlock[100];
  unsigned int outputLen = 0;

  FILE *outputFile = NULL;

  STREAM outputStream = NULL;

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

  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 (userInput, sizeof (userInput),
                           "Supply password to protect PKCS #12 contents");
  if (status != 0)
    goto CLEANUP;

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

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

  status = C_OpenStream (ctx, OUTPUT_MEM_IO_NAME, NULL_PTR, 0, &outputStream);
  if (status != 0)
    goto CLEANUP;

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

  status = RSA_OpenDataFilePrompt (&outputFile, RSA_DEMO_WRITE_BINARY,
                                   "Enter name of file to output PFX file");
  if (status != 0)
    goto CLEANUP;

  for (;;) {
    status = C_ReadStream (outputStream, outputBlock, sizeof (outputBlock),
                           &outputLen);
    if (status == E_EOS) {
      status = 0;
      break;
    } else if (status != 0)
      goto CLEANUP;

    status = RSA_AppendDataToFile (outputFile, outputBlock, outputLen);
    if (status != 0)
      goto CLEANUP;
  }

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

  T_memset (password.data, 0, password.len);
  T_free (password.data);
  C_CloseStream (outputStream);
  RSA_CloseDataFile (&outputFile);

  return status;
}  /* end GeneratePKCS12 */

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