RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

pkcs11db.c

Setup of PKCS #11 Database Provider. Generates a key pair on the device. Generates a self-signed certificate. Puts that certificate on the device.

/* $Id: pkcs11db.c,v 1.4 2004/03/02 05:18:39 gsingh Exp $ */
/* pkcs11db.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 setup of the PKCS #11 database provider.  This is a stand-alone
** application that is a simple demonstration that generates a keypair on the
** device, generates a self-signed certificate, and puts that certificate on
** the device.
**
** For another example that shows the use of keys and certs stored in a
** PKCS #11 device being used for PKCS #7 messaging, see the sample in
** samples/pkcs7/pkcs11msg.c.  Keep in mind that the device must be
** configured properly to allow the insertion of a cert and private key to the
** token.  Also, make sure that there is enough available space on the token.
**
** An exercise for the user would be to modify this program, or write a
** similar program, that generated a PKCS #10 for the keypair on the device.
** Then, by whatever means that request is fulfilled, take the issued cert and
** place it on the device.
**
** 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 "pkcs11db.h"
#include "rsacsp.h"
#include "demoutil.h"
#include "certutil.h"
#include "p11util.h"

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

/*  This procedure generates a new RSA keypair on the PKCS #11 device.  The
 *  code here has been largely lifted from the Crypto-C p11rsakeygen.c sample
 *  in the Crypto-C samples/pkcs11 directory.  Be sure to examine the options
 *  in the function definition below to make sure the options are compatible
 *  with the configuration of your device.
 */
static int GeneratePkcs11RsaKeypair (CERTC_CTX ctx, B_KEY_OBJ publicKey,
                              B_KEY_OBJ privateKey);

/*  This procedure generates a certificate for the given public key, signed
 *  with the given private key.  The user is prompted to supply information
 *  for the other certificate fields.
 */
static int GenerateUserCert (CERTC_CTX ctx, B_KEY_OBJ publicKey,
                      B_KEY_OBJ privateKey, CERT_OBJ certObj);

/*  InsertUserInfo inserts the given cert and private key into the database
 *  specified by dbName. A properly initialized CERTC_CTX should be passed in.
 */
static int InsertUserInfo (CERTC_CTX ctx, char *dbName, CERT_OBJ certObj);

/*  DumpDatabaseContents retrieves all of the certificates from the 
 *  database specified by dbName.  Information about all of these certificates
 *  are then printed out.  A properly initialized CERTC_CTX must be passed in.
 */
static int DumpDatabaseContents (CERTC_CTX ctx, char *dbName);

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

  CERTC_CTX ctx = NULL;

  B_KEY_OBJ publicKey = NULL, privateKey = NULL;
  CERT_OBJ certObj = NULL;

  B_PKCS11_SESSION p11SessionInfo;
  PKCS11_INIT_PARAMS p11InitParams;
  PKCS11_CRYPTO_PARAMS p11CryptoParams;

  SERVICE_HANDLER p11DbServiceHandler = {
    SPT_DATABASE, "Sample PKCS #11 Database", S_InitializePKCS11DB
  };

  SERVICE_HANDLER p11CryptoServiceHandler = {
    SPT_CRYPTO, "Crypto Provider with PKCS #11", S_InitializeDefaultCSP2
  };

  char *libraryName = NULL;
  ITEM tokenLabel = {NULL, 0}, passphrase = {NULL, 0};

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

  /* Initialize variables for graceful error-handling */
  T_memset ((POINTER)&p11SessionInfo, 0, sizeof (p11SessionInfo));
  T_memset ((POINTER)&p11InitParams, 0, sizeof (p11InitParams));
  T_memset ((POINTER)&p11CryptoParams, 0, sizeof (p11CryptoParams));

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

  RSA_PrintMessage ("PKCS #11 Key Generation and Cert Storage Example\n");
  RSA_PrintMessage ("================================================\n");

  status = C_InitializeCertC (NULL, NULL, 0, &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

  /* This function is described in samples/common/include/p11util.h */
  status = RSA_Pkcs11InfoPrompt (&libraryName, &tokenLabel, &passphrase);
  if (status != 0)
    goto CLEANUP;

  p11SessionInfo.libraryName = libraryName;
  p11SessionInfo.tokenLabel.data = tokenLabel.data;
  p11SessionInfo.tokenLabel.len = tokenLabel.len;
  p11SessionInfo.passPhrase.data = passphrase.data;
  p11SessionInfo.passPhrase.len = passphrase.len;

  p11InitParams.pPKCS11Info = &p11SessionInfo;

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

  RSA_PrintMessage ("Registration of PKCS #11 DB provider successful.\n");

  /* We need the PKCS #11 Crypto provider since we're doing key generation.
     Currently, only one connection per chooser (crypto service provider
     instance) is supported */
  p11CryptoParams.pSessionInfo = &p11SessionInfo;
  p11CryptoParams.sessionCount = 1;

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

  RSA_PrintMessage
    ("Registration of Crypto Provider with PKCS #11 successful.\n");

  /* Generate a new keypair for the user */
  status = B_CreateKeyObject (&publicKey);
  if (status != 0)
    goto CLEANUP;

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

  status = GeneratePkcs11RsaKeypair (ctx, publicKey, privateKey);
  if (status != 0)
    goto CLEANUP;

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

  status = GenerateUserCert (ctx, publicKey, privateKey, certObj);
  if (status != 0)
    goto CLEANUP;

  status = InsertUserInfo (ctx, p11DbServiceHandler.name, certObj);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintMessage ("\nDumping database contents...\n");
  status = DumpDatabaseContents (ctx, p11DbServiceHandler.name);
  if (status != 0)
    goto CLEANUP;
  
CLEANUP:
  if (status != 0)
    RSA_PrintError ("pkcs11db.c", status);
  else
    RSA_PrintMessage ("Success!\n");

  T_memset (passphrase.data, 0, passphrase.len);

  T_free ((POINTER)libraryName);
  T_free (tokenLabel.data);
  T_free (passphrase.data);

  B_DestroyKeyObject (&publicKey);
  B_DestroyKeyObject (&privateKey);
  C_DestroyCertObject (&certObj);
  C_FinalizeCertC (&ctx);

  return status;
}  /* end main */

/* See function declaration at the top for a description */
int GeneratePkcs11RsaKeypair (CERTC_CTX ctx, B_KEY_OBJ publicKey,
                              B_KEY_OBJ privateKey)
{
  int status = 0;

  B_ALGORITHM_OBJ keyGenObj = NULL;
  B_KEYPAIR_GEN_PARAMS keypairGenParams;
  A_RSA_KEY_GEN_PARAMS rsaKeyGenParams;

  B_ALGORITHM_OBJ randomObj = NULL;
  B_ALGORITHM_CHOOSER chooser;
  ITEM deviceName = {NULL, 0};

  unsigned char f4data[] = {0x01, 0x00, 0x01};

  RSA_PrintMessage ("Generating RSA keypair...\n");

  status = B_CreateAlgorithmObject (&keyGenObj);
  if (status != 0)
    goto CLEANUP;

  /* Supply information needed to generate keypair */
  status = RSA_GetInteger ((int *)&rsaKeyGenParams.modulusBits,
                           "Enter desired RSA key size (512-2048)");
  if (status != 0)
    goto CLEANUP;

  rsaKeyGenParams.publicExponent.data = f4data;
  rsaKeyGenParams.publicExponent.len = sizeof (f4data);

  keypairGenParams.privateKeyAttributes.keyUsage =
    CF_DIGITAL_SIGNATURE | CF_KEY_ENCIPHERMENT | CF_DATA_ENCIPHERMENT;
  keypairGenParams.privateKeyAttributes.tokenFlag = 
    TF_RESIDE_ON_TOKEN | TF_PRIVATE;
  keypairGenParams.privateKeyAttributes.start = 0;
  keypairGenParams.privateKeyAttributes.end = 0;
  keypairGenParams.publicKeyAttributes.keyUsage =
    CF_DIGITAL_SIGNATURE | CF_KEY_ENCIPHERMENT | CF_DATA_ENCIPHERMENT;
  keypairGenParams.publicKeyAttributes.tokenFlag = TF_RESIDE_ON_TOKEN;
  keypairGenParams.publicKeyAttributes.start = 0;
  keypairGenParams.publicKeyAttributes.end = 0;
  keypairGenParams.keypairGenInfoType = AI_RSAKeyGen;
  keypairGenParams.keypairGenInfo = (POINTER)&rsaKeyGenParams;

  status = B_SetAlgorithmInfo (keyGenObj, AI_KeypairGen,
                               (POINTER)&keypairGenParams);
  if (status != 0)
    goto CLEANUP;

  status = C_GetChooser (ctx, &chooser);
  if (status != 0)
    goto CLEANUP;

  status = B_GenerateInit (keyGenObj, chooser, NULL);
  if (status != 0)
    goto CLEANUP;

  status = B_GetDevice (&deviceName, keyGenObj);
  if (status != 0)
    goto CLEANUP;

  RSA_PrintBuf ("Device Name for RSA Key Generation",
                deviceName.data, deviceName.len);

  status = C_GetRandomObject (ctx, &randomObj);
  if (status != 0)
    goto CLEANUP;

  status = B_GenerateKeypair (keyGenObj, publicKey, privateKey,
                              randomObj, NULL);

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

  B_DestroyAlgorithmObject (&keyGenObj);

  return status;
}  /* end GeneratePkcs11RsaKeypair */

/* See function declaration at the top for a description */
int GenerateUserCert (CERTC_CTX ctx, B_KEY_OBJ publicKey,
                      B_KEY_OBJ privateKey, CERT_OBJ certObj)
{
  int status = 0;
  ALGORITHM_IDENTIFIER signatureAlg;
  CERT_FIELDS certFields;

  RSA_PrintMessage ("Generating User's Cert...\n");

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

  status = RSA_ChooseCertVersionPrompt (&certFields.version);
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetItem (&certFields.serialNumber,
                        "Enter hex-ascii serial number (blank to quit)");
  if (status != 0)
    goto CLEANUP;

  status = RSA_ChooseSignatureAlgorithmPrompt (&signatureAlg);
  if (status != 0)
    goto CLEANUP;

  certFields.signatureAlgorithm = signatureAlg.algorithmId;

  status = C_CreateNameObject (&certFields.issuerName);
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetNameObject (certFields.issuerName, "issuer");
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetInputToUint4Time (&certFields.validity.start,
                                    "Enter validity start");
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetInputToUint4Time (&certFields.validity.end,
                                    "Enter validity end");
  if (status != 0)
    goto CLEANUP;

  status = C_CreateNameObject (&certFields.subjectName);
  if (status != 0)
    goto CLEANUP;
  
  status = RSA_GetNameObject (certFields.subjectName, "subject");
  if (status != 0)
    goto CLEANUP;

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

  if (certFields.version == CERT_VERSION_1) {
    certFields.issuerUniqueID.data = NULL_PTR;
    certFields.issuerUniqueID.len = 0;
    certFields.issuerUniqueID.unusedBits = 0;
    certFields.subjectUniqueID.data = NULL_PTR;
    certFields.subjectUniqueID.len = 0;
    certFields.subjectUniqueID.unusedBits = 0;
  }
  else {
    status = RSA_GetBitString
             (&certFields.issuerUniqueID,
              "Enter optional unique ID of cert issuer (blank to omit)");
    if (status != 0 && status != RSA_DEMO_E_CANCEL)
      goto CLEANUP;

    status = RSA_GetBitString
             (&certFields.subjectUniqueID,
              "Enter optional unique ID of cert subject (blank to omit)");
    if (status != 0 && status != RSA_DEMO_E_CANCEL)
      goto CLEANUP;
  }

  status = C_CreateExtensionsObject (&certFields.certExtensions,
                                     CERT_EXTENSIONS_OBJ, ctx);
  if (status != 0)
    goto CLEANUP;

  if (certFields.version == CERT_VERSION_3) {
    status = RSA_GetExtensionsObject (certFields.certExtensions,
                                      CERT_EXTENSIONS_OBJ);
    if (status != 0)
      goto CLEANUP;
  }

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

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

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

  C_DestroyNameObject (&certFields.issuerName);
  C_DestroyNameObject (&certFields.subjectName);
  C_DestroyExtensionsObject (&certFields.certExtensions);
  T_free (certFields.publicKey.data);
  T_free (certFields.issuerUniqueID.data);
  T_free (certFields.subjectUniqueID.data);

  return status;
}  /* end GenerateUserCert */

/* See function declaration at the top for a description */
int InsertUserInfo (CERTC_CTX ctx, char *dbName, CERT_OBJ certObj)
{
  int status = 0;

  SERVICE db = (SERVICE)NULL_PTR;

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

  RSA_PrintMessage ("Inserting certificate into database...\n");
  status = C_InsertCert (db, certObj);
  if (status != 0)
    goto CLEANUP;

  /* since the private key was generated on the PKCS #11 device, we do
     not need to insert it here */

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

  C_UnbindService (&db);

  return status;
}  /* end InsertUserInfo */

/* See function declaration at the top for a description */
int DumpDatabaseContents (CERTC_CTX ctx, char *dbName)
{
  int status = 0;

  SERVICE db = (SERVICE)NULL_PTR;
  LIST_OBJ certList = (LIST_OBJ)NULL_PTR;
  DB_ITERATOR iterator = (DB_ITERATOR)NULL_PTR;
  CERT_OBJ certObj = (CERT_OBJ)NULL_PTR;

  unsigned int certCount = 0, i = 0;

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

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

  /*  To walk through the certs in the database, we need to initialize an
   *  iterator by first calling C_SelectFirstCert().  Subsequent calls can
   *  then be made to C_SelectNextCert().  When the iterator is NULL_PTR, we
   *  know that any memory associated with it has been freed and no certs
   *  remain in the database.  Should you need to dispose of the iterator
   *  before all certs have been retrieved, you may call C_FreeIterator().
   */
  status = C_SelectFirstCert (db, &iterator, certList);
  if (iterator == NULL) {
    RSA_PrintMessage ("Database empty.\n");
    status = 0;
  }
  else if (status != 0)
    goto CLEANUP;
  else {
    RSA_PrintMessage ("Retrieving first cert...\n");

    /*  Retrieve all of the remaining certificates.  When there are no more
     *  certificates, the iterator will be set to NULL.
     */
    for (;;) {
      status = C_SelectNextCert (&iterator, certList);
      if (iterator == NULL) {
        status = 0;
        break;
      }
      else if (status != 0)
        goto CLEANUP;

      RSA_PrintMessage ("Retrieving another cert from database...\n");
    }
  }

  /*  Now that we have a list object filled with certificates, print out some
   *  information about each certificate.
   */
  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;

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

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

  C_DestroyListObject (&certList);
  C_FreeIterator (&iterator);
  C_UnbindService (&db);
  
  return status;
}  /* end DumpDatabaseContents */

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