RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

pkcs12.c

Reads and parses a PKCS #12 message, outputting any certificates, CRLs, and private keys to a database.

/* $Id: pkcs12.c,v 1.4 2004/03/02 05:18:41 gsingh Exp $ */
/* pkcs12.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 file takes in a PKCS #12 message and parses it, outputting any certs,
** CRLs, and private keys contained in the message.
**
** 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 "fileio.h"
#include "demoutil.h"
#include "p12util.h"
#include "certutil.h"
#include "crlutil.h"
#include "keyutil.h"

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

#define SP_COUNT 3
#define DB_NAME "In-memory 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);

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

  CERTC_CTX ctx = NULL;
  SERVICE db = NULL;

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

  POINTER spParams[SP_COUNT] = {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;
  
  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 = C_BindService (ctx, SPT_DATABASE, DB_NAME, &db);
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetCommand (fileName, sizeof (fileName),
                           "Enter name of PKCS #12 binary (blank to cancel)");
  if (status != 0 || fileName[0] == '\0')
    goto CLEANUP;

  status = RSA_GetCommand (password, sizeof (password),
                           "Enter MAC password to verify message integrity");
  if (status != 0)
    goto CLEANUP;

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

  RSA_PrintMessage ("Enter decryption password to decrypt private key\n");
  status = RSA_GetCommand (password, sizeof (password),
                           "(blank to use the same password)");
  if (T_strlen (password) == 0) {
    status = C_ImportPKCS12 (ctx, fileName, &macPasswordItem, db, p12Options);
    if (status != 0) {
      /* RSA_FormatPkcs12Password will turn a blank password into the two-byte
         value {0x00, 0x00}.  This is the blank password used by Netscape, but
         not IE/CryptoAPI.  If the initial call failed, try again with the
         IE/CryptoAPI blank password. */
      if (macPasswordItem.len == 2) {
        RSA_PrintMessage ("Trying password of length 0...\n");
        macPasswordItem.len = 0;
        status = C_ImportPKCS12 (ctx, fileName, &macPasswordItem, db,
                                 p12Options);
      }
        
      if (status != 0)
        goto CLEANUP;
    }
  } else if (status != 0) {
    goto CLEANUP;
  } else {
    status = RSA_FormatPkcs12Password (password, &encPasswordItem);
    if (status != 0)
      goto CLEANUP;

    p12Options |= PKCS12_USE_TWO_PASSWORDS;

    status = C_ImportPKCS12 (ctx, fileName, &macPasswordItem, db, p12Options,
                             &encPasswordItem);
    if (status != 0)
      goto CLEANUP;
  }

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

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

  T_memset ((unsigned char *)password, 0, sizeof (password));
  T_memset (macPasswordItem.data, 0, macPasswordItem.len);
  T_memset (encPasswordItem.data, 0, encPasswordItem.len);
  T_free (macPasswordItem.data);
  T_free (encPasswordItem.data);
  C_UnbindService (&db);
  C_FinalizeCertC (&ctx);

  return status;
}  /* end main */

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;

  /* 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;
  }  

  status = B_CreateKeyObject (&privateKeyObj);
  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 */

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