| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: cms.c,v 1.5 2004/03/02 05:18:36 gsingh Exp $ */ /* cms.c ** Copyright (c) 2002-2003, RSA Security Inc. ** ** This file is for demonstration purposes only. ** 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. ** ** Use Cert-C to make or read a CMS message. ** ** Note that the in-memory database used when reading and writing signed data ** and enveloped data messages lasts for the lifetime of the application. ** (i.e. one shared database is used and the contents are not destroyed) ** That way, the user doesn't have to keep adding the same certs over and over ** again until the application is restarted. This program can be modified to ** either use a new database each time by registering and unregistering the ** database called RSA_DEMO_DATABASE_NAME in each procedure that uses the ** database SERVICE. Or, for a more persistent data store, the RSA default ** database provider can be used. ** ** 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 "p7util.h" #include "certutil.h" #include "imdb.h" #include "rsacsp.h" #include "pkixpath.h" #include "cmsobj.h" #ifdef _MSC_VER # pragma warning (disable: 279) /* while (0) */ #endif /* For demonstration purposes, we use the in-memory database with the * following name for easy access. */ #define RSA_DEMO_DATABASE_NAME "in-memory database" /* For multiple updates */ #define DEMO_UPDATE_SIZE 64000 /* These functions are from the Crypto-C sample code. Could use RSA utils functions instead */ static int GetDataFromFile (FILE *, unsigned int, unsigned char *, unsigned int *, int *); static int AppendDataToFile (FILE *, unsigned char *, unsigned int); static int InitializeDatabase (CERTC_CTX ctx); static int DecomposeCMSEnvelopedDataMsg (CERTC_CTX ctx); static int WriteCMSEnvelopedDataMsgFromRawData (CERTC_CTX ctx); /* Used by WriteCMSEnvelopedDataMsgFromRawData, this procedure prompts the user for the * necessary information to obtain the recipientInfos. The certificates will * be placed into the database. */ static int AddCMSRecipients (CERTC_CTX ctx, C_CMS_OBJ cmsObj, SERVICE database); /* Number of service providers registered in the context. */ #define SP_COUNT 2 /* This function initializes the database with the name * RSA_DEMO_DATABASE_NAME, which must be registered in the given CERTC_CTX. */ static int InitializeDatabase (CERTC_CTX ctx) { SERVICE db = NULL; LIST_OBJ trustedRoots = NULL, certList = NULL; int status = 0; RSA_PrintMessage ("Initializing database...\n"); /* Obtain trusted roots and other intermediate certs */ status = C_CreateListObject (&trustedRoots); if (status != 0) goto CLEANUP; status = C_CreateListObject (&certList); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nSupply trusted root certificates.\n"); status = RSA_AddCertsToListPrompt (ctx, trustedRoots); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nTrusted Roots\n"); status = RSA_PrintCertList (trustedRoots); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOptionally supply other relevant certificates, such "); RSA_PrintMessage ("as those needed to\nconstruct the certificate chain.\n"); status = RSA_AddCertsToListPrompt (ctx, certList); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOther certificates\n"); status = RSA_PrintCertList (certList); if (status != 0) goto CLEANUP; /* Add roots and intermediate certs to database */ status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &db); if (status != 0) goto CLEANUP; status = C_InsertCertList (db, trustedRoots); if (status != 0) goto CLEANUP; status = C_InsertCertList (db, certList); if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("InitializeDatabase", status); C_DestroyListObject (&trustedRoots); C_DestroyListObject (&certList); C_UnbindService (&db); return (status); } int main (int argc, char *argv[]) { int status = 0; char command[RSA_DEMO_MAX_LINE_LEN]; CERTC_CTX ctx = NULL; UINT4 flags; SERVICE_HANDLER spTable[SP_COUNT] = { {SPT_DATABASE, RSA_DEMO_DATABASE_NAME, S_InitializeMemoryDB}, {SPT_CRYPTO, "BSAFE Crypto-C", S_InitializeDefaultCSP} }; 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; status = C_InitializeCertC (spTable, spParams, SP_COUNT, &ctx); if (status != 0) goto CLEANUP; /* Set Cert-C flags to indicate the CMS streaming API version 1 */ flags = CERTC_CTX_FLAG_CMS_STREAM_1; status = C_SetCertCFlags (ctx, flags); 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 ("CMS Message Objects\n"); RSA_PrintMessage ("=======================\n"); status = InitializeDatabase (ctx); if (status != 0) goto CLEANUP; for (;;) { RSA_PrintMessage ("\nCMS Object Operations\n"); RSA_PrintMessage (" A - Pull apart a CMS EnvelopedData message\n"); RSA_PrintMessage (" B - Create CMS EnvelopedData message\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 = DecomposeCMSEnvelopedDataMsg (ctx); break; case 'b': case 'B': status = WriteCMSEnvelopedDataMsgFromRawData (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 ("cms", status); C_FinalizeCertC (&ctx); return status; } /* end main */ static int WriteCMSEnvelopedDataMsgFromRawData (CERTC_CTX ctx) { C_CMS_OBJ envelopedObj = NULL; UINT4 cmsFlag = 0; char inputFilename[256], outputFilename[256]; FILE *inputFile=NULL, *outputFile=NULL; ITEM dataToRead = {NULL, 0}; ITEM buffer = {NULL, 0}; int status = 0; int endFlag = 0; int done = 0; SERVICE database = NULL; status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &database); if (status != 0) goto CLEANUP; /* To allow "(blank for empty message): "); as data, use RSA_GetCommand */ status = RSA_GetRequiredCommand (inputFilename, sizeof (inputFilename), "Enter name of file to encapsulate in an enveloped message (required)"); if (status != 0) goto CLEANUP; inputFile = fopen (inputFilename, "rb"); if (inputFile == (FILE *)NULL_PTR) { status = RSA_DEMO_E_FILE_IO; goto CLEANUP; }; status = RSA_GetRequiredCommand (outputFilename, sizeof (outputFilename), "Enter name of file to store CMS data message"); if (status != 0) goto CLEANUP; outputFile = fopen (outputFilename, "wb"); if (outputFile == (FILE *)NULL_PTR) { status = RSA_DEMO_E_FILE_IO; goto CLEANUP; } /* Step 1: Create a CMS object */ status = C_CreateCMSObject (ctx, &envelopedObj); if (status != 0) goto CLEANUP; /* Step 2: Set CMS message type */ status = C_SetCMSMessageType (envelopedObj, C_CMS_MSGTYPE_ENVELOPED_DATA); if (status != 0) goto CLEANUP; /* Step 3: Add recipient info */ /* Get the certs of the recipients into the database */ status = AddCMSRecipients (ctx, envelopedObj, database); if (status != 0) goto CLEANUP; /* Symmetric encryption algorithm is 3DES for now, so don't need to set it. */ dataToRead.data = T_malloc(DEMO_UPDATE_SIZE); buffer.data = T_malloc(DEMO_UPDATE_SIZE); while (!done) { if (!endFlag) { status = GetDataFromFile (inputFile, DEMO_UPDATE_SIZE, dataToRead.data, &dataToRead.len, &endFlag); if (status != 0) goto CLEANUP; /* If we are done reading from the file, then set C_CMS_IO_FLAGS_COMPLETE. Otherwise, set the flag to 0. */ if (endFlag == 1) cmsFlag = C_CMS_IO_FLAGS_COMPLETE; else cmsFlag = 0; /* Step 4: Write data from buffer into CMS object */ status = C_WriteCMSMessage (envelopedObj, dataToRead.data, dataToRead.len, cmsFlag); if (status != 0) goto CLEANUP; } /* end if (!endFlag) */ /* Step 5: Read data from CMS object into buffer */ status = C_ReadCMSMessage (envelopedObj, DEMO_UPDATE_SIZE, buffer.data, &buffer.len, &cmsFlag); if (status != 0) goto CLEANUP; if (cmsFlag & C_CMS_IO_FLAGS_COMPLETE) { done = 1; } /* Write envelopedData from buffer into file */ status = AppendDataToFile (outputFile, buffer.data, buffer.len); if (status != 0) goto CLEANUP; } /* end while */ if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("WriteDataMsg", status); /* Step 6: Destroy */ C_DestroyCMSObject (&envelopedObj); if (dataToRead.data) T_free (dataToRead.data); if (buffer.data) T_free (buffer.data); if (inputFile) fclose (inputFile); if (outputFile) fclose (outputFile); C_UnbindService (&database); return (status); } /* end WriteCMSEnvelopedDataMsgFromRawData */ static int DecomposeCMSEnvelopedDataMsg (CERTC_CTX ctx) { C_CMS_OBJ cmsObj = NULL; UINT4 cmsFlag = 0; char inputFilename[256], outputFilename[256]; FILE *inputFile=NULL, *outputFile=NULL; ITEM buffer = {NULL, 0}; B_KEY_OBJ recipientPvtKey = NULL; CERT_OBJ recipientCert = NULL; int status = 0; int endFlag = 0; int done = 0; SERVICE database = NULL; status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &database); if (status != 0) goto CLEANUP; status = RSA_GetRequiredCommand (inputFilename, sizeof (inputFilename), "Enter name of file containing CMS message (required)"); if (status != 0) goto CLEANUP; inputFile = fopen (inputFilename, "rb"); if (inputFile == (FILE *)NULL_PTR) { status = RSA_DEMO_E_FILE_IO; goto CLEANUP; }; status = RSA_GetRequiredCommand (outputFilename, sizeof (outputFilename), "Enter name of file to store output message (required)"); if (status != 0) goto CLEANUP; outputFile = fopen (outputFilename, "wb"); if (outputFile == (FILE *)NULL_PTR) { status = RSA_DEMO_E_FILE_IO; goto CLEANUP; } /* Obtain recipient's private key */ RSA_PrintMessage ("Obtaining recipient certificate and private key...\n"); status = C_CreateCertObject (&recipientCert, ctx); if (status != 0) goto CLEANUP; status = B_CreateKeyObject (&recipientPvtKey); if (status != 0) goto CLEANUP; status = RSA_GetCertAndPvtKey (ctx, recipientCert, recipientPvtKey); if (status != 0) goto CLEANUP; status = C_InsertCert (database, recipientCert); if (status != 0) goto CLEANUP; status = C_InsertPrivateKey (database, recipientCert, recipientPvtKey); if (status != 0) goto CLEANUP; /* Step 1: Create a CMS object */ status = C_CreateCMSObject (ctx, &cmsObj); if (status != 0) goto CLEANUP; /* Do not set CMS message type, since we don't know what type yet */ buffer.data = T_malloc(DEMO_UPDATE_SIZE); while (!done) { if (!endFlag) { status = GetDataFromFile (inputFile, DEMO_UPDATE_SIZE, buffer.data, &buffer.len, &endFlag); if (status != 0) goto CLEANUP; /* If we are done reading from the file, then set C_CMS_IO_FLAGS_COMPLETE. Otherwise, set the flag to 0. */ if (endFlag == 1) cmsFlag = C_CMS_IO_FLAGS_COMPLETE; else cmsFlag = 0; /* Step 2: Write data from buffer into CMS object */ /* The object will configure itself on the first write(s), ** based on information in the message. If this is ** an EnvelopedData object, then recipient certificates (necessary ** for decrypting the message) will be fetched from any of ** the databases found in the Cert-C context. */ status = C_WriteCMSMessage (cmsObj, buffer.data, buffer.len, cmsFlag); if (status != 0) goto CLEANUP; } /* end if (!endFlag) */ /* Step 3: Read data from CMS object into buffer */ status = C_ReadCMSMessage (cmsObj, DEMO_UPDATE_SIZE, buffer.data, &buffer.len, &cmsFlag); if (status != 0) goto CLEANUP; if (cmsFlag & C_CMS_IO_FLAGS_COMPLETE) { done = 1; } status = AppendDataToFile (outputFile, buffer.data, buffer.len); if (status != 0) goto CLEANUP; } /* end while (!done) */ if (status != 0) goto CLEANUP; /* Call C_GetCMSRecipients() to get recipient list */ CLEANUP: if (status != 0) RSA_PrintError ("DecomposeCMSEnvelopedDataMsg", status); /* Step 4: Destroy */ C_DestroyCMSObject (&cmsObj); if (buffer.data) T_free (buffer.data); if (inputFile) fclose (inputFile); if (outputFile) fclose (outputFile); C_UnbindService (&database); C_DestroyCertObject (&recipientCert); B_DestroyKeyObject (&recipientPvtKey); return (status); } static int AddCMSRecipients (CERTC_CTX ctx, C_CMS_OBJ cmsObj, SERVICE database) { int status = 0, recipientCount = 1; unsigned char *certBer = NULL; unsigned int certBerLen = 0; CERT_OBJ receiverCert = NULL; CERT_FIELDS certFields; RECIPIENT_INFO recipientInfo; RSA_PrintMessage ("\nSpecify at least one message recipient.\n"); for (;;) { RSA_PrintMessage ("Supply certificate for recipient #%i,", recipientCount++); RSA_PrintMessage ("blank if there are no more recipients.\n"); status = RSA_GetFileToAllocBuffer (&certBer, &certBerLen, "Enter name of file containing cert binary"); if (status == RSA_DEMO_E_CANCEL) { status = 0; break; } else if (status != 0) goto CLEANUP; /* For now, the only encryption algorithm available with which to encrypt * the data encryption key is RSA. If there is another alternative, the * user should be prompted to choose here for use below. */ status = C_CreateCertObject (&receiverCert, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (receiverCert, certBer, certBerLen); if (status != 0) goto CLEANUP; status = C_InsertCert (database, receiverCert); if (status != 0) goto CLEANUP; status = C_GetCertFields (receiverCert, &certFields); if (status != 0) goto CLEANUP; recipientInfo.type = KEY_TRANSPORT; /* only one available for now */ recipientInfo.info.keyTrans.recipCertId.type = ISSUER_SERIAL; recipientInfo.info.keyTrans.recipCertId.id.issuerSerialNumber.issuerName = certFields.issuerName; recipientInfo.info.keyTrans.recipCertId.id.issuerSerialNumber.serialNumber = certFields.serialNumber; recipientInfo.info.keyTrans.keyEncryptionAlgorithmId.algorithmId = KA_RSA_ENCRYPTION; recipientInfo.info.keyTrans.keyEncryptionAlgorithmId.algorithmParam = NULL; /* status = C_AddRecipientToList (recipientInfos, &recipientInfo, NULL); */ status = C_AddCMSRecipient (cmsObj, &recipientInfo); if (status != 0) goto CLEANUP; T_free (certBer); certBer = NULL; C_DestroyCertObject (&receiverCert); } RSA_PrintMessage ("\nFinished collecting Recipient Information\n"); CLEANUP: if (status != 0) RSA_PrintError ("GetRecipientInfoList", status); T_free (certBer); C_DestroyCertObject (&receiverCert); return (status); } /* end AddCMSRecipients */ static int GetDataFromFile ( FILE *inputFile, unsigned int sizeToRead, unsigned char *dataToRead, unsigned int *dataToReadLen, int *endFlag) { unsigned char dummy; do { *dataToReadLen = fread (dataToRead, 1, sizeToRead, inputFile); if (*dataToReadLen == sizeToRead) /* Read exactly sizeToRead bytes, so reading one more will set end of file if there were exactly sizeToRead bytes in the file. */ fread (&dummy, 1, 1, inputFile); if (feof (inputFile)) { *endFlag = 1; fclose (inputFile); } else { *endFlag = 0; fseek (inputFile, -1, SEEK_CUR); } } while (0); return (0); } /* end GetDataFromFile */ int AppendDataToFile (FILE *outputFile, unsigned char *blockOfData, unsigned int outputLenUpdate) { do { if (fwrite (blockOfData, 1, outputLenUpdate, outputFile) < outputLenUpdate) { fclose (outputFile); } } while (0); return (0); } /* end AppendDataToFile */