RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

pkcs11msg.c

Demonstrates the use of the PKCS #11 database and PKCS #11-aware cryptographic service providers. Creates a PKCS #7 signed data message using a private key on a token accessed using PKCS #11. This signed data message should be able to be verified using the datamsg sample. Finds the first matching certificate and private key to use for signing. A real application could present the user with a choice of certificates/keys to use for signing.

/* $Id: pkcs11msg.c,v 1.4 2004/03/02 05:18:42 gsingh Exp $ */
/* pkcs11msg.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.
**
** Create a PKCS #7 signed data message using a private key accessed on a
** token using PKCS #11.  This signed data message should be able to be
** verified using the datamsg sample.
**
** You can use the samples/db/pkcs11db sample to put a private key and
** matching self-signed cert onto the device.
**
** This sample just finds the first matching cert and private key to use
** for signing.  A real application could present the user with a choice
** of certs/keys to use for signing.
**
** 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 "demoutil.h"
#include "p7util.h"
#include "p11util.h"
#include "keyutil.h"

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

/* Identifier for PKCS #11 database instance */
#define P11_DB_NAME "Sample PKCS #11 Database"

/* This routine takes a SERVICE containing a PKCS #11 database source, a
 * cert object and key object which have been created but not initialized,
 * and finds a matching cert and private key to place in those objects.
 * A real application would do a better job of selection, but for the purposes
 * of this example, we just get a private key and matching cert.
 */
static int GetCertAndPrivateKey (SERVICE db, CERT_OBJ cert,
                                 B_KEY_OBJ privateKey);

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

  CERTC_CTX ctx = NULL;
  SERVICE db = NULL;

  B_KEY_OBJ signerPrivateKey = NULL;
  CERT_OBJ signerCert = NULL;
  CERT_FIELDS certFields;

  LIST_OBJ signerInfos = NULL;
  SIGNER_INFO signerInfo;
  ITEM signedAttribBer = {NULL, 0}, unsignedAttribBer = {NULL, 0};
  ITEM fileContents = {NULL, 0}, dataMsg = {NULL, 0}, signedMsg = {NULL, 0};

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

  B_PKCS11_SESSION p11SessionInfo;
  PKCS11_INIT_PARAMS p11InitParams;
  PKCS11_CRYPTO_PARAMS p11CryptoParams;

  SERVICE_HANDLER p11DbServiceHandler = {
    SPT_DATABASE, P11_DB_NAME, S_InitializePKCS11DB
  };

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

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

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

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

  RSA_PrintMessage ("Using PKCS #11 Device for Signing Data 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

  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");

  /* 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");

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

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

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

  status = GetCertAndPrivateKey (db, signerCert, signerPrivateKey);
  if (status != 0)
    goto CLEANUP;

  /* Obtain data to sign, which must be a PKCS #7 message of some type */
  status = RSA_GetFileToAllocBuffer (&fileContents.data, &fileContents.len,
                                     "Enter name of file to sign");
  if (status != 0)
    goto CLEANUP;

  status = C_WriteDataMsg (ctx, &fileContents, &dataMsg);
  if (status != 0)
    goto CLEANUP;

  /* Don't need fileContents any more... */
  T_free (fileContents.data);
  fileContents.data = NULL;

  /* Set up signer information */
  status = C_CreateListObject (&signerInfos);
  if (status != 0)
    goto CLEANUP;

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

  signerInfo.signerCertId.type = ISSUER_SERIAL;
  signerInfo.signerCertId.id.issuerSerialNumber.issuerName =
    certFields.issuerName;
  signerInfo.signerCertId.id.issuerSerialNumber.serialNumber =
    certFields.serialNumber;

  /* Fill in the signer's digest algorithm and "signature" algorithm.  Note
   * that the digest algorithm can be either MD5 or SHA1 and the "signature"
   * algorithm, which specifies the operation performed on the digest, can
   * either be RSA Encryption or DSA with SHA1.
   */
  status = RSA_SignerInfoSignatureAlgPrompt
             (&signerInfo.digestAlgorithmId, &signerInfo.signatureAlgorithmId);
  if (status != 0)
    goto CLEANUP;

  /* prompt for attributes to be signed - this is optional
   *  If RSA_GetFileToAllocBuffer returns RSA_DEMO_E_CANCEL,
   *  signerInfo.signedAttributes will remain NULL.
   */
  RSA_PrintMessage ("\nOptionally supply an attributes object to be ");
  RSA_PrintMessage ("authenticated attributes.\nEnter name of file ");
  RSA_PrintMessage ("containing attributes object, or blank to omit:\n");
  status = RSA_GetFileToAllocBuffer (&signedAttribBer.data,
                                     &signedAttribBer.len, NULL);
  if (status != 0 && status != RSA_DEMO_E_CANCEL)
    goto CLEANUP;
  else if (status == 0) {
    status = C_CreateAttributesObject (&signerInfo.signedAttributes);
    if (status != 0)
      goto CLEANUP;

    status = C_SetAttributesBER (signerInfo.signedAttributes,
                                 signedAttribBer.data, signedAttribBer.len);
    if (status != 0)
      goto CLEANUP;
  }
    
  /* prompt for attributes which will not be signed - this is optional
   *  If RSA_GetFileToAllocBuffer returns RSA_DEMO_E_CANCEL,
   *  signerInfo.unsignedAttributes will remain NULL.
   */
  RSA_PrintMessage ("\nOptionally supply unauthenticated attributes.\n");
  RSA_PrintMessage ("Enter name of file containing attributes object, or ");
  RSA_PrintMessage ("blank to omit:\n");
  status = RSA_GetFileToAllocBuffer (&unsignedAttribBer.data,
                                     &unsignedAttribBer.len, NULL);
  if (status != 0 && status != RSA_DEMO_E_CANCEL)
    goto CLEANUP;
  else if (status == 0) {
    status = C_CreateAttributesObject (&signerInfo.unsignedAttributes);
    if (status != 0)
      goto CLEANUP;

    status = C_SetAttributesBER (signerInfo.unsignedAttributes,
                                 unsignedAttribBer.data,
                                 unsignedAttribBer.len);
    if (status != 0)
      goto CLEANUP;
  }

  /*  Other possibilities are C_AddUniqueSignerToList and
   *  C_InsertSignerInList.
   */
  status = C_AddSignerToList (signerInfos, &signerInfo, NULL);
  if (status != 0)
    goto CLEANUP;

  status = C_WriteSignedDataMsg (ctx, NULL, db, &dataMsg, CMSF_NONE, NULL,
                                 NULL, signerInfos, &signedMsg);
  if (status != 0)
    goto CLEANUP;

  /* Don't need data message any more... */
  T_free (dataMsg.data);
  dataMsg.data = NULL;

  status = RSA_WriteDataToFile
             (signedMsg.data, signedMsg.len,
              "Enter name of file to store signed data message");

CLEANUP:
  if (status != 0)
    RSA_PrintError ("pkcs11msg", 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);

  T_free (signedAttribBer.data);
  T_free (unsignedAttribBer.data);

  T_free (fileContents.data);
  T_free (dataMsg.data);
  T_free (signedMsg.data);

  C_DestroyAttributesObject (&signerInfo.signedAttributes);
  C_DestroyAttributesObject (&signerInfo.unsignedAttributes);
  C_DestroyListObject (&signerInfos);

  B_DestroyKeyObject (&signerPrivateKey);
  C_DestroyCertObject (&signerCert);

  C_UnbindService (&db);
  C_FinalizeCertC (&ctx);
  
  return status;
}  /* end main */

/* See function declaration at the top for a description */
static int GetCertAndPrivateKey (SERVICE db, CERT_OBJ cert,
                                 B_KEY_OBJ privateKey)
{
  int status = 0;

  DB_ITERATOR iterator = NULL;
  LIST_OBJ certList = NULL;
  CERT_OBJ currentCert = NULL;
  ITEM currentCertBer = {NULL, 0};

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

  status = C_SelectFirstCert (db, &iterator, certList);
  if (iterator == NULL) {
    RSA_PrintMessage ("Database empty - no certs present.\n");
    status = 0;
    goto CLEANUP;
  }
  else if (status != 0)
    goto CLEANUP;

  for (;;) {
    /* We're done if there's a private key corresponding to the cert
       we've found */
    status = C_GetListObjectEntry (certList, 0, (POINTER *)&currentCert);
    if (status != 0)
      goto CLEANUP;

    status = C_SelectPrivateKeyByCert (db, currentCert, privateKey);
    if (status == 0) {  /* we're done - copy the resulting cert */
      status = C_GetCertDER (currentCert, &currentCertBer.data,
                             &currentCertBer.len);
      if (status != 0)
        goto CLEANUP;

      status = C_SetCertBER (cert, currentCertBer.data, currentCertBer.len);
      if (status != 0)
        goto CLEANUP;

      RSA_PrintMessage ("Found cert and private key...\n");
      break;
    }

    /* Clear the certList and try again with the next cert */
    C_ResetListObject (certList);

    status = C_SelectNextCert (&iterator, certList);
    if (status != 0) {
      RSA_PrintMessage ("Could not find cert and private key.\n");
      goto CLEANUP;
    }

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

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

  C_DestroyListObject (&certList);
  C_FreeIterator (&iterator);

  return status;
}  /* end GetCertAndPrivateKey */

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