RSA BSAFE Cert-C

Certificate Components for C

Crypto-C 6.2.1 Developer's Guide
Search

fulfill.c

Utilities to decompose information from a certificate request into an unsigned Certificate object, add default-supported X.509 v3 extensions, and sign the certificate

/* $Id: fulfill.c,v 1.4 2004/03/02 05:18:34 gsingh Exp $ */
/* fulfill.c
** Copyright (c) 1995-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.
*/

#include <time.h>
#include <stdio.h>
#include "certc.h"
#include "aglobal.h"
#include "bsafe.h"
#include "demo.h"
#include "bcert.h"
#include "dutil.h"   
#include "stdlibrf.h" 
#include "userextn.h"
#include "dexten.h"
#include "myprint.h"  
#include "fulfill.h"
#include "inoutcl.h"

#ifdef TEST_MEMORY_LEAK
#include "memlog.h"
#endif

static UINT4 CURRENT_SERIAL = (UINT4)0;
#if 0
static unsigned char CERT_POLICY_ID_1[] = "Certicate Policy ID 1";
static unsigned char CERT_POLICY_ID_2[] = "Certicate Policy ID 2";
#endif

/* demo unique serial number.  Real application should make a unique
   serial number for each certificate issued by a certificate authority.
  */
static unsigned char SERIAL_NUMBER[] = {
  0x02, 0x01, 0x01, 0x01
};


static int AddCertExtensions PROTO_LIST ((EXTENSIONS_OBJ));
#if 0
static void ConverseToString PROTO_LIST ((unsigned char *, UINT4));
static UINT4 ConverseToUINT4 PROTO_LIST ((unsigned char *));
#endif

#ifdef _BCERT_API_
static int DoSignCert PROTO_LIST
  ((CERT_OBJ, B_ALGORITHM_OBJ, A_SURRENDER_CTX *));
#else
static int DoSignCert PROTO_LIST ((CERT_OBJ));
static int GetCertObjFromPKCS10BER PROTO_LIST ((SESSION_CTX *, CERT_OBJ,
                                                ITEM *));
#endif /* _BCERT_API_ */

static int GetCertRequestInfo PROTO_LIST ((CERT_OBJ, SESSION_CTX *));
static int GetNextSerialNumberAlloc PROTO_LIST 
  ((unsigned char **, unsigned int *));

/* Read the certificate request file and use the authority private key to
     fulfill the request.  The result is outputted into a file.
 */
int FulfillCertRequest (sessionContext)
SESSION_CTX *sessionContext;
{
  CERT_OBJ certObject = NULL_PTR;
  ITEM ber;
  int status;
  
  do {
#ifdef _BCERT_API_
    if ((status = C_CreateCertObject
         (&certObject, sessionContext->applContext)) != 0)
      break;
#else
    if ((status = C_CreateCertObject
         (&certObject, sessionContext->certcContext)) != 0)
      break;
#endif /* _BCERT_API_ */
    if ((status = GetCertRequestInfo (certObject, sessionContext)) != 0)
      break;

#ifdef _BCERT_API_
    if ((status = DoSignCert 
         (certObject, sessionContext->randomObject, 
          &sessionContext->surrender)) != 0)
      break;
#else
    if ((status = DoSignCert (certObject)) != 0)
      break;
#endif /* _BCERT_API_ */
    
    PrintCertObject 
      (certObject, "\n\nFulfilled Certificate information:",
       &sessionContext->ioContext);

    /* Get the BER encoding of the cert request */
    if ((status = C_GetCertDER (certObject, &ber.data, &ber.len)) != 0)
      break;
    
    /* Write to a file */
    if ((status = WriteToFile
         (&ber, "approved certificate", &sessionContext->ioContext)) != 0) 
      break;
  } while (0);

  C_DestroyCertObject (&certObject);
  return (status);
}

#ifdef _BCERT_API_
int DoVerifyCert (certObject, surrender)
CERT_OBJ certObject;
A_SURRENDER_CTX *surrender;
#else
int DoVerifyCert (certObject)
CERT_OBJ certObject;
#endif /* _BCERT_API_ */
{
  B_KEY_OBJ keyObject = NULL_PTR;
  ITEM publicKeyInfo;
  int status;

  do {
    if ((status = B_CreateKeyObject (&keyObject)) != 0)
      break;
    publicKeyInfo.data = ISSUER_PUBLIC_KEY;
    publicKeyInfo.len = sizeof (ISSUER_PUBLIC_KEY);
    if ((status = B_SetKeyInfo 
         (keyObject, KI_RSAPublicBER, (POINTER)&publicKeyInfo)) != 0)
      break;
#ifdef _BCERT_API_
    status = C_VerifyCertSignature (certObject, keyObject, surrender);
#else
    status = C_VerifyCertSignature (certObject, keyObject);
#endif /* _BCERT_API_ */
  } while (0);
  B_DestroyKeyObject (&keyObject);
  return (status);
}


/* This function decomposes a certificate request into a certObject.
   Updates the certObject with issuer name, new certificate serial number,
   and validity period.  It also adds a signing time attribute to the
   certificate extension object.  
   Note: 
     The signing time attribute may be inserted in the
     subjectDirectoryAttributes extension.  It is used here as a value for
     the user-defined extension  "USER_DEFINED_EXTENSION"
     
 */
static int GetCertRequestInfo (certObject, sessionContext)
CERT_OBJ certObject;
SESSION_CTX *sessionContext;
{ 
  CERT_FIELDS certFields;  
  ITEM certRequestBER; 
  int status;

#ifdef _BCERT_API_
  unsigned char digest[32];
  unsigned int digestLen;
#endif /* _BCERT_API_ */

  certRequestBER.data = NULL_PTR;
  certRequestBER.len = 0;
  T_memset ((POINTER)&certFields, 0, sizeof (certFields));
  
  do { 
    /* Read in the cert request information from input file */
    if ((status = ReadFromFile 
         (&certRequestBER, "certificate request", &sessionContext->ioContext))
         != 0)
      break;

    PrintMessage
       ("\nValidating signature on the certificate request...",
        0, IO_CTX_PROMPT, &sessionContext->ioContext);  
        
    /* Transfer certificate request information into an unsigned cert object.  
         This process first validates the signature on the certificate request 
         to make sure that the public key is really binded to the private key 
         which was used to sign the request.  Then it will initialize the 
         validity to one year period. */
#ifdef _BCERT_API_
    if ((status = C_DecomposePKCSCertRequestBER
         (certObject, (ATTRIBUTES_OBJ)NULL_PTR, certRequestBER.data, 
          certRequestBER.len, digest, &digestLen, &sessionContext->surrender))
         != 0)
      break;
#else
    if ((status = GetCertObjFromPKCS10BER
                  (sessionContext, certObject, &certRequestBER)) != 0)
      break;
#endif /* _BCERT_API_ */
        
    /* Get out cert request fields */
    if ((status = C_GetCertFields (certObject, &certFields)) != 0)
      break;
    
    /* This serial number should be unique for each certificate that the 
         issuer is going to generate.
     */
    if ((status = GetNextSerialNumberAlloc 
         (&certFields.serialNumber.data, &certFields.serialNumber.len)) != 0)
      break;
    
    /* Since C_DecomposePKCSCertRequestBER() sets the issuer name to be the
         same as the subject name.  We must reset the issuer name to the 
         correct name. Note that the subject name is subordinate to the 
         issuer name even though we don't check for subordination here. 
     */
    C_ResetNameObject (certFields.issuerName);
    if ((status = MakeNameObject 
         (certFields.issuerName, (unsigned char *)NULL_PTR, 0)) != 0)
      break;
     
    if ((status = AddCertExtensions (certFields.certExtensions)) != 0)
      break;
      
    /* Update the certObject with new information in the certFields.  It is
         important that we call C_SetCertFields().  Otherwise, the certObject
         will still be in the state after the C_DecomposePKCSCertRequestBER()
         call. 
     */
         
    /* The validity is already set for 1 year period from now.  Make it 
       validate 2 days later. */
     certFields.validity.start += (UINT4)48 * 3600;
     certFields.validity.end += (UINT4)48 * 3600;  
     
    /* BCERT 1.0 only supports certificate request version 1, therefore the
         certFields.version is set to CERT_VERSION_1.  Since we add some v3
         certificate extensions to certFields.certExtensions, we need to
         change the version to CERT_VERSION_3.
     */
    certFields.version = CERT_VERSION_3;
    status = C_SetCertFields (certObject, &certFields);
    
  } while (0);
  T_free ((POINTER)certRequestBER.data);
  T_free ((POINTER)certFields.serialNumber.data); 
  return (status);
}

/* This routine will add 3 certificate extensions to the extensionsObject.
     A signing time attribute is added as a user-defined extension type.  
     keyUsage and privateKeyUsagePeriod extensions will also be added.
 */
static int AddCertExtensions (extObject)
EXTENSIONS_OBJ extObject;
{
  ATTRIBUTES_OBJ attributes = (ATTRIBUTES_OBJ)NULL_PTR;
  UINT4 currentTime, keyUsage;  
  PRIVATE_KEY_USAGE_PERIOD privateKeyValidity;
  int status;
  
  do {
    if ((status = C_CreateAttributesObject (&attributes)) != 0)
      break;  
    /* Create signing time attribute as an extension value */
    T_time (&currentTime);
    if ((status = C_SetSigningTimeAttribute (attributes, currentTime)) != 0)
      break; 
    /* Add a user-defined extension */
    if ((status = AddExtensions 
         (extObject, USER_DEFINED_EXTENSION, 
          USER_DEFINED_EXTENSION_LEN, (POINTER)attributes)) != 0)
      break;
   
    keyUsage = (UINT4)(CF_DIGITAL_SIGNATURE | CF_CRL_SIGN);
    /* Add a key usage extension */
    if ((status = AddExtensions 
         (extObject, ET_KEY_USAGE, ET_KEY_USAGE_LEN, (POINTER)&keyUsage)) 
         != 0)
      break; 
 
    privateKeyValidity.end.year = 
      (unsigned short)((privateKeyValidity.start.year = 1996) + 1);
    privateKeyValidity.end.month = (privateKeyValidity.start.month = 11); 
    privateKeyValidity.end.day = (privateKeyValidity.start.day = 06);  
    privateKeyValidity.end.hour = (privateKeyValidity.start.hour = 21);
    privateKeyValidity.end.minute = (privateKeyValidity.start.minute = 6); 
    privateKeyValidity.end.second = (privateKeyValidity.start.second = 27); 
    privateKeyValidity.end.microSecond = 
      ((privateKeyValidity.start.microSecond = 1111) + 2222);  
    privateKeyValidity.end.timeZone = 
      (short int)((privateKeyValidity.start.timeZone = 0) - 10);
    /* Add a private key usage period extension */
    if ((status = AddExtensions 
         (extObject, ET_PRIVATE_KEY_USAGE_PERIOD, 
          ET_PRIVATE_KEY_USAGE_PERIOD_LEN, (POINTER)&privateKeyValidity)) != 0)
      break;  
  } while (0);
  C_DestroyAttributesObject (&attributes);
  return (status);
}

/*  Sign a certificate with the issuer's private key. 
 */
#ifdef _BCERT_API_
static int DoSignCert (certObject, randomObject, surrender)
CERT_OBJ certObject;
B_ALGORITHM_OBJ randomObject;
A_SURRENDER_CTX *surrender;
#else
static int DoSignCert (certObject)
CERT_OBJ certObject;
#endif /* _BCERT_API_ */
{
  B_KEY_OBJ keyObject = NULL_PTR;
  ITEM privateKeyInfo;
  int status;

  do {
    if ((status = B_CreateKeyObject (&keyObject)) != 0)
      break;
    privateKeyInfo.data = ISSUER_PRIVATE_KEY;
    privateKeyInfo.len = sizeof (ISSUER_PRIVATE_KEY);
    if ((status = B_SetKeyInfo 
         (keyObject, KI_PKCS_RSAPrivateBER, (POINTER)&privateKeyInfo)) != 0)
      break; 
#ifdef _BCERT_API_
    status = C_SignCert (certObject, keyObject, randomObject, surrender);
#else
    status = C_SignCert (certObject, keyObject);
#endif /* _BCERT_API_ */
  } while (0);
  B_DestroyKeyObject (&keyObject);
  return (status);
}

#ifndef _BCERT_API_
/* Since C_DecomposePKCSCertRequestBER was deprecated, we must construct
   a procedure which creates a CERT_OBJ template using the information
   contained in a PKCS #10 message.
 */
static int GetCertObjFromPKCS10BER (SESSION_CTX *sessionContext,
                                    CERT_OBJ certObj, ITEM *pkcs10BER)
{
  int status = 0;
  PKCS10_OBJ pkcs10Obj = NULL;
  PKCS10_FIELDS pkcs10Fields;
  CERT_FIELDS certFields;
  ITEM subjectName = {NULL, 0}, publicKeyBER = {NULL, 0};

  status = C_CreatePKCS10Object (sessionContext->certcContext, &pkcs10Obj);
  if (status != 0)
    goto CLEANUP;

  status = C_SetPKCS10BER (pkcs10Obj, pkcs10BER->data, pkcs10BER->len);
  if (status != 0)
    goto CLEANUP;

  /* Verify that the PKCS #10 request was signed with the private key
     corresponding to the public key contained in the PKCS #10 request. */
  status = C_VerifyPKCS10Signature (pkcs10Obj);
  if (status != 0)
    goto CLEANUP;

  status = C_GetPKCS10Fields (pkcs10Obj, &pkcs10Fields);
  if (status != 0)
    goto CLEANUP;

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

  certFields.version = CERT_VERSION_1;
  certFields.signatureAlgorithm = SA_MD5_WITH_RSA_ENCRYPTION;
  certFields.serialNumber.data = NULL;
  certFields.serialNumber.len = 0;
  
  status = C_GetNameDER (pkcs10Fields.subjectName, &subjectName.data,
                         &subjectName.len);
  if (status != 0)
    goto CLEANUP;

  /* as a default, set issuer name to be the subject name */
  status = C_SetNameBER (certFields.issuerName, subjectName.data,
                         subjectName.len);
  if (status != 0)
    goto CLEANUP;

  status = C_SetNameBER (certFields.subjectName, subjectName.data,
                         subjectName.len);
  if (status != 0)
    goto CLEANUP;

  /* by default, set validity to 1 year */
  T_time (&certFields.validity.start);
  certFields.validity.end = certFields.validity.start +
    ((3600 * 24 * 365) - 1);

  publicKeyBER.len = pkcs10Fields.publicKey.len;
  publicKeyBER.data = T_malloc (publicKeyBER.len);
  if (publicKeyBER.data == NULL) {
    status = E_ALLOC;
    goto CLEANUP;
  }

  T_memcpy (publicKeyBER.data, pkcs10Fields.publicKey.data,
            publicKeyBER.len);

  certFields.publicKey.data = publicKeyBER.data;
  certFields.publicKey.len = publicKeyBER.len;

  status = C_SetCertFields (certObj, &certFields);

CLEANUP:
  C_DestroyPKCS10Object (&pkcs10Obj);
  T_free (publicKeyBER.data);

  return status;
}  /* end GetCertObjFromPKCS10BER */
#endif /* _BCERT_API_ */

static UINT4 ConvertToUINT4 (text)
unsigned char *text;
{
  UINT4 number = 0L;
  int i, j;
  
  for (i=0, j=3; i < 4; i++, j--)  
    number |= ((UINT4) (*(text + i))) << (j * 8);
  return (number);
}

static void ConvertToString (text, number)
unsigned char *text;
UINT4 number;
{
  int i, j;
  /* start i at 1 because we will add on the 0x20 at the beginning
     when we return from this function */
  for (i= 1, j = 3; i < 5; i++, j--)
    *(text + i) = (unsigned char)((number >> (j*8)) & 0xff);
}

static int GetNextSerialNumberAlloc (text, textLen)
unsigned char **text;
unsigned int *textLen;
{
  int status;

  if ((*text = (unsigned char *)T_malloc (6)) == 0)
    return (status = E_ALLOC);
    
  if (!CURRENT_SERIAL)
    CURRENT_SERIAL = ConvertToUINT4 (SERIAL_NUMBER);
  ++CURRENT_SERIAL;
  ConvertToString (*text, CURRENT_SERIAL);
  **text = 0x20;
  *textLen = 5;
  return (0);
}


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