RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

sslcpvt.c

Uses ASN.1 decoding APIs to take a a PKCS #8 binary, verify that it is a PKCS #8 containing an RSAPrivateKey, extract the RSAPrivateKey, and save it to a file.

/* $Id: sslcpvt.c,v 1.3 2004/03/02 05:18:32 gsingh Exp $ */
/* sslcpvt.c
** Copyright (c) 2001-2002, 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.
**
** Crypto-C and Cert-C use RSA private keys contained in PKCS #8
** PrivateKeyInfo containers, while SSL-C uses data in the raw PKCS #1
** RSAPrivateKey format.  (The advantage of the PKCS #8 format, like the
** X.509 SubjectPublicKeyInfo for public keys, is that it contains an
** algorithm identifier specifying the type of key, so that an application
** receiving the key knows what type of key it is)  This sample demonstrates
** the ASN.1 decoding APIs in Cert-C by taking a PKCS #8 binary, verifying
** that it is a PKCS #8 containing an RSAPrivateKey, extracting the
** RSAPrivateKey, and saving it to a file.  Note that this is a binary,
** unencrypted RSA private key.
**
** Here is the ASN.1 definition of a PrivateKeyInfo:
**
**   PrivateKeyInfo ::= SEQUENCE {
**     version Version,
**     privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
**     privateKey PrivateKey,
**     attributes [0] IMPLICIT Attributes OPTIONAL
**   }
**
**   PrivateKey ::= OCTET STRING
**
** Basically, we need to get the third item in the sequence and extract the
** value of that octet string.
**
** 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 "demoutil.h"
#include "keyutil.h"

/* This routine prompts the user to supply a PKCS #8 PrivateKeyInfo and
 * extracts the private key binary contained in it.
 */
static int ExtractKey (CERTC_CTX ctx);

/* This routine prompts the user to supply a PKCS #1 RSAPrivateKey and
 * wraps it in a PKCS #8 PrivateKeyInfo.  This example can be extended
 * to encapsulate other keys.
 */
static int AssemblePrivateKeyInfo (CERTC_CTX ctx);

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

  CERTC_CTX ctx = 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 (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

  RSA_PrintMessage ("Private Key Format Converter\n");
  RSA_PrintMessage ("============================\n");

  for (;;) {
    RSA_PrintMessage ("\nPrivate Key Conversion Operations\n");
    RSA_PrintMessage ("  A - Extract key from PrivateKeyInfo\n");
    RSA_PrintMessage ("  B - Put key binary into PrivateKeyInfo\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 = ExtractKey (ctx);
        break;
      case 'b':
      case 'B':
        status = AssemblePrivateKeyInfo (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 ("asn1.c", status);

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

static int ExtractKey (CERTC_CTX ctx)
{
  int status;

  B_KEY_OBJ privateKey = NULL;

  ITEM privateKeyInfoBer = {NULL, 0}, pvtBer = {NULL, 0};
  ITEM *privateKeyBer = NULL;

  int tag = 0;
  unsigned int tagClass = 0;
  LIST_OBJ privateKeyInfoItems = NULL;

  RSA_PrintMessage ("\nPlease supply a PKCS #8 PrivateKeyInfo binary.\n");
  status = RSA_GetKeyObjFromFile (RSA_DEMO_PRIVATE_KEY, &privateKey);
  if (status != 0)
    goto CLEANUP;

  status = RSA_GetKeyBer (RSA_DEMO_PRIVATE_KEY, privateKey,
                          &privateKeyInfoBer);
  if (status != 0)
    goto CLEANUP;

  /* Pull apart the PrivateKeyInfo SEQUENCE */
  status = C_CreateListObject (&privateKeyInfoItems);
  if (status != 0)
    goto CLEANUP;

  status = C_BERDecodeList (ctx, privateKeyInfoBer.data, privateKeyInfoBer.len,
                            &tag, &tagClass, privateKeyInfoItems);
  if (status != 0)
    goto CLEANUP;

  if (tag != VT_SEQUENCE) {
    status = RSA_DEMO_E_INVALID_MESSAGE;
    RSA_PrintMessage ("Expected VT_SEQUENCE...\n");
    goto CLEANUP;
  }

  /* If desired, the second element in the PrivateKeyInfo can be examined
   * to see if it is a known algorithm identifier to identify the type of
   * key we are going to extract.  We leave this as an optional exercise
   * for the reader.
   */

  /* Access the PrivateKey, the third element in the PrivateKeyInfo */
  status = C_GetListObjectEntry (privateKeyInfoItems, 2,
                                 (POINTER *)&privateKeyBer);
  if (status != 0)
    goto CLEANUP;

  /* The privateKeyBer should be an OCTET STRING */
  status = C_BERDecodeString (ctx, privateKeyBer->data, privateKeyBer->len,
                              &tag, &tagClass, &pvtBer.data, &pvtBer.len);
  if (status != 0)
    goto CLEANUP;

  if (tag != VT_OCTET_STRING) {
    status = RSA_DEMO_E_INVALID_MESSAGE;
    RSA_PrintMessage ("Expected VT_OCTET_STRING...\n");
    goto CLEANUP;
  }

  status = RSA_WriteDataToFile (pvtBer.data, pvtBer.len,
             "Enter name of file to store extracted private key binary");
  
CLEANUP:
  if (status != 0)
    RSA_PrintError ("ExtractKey", status);

  T_memset (pvtBer.data, 0, pvtBer.len);
  T_free (pvtBer.data);

  T_memset (privateKeyInfoBer.data, 0, privateKeyInfoBer.len);
  T_free (privateKeyInfoBer.data);

  B_DestroyKeyObject (&privateKey);
  C_DestroyListObject (&privateKeyInfoItems);
  
  return status;
}  /* end ExtractKey */

static int AssemblePrivateKeyInfo (CERTC_CTX ctx)
{
  int status;

  ITEM rsaPvtKeyBer = {NULL, 0}, pvtKeyOctetString = {NULL, 0};
  ITEM pvtKeyInfoBer = {NULL, 0};
  LIST_OBJ sequence = NULL;

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

  unsigned char rsaEncryptionAlgId[] = {
    0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
    0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00
  };

  ITEM version, algId;

  RSA_PrintMessage ("\nThis example currently assumes that you want to wrap ");
  RSA_PrintMessage ("a\nPKCS #1 RSAPrivateKey in a PKCS #8 PrivateKeyInfo.");
  RSA_PrintMessage ("  No\nchecking is done on the PKCS #1 binary you ");
  RSA_PrintMessage ("supply.\n\n");
  status = RSA_GetFileToAllocBuffer
             (&rsaPvtKeyBer.data, &rsaPvtKeyBer.len,
              "Enter name of file containing a binary PKCS #1 RSAPrivateKey");
  if (status != 0)
    goto CLEANUP;

  /* Wrap the private key in an OCTET STRING */
  status = C_DEREncodeString (ctx, VT_OCTET_STRING, VTC_UNIVERSAL,
                              rsaPvtKeyBer.data, rsaPvtKeyBer.len,
                              &pvtKeyOctetString.data, &pvtKeyOctetString.len);
  if (status != 0)
    goto CLEANUP;

  /* Create the PKCS #8 PrivateKeyInfo SEQUENCE containing the version,
   * RSA Enryption algorithm identifier, and the private key OCTET STRING.
   * This program only handles RSA keys; checking and supporting algorithm
   * identifiers for other types of keys is left as an exercise for the user.
   */

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

  version.data = pkcs8Version;
  version.len = sizeof (pkcs8Version);

  status = C_AddItemToList (sequence, &version, NULL);
  if (status != 0)
    goto CLEANUP;

  algId.data = rsaEncryptionAlgId;
  algId.len = sizeof (rsaEncryptionAlgId);

  status = C_AddItemToList (sequence, &algId, NULL);
  if (status != 0)
    goto CLEANUP;

  status = C_AddItemToList (sequence, &pvtKeyOctetString, NULL);
  if (status != 0)
    goto CLEANUP;

  status = C_DEREncodeList (ctx, VT_SEQUENCE, VTC_UNIVERSAL, sequence,
                            &pvtKeyInfoBer.data, &pvtKeyInfoBer.len);
  if (status != 0)
    goto CLEANUP;

  status = RSA_WriteDataToFile
             (pvtKeyInfoBer.data, pvtKeyInfoBer.len,
              "Enter name of file to store PKCS #8 PrivateKeyInfo binary");

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

  T_memset (rsaPvtKeyBer.data, 0, rsaPvtKeyBer.len);
  T_free (rsaPvtKeyBer.data);

  T_memset (pvtKeyOctetString.data, 0, pvtKeyOctetString.len);
  T_free (pvtKeyOctetString.data);

  T_memset (pvtKeyInfoBer.data, 0, pvtKeyInfoBer.len);
  T_free (pvtKeyInfoBer.data);

  C_DestroyListObject (&sequence);

  return status;
}  /* end AssemblePrivateKeyInfo */

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