| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: datamsg.c,v 1.4 2004/03/02 05:18:41 gsingh Exp $ */ /* datamsg.c ** Copyright (c) 1999-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. ** ** Use Cert-C to make or read a PKCS #7 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" /* For demonstration purposes, we use the in-memory database with the * following name for easy access. */ #define RSA_DEMO_DATABASE_NAME "in-memory database" static int DecomposePkcs7 (CERTC_CTX ctx); /* Note that for the following functions, the caller must free the data field * of the contents output. */ static int DecomposeDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents); static int DecomposeSignedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents); static int DecomposeEnvelopedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents); static int DecomposeDigestedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents); static int DecomposeEncryptedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents); static int WriteDataMsg (CERTC_CTX ctx); static int WriteSignedDataMsg (CERTC_CTX ctx); static int WriteEnvelopedDataMsg (CERTC_CTX ctx); static int WriteDigestedDataMsg (CERTC_CTX ctx); static int WriteEncryptedDataMsg (CERTC_CTX ctx); /* Used by WriteSignedDataMsg, this procedure prompts the user for the * necessary information to obtain the signerInfos. The private keys and * certificates of the signers will be placed in the database. */ static int GetSignerInfoList (CERTC_CTX ctx, SERVICE database, LIST_OBJ signerInfos); /* Used by WriteEnvelopedDataMsg, this procedure prompts the user for the * necessary information to obtain the recipientInfos. The certificates will * be placed into the database. */ static int GetRecipientInfoList (CERTC_CTX ctx, SERVICE database, LIST_OBJ recipientInfos); /* Number of service providers registered in the context. */ #define SP_COUNT 3 /* This global cert path context is used for path validation */ static CERT_PATH_CTX P7_PATH_CTX; /* This function initializes the P7_PATH_CTX and requires a database with the * name RSA_DEMO_DATABASE_NAME to be registered in the given CERTC_CTX. */ static int InitializeGlobalPathCtx (CERTC_CTX ctx) { int status = 0; P7_PATH_CTX.pathAlgorithm = PA_X509_V1; P7_PATH_CTX.pathOptions = PF_IGNORE_REVOCATION; RSA_PrintMessage ("Initializing path context...\n"); status = C_CreateListObject (&P7_PATH_CTX.trustedCerts); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply trusted certificates.\n"); status = RSA_AddCertsToListPrompt (ctx, P7_PATH_CTX.trustedCerts); if (status != 0) goto CLEANUP; P7_PATH_CTX.policies = ANY_POLICY; P7_PATH_CTX.validationTime = PF_VALIDATION_TIME_NOW; status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &P7_PATH_CTX.database); if (status != 0) goto CLEANUP; /* Add trusted certs to database */ status = C_InsertCertList (P7_PATH_CTX.database, P7_PATH_CTX.trustedCerts); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\n"); CLEANUP: if (status != 0) RSA_PrintError ("InitializeGlobalPathCtx", status); return status; } /* end InitializeGlobalPathCtx */ static void DestroyGlobalPathCtx () { C_DestroyListObject (&P7_PATH_CTX.trustedCerts); C_DestroyListObject (&P7_PATH_CTX.policies); C_UnbindService (&P7_PATH_CTX.database); } /* end DestroyGlobalPathCtx */ int main (int argc, char *argv[]) { int status = 0; char command[RSA_DEMO_MAX_LINE_LEN]; CERTC_CTX ctx = NULL; SERVICE_HANDLER spTable[SP_COUNT] = { {SPT_DATABASE, RSA_DEMO_DATABASE_NAME, S_InitializeMemoryDB}, {SPT_CRYPTO, "BSAFE Crypto-C", S_InitializeDefaultCSP}, {SPT_CERT_PATH, "Path Provider", S_InitializePKIXPath} }; POINTER spParams[SP_COUNT] = { NULL, 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; /* 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 = InitializeGlobalPathCtx (ctx); if (status != 0) goto CLEANUP; RSA_PrintMessage ("PKCS #7 Message Objects\n"); RSA_PrintMessage ("=======================\n"); for (;;) { RSA_PrintMessage ("\nPKCS #7 Object Operations\n"); RSA_PrintMessage (" A - Pull apart a PKCS #7 message\n"); RSA_PrintMessage (" B - Create PKCS #7 data message\n"); RSA_PrintMessage (" C - Create PKCS #7 signed data message\n"); RSA_PrintMessage (" D - Create PKCS #7 enveloped data message\n"); RSA_PrintMessage (" E - Create PKCS #7 digested data message\n"); RSA_PrintMessage (" F - Create PKCS #7 encrypted data 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 = DecomposePkcs7 (ctx); break; case 'b': case 'B': status = WriteDataMsg (ctx); break; case 'c': case 'C': status = WriteSignedDataMsg (ctx); break; case 'd': case 'D': status = WriteEnvelopedDataMsg (ctx); break; case 'e': case 'E': status = WriteDigestedDataMsg (ctx); break; case 'f': case 'F': status = WriteEncryptedDataMsg (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 ("datamsg", status); DestroyGlobalPathCtx (); C_FinalizeCertC (&ctx); return status; } /* end main */ static int DecomposePkcs7 (CERTC_CTX ctx) { int status = 0; ITEM fileData = {NULL, 0}, pkcs7Oid = {NULL, 0}, outputData = {NULL, 0}; status = RSA_GetFileToAllocBuffer (&fileData.data, &fileData.len, "Enter name of file containing PKCS #7 message\n(blank to cancel)"); if (status != 0) goto CLEANUP; status = C_ReadMessageType (ctx, &fileData, &pkcs7Oid); if (status != 0) goto CLEANUP; for (;;) { if (fileData.len == 0) break; /* no more data... */ if ((pkcs7Oid.len == CT_ID_DATA_LEN) && (T_memcmp (pkcs7Oid.data, CT_ID_DATA, CT_ID_DATA_LEN) == 0)) status = DecomposeDataMsg (ctx, &fileData, &pkcs7Oid, &outputData); else if ((pkcs7Oid.len == CT_ID_SIGNED_DATA_LEN) && (T_memcmp (pkcs7Oid.data, CT_ID_SIGNED_DATA, CT_ID_SIGNED_DATA_LEN) == 0)) status = DecomposeSignedDataMsg (ctx, &fileData, &pkcs7Oid, &outputData); else if ((pkcs7Oid.len == CT_ID_ENVELOPED_DATA_LEN) && (T_memcmp (pkcs7Oid.data, CT_ID_ENVELOPED_DATA, CT_ID_ENVELOPED_DATA_LEN) == 0)) status = DecomposeEnvelopedDataMsg (ctx, &fileData, &pkcs7Oid, &outputData); else if ((pkcs7Oid.len == CT_ID_DIGESTED_DATA_LEN) && (T_memcmp (pkcs7Oid.data, CT_ID_DIGESTED_DATA, CT_ID_DIGESTED_DATA_LEN) == 0)) status = DecomposeDigestedDataMsg (ctx, &fileData, &pkcs7Oid, &outputData); else if ((pkcs7Oid.len == CT_ID_ENCRYPTED_DATA_LEN) && (T_memcmp (pkcs7Oid.data, CT_ID_ENCRYPTED_DATA, CT_ID_ENCRYPTED_DATA_LEN) == 0)) status = DecomposeEncryptedDataMsg (ctx, &fileData, &pkcs7Oid, &outputData); else { status = 0; break; } if (status != 0) break; /* loop around to see if there is another contentInfo contained in the * data that was just extracted */ T_free (fileData.data); fileData.data = outputData.data; fileData.len = outputData.len; outputData.data = NULL; outputData.len = 0; } if (status != 0) goto CLEANUP; RSA_PrintMessage ("No more PKCS #7 ContentInfos to decode.\nExiting.\n"); CLEANUP: if (status != 0) RSA_PrintError ("ReadPkcs7", status); T_free (outputData.data); T_free (fileData.data); return status; } /* end DecomposePkcs7 */ static int DecomposeDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents) { int status = 0; oid->data = NULL; oid->len = 0; contents->data = NULL; contents->len = 0; RSA_PrintMessage ("Reading PKCS #7 Data message...\n"); status = C_ReadDataMsg (ctx, dataMsg, contents); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (contents->data, contents->len, "Enter name of file to store extracted data"); if (status != 0 && status != RSA_DEMO_E_CANCEL) goto CLEANUP; /* check if any other contentInfos are inside */ status = C_ReadMessageType (ctx, contents, oid); if (status != 0) { oid->data = NULL; oid->len = 0; status = 0; /* it's okay to have no more content */ goto CLEANUP; } CLEANUP: if (status != 0) { T_free (contents->data); contents->data = NULL; contents->len = 0; oid->data = NULL; oid->len = 0; RSA_PrintError ("DecomposeDataMsg", status); } return status; } /* end DecomposeDataMsg */ static int DecomposeSignedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents) { int status = 0; unsigned int i = 0; UINT4 cmsOptions = CMSF_NONE; SERVICE database = NULL; LIST_OBJ certs = NULL, crls = NULL; LIST_OBJ verifiedSigners = NULL, untrustedSigners = NULL; CERT_OBJ certObj = NULL; unsigned char *certBer = NULL; unsigned int certBerLen = 0, certCount = 0; CRL_OBJ crlObj = NULL; unsigned char *crlBer = NULL; unsigned int crlBerLen = 0, crlCount = 0; SIGNER_INFO *signerInfo = NULL; unsigned int signerInfoCount = 0; ITEM externMsg = {NULL, 0}; oid->data = NULL; oid->len = 0; contents->data = NULL; contents->len = 0; RSA_PrintMessage ("Reading PKCS #7 Signed Data message...\n"); status = C_CreateListObject (&certs); if (status != 0) goto CLEANUP; status = C_CreateListObject (&crls); if (status != 0) goto CLEANUP; status = C_CreateListObject (&verifiedSigners); if (status != 0) goto CLEANUP; status = C_CreateListObject (&untrustedSigners); if (status != 0) goto CLEANUP; status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &database); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Optionally supply any additional certs needed to verify"); RSA_PrintMessage (" the message.\n"); status = RSA_AddCertsToDbPrompt (ctx, database); if (status != 0) goto CLEANUP; /* If we are dealing with a detached signature, setting the cmsOptions * argument to C_ReadSignedDataMsg with CMSF_EXTERNAL_SIGNATURE will cause * the data argument to be treated as an input parameter. */ RSA_PrintMessage ("Enter name of PKCS #7 binary if this is an external "); status = RSA_GetFileToAllocBuffer (&externMsg.data, &externMsg.len, "signature\n(blank otherwise)"); if (status != 0 && status != RSA_DEMO_E_CANCEL) goto CLEANUP; if (externMsg.len != 0) { /* we have an external signature */ cmsOptions |= CMSF_EXTERNAL_SIGNATURE; contents->data = externMsg.data; contents->len = externMsg.len; } RSA_PrintMessage ("Setting CMSF_VERIFY_SIGNER_CERTS...\n"); status = C_ReadSignedDataMsg (ctx, &P7_PATH_CTX, database, dataMsg, cmsOptions | CMSF_VERIFY_SIGNER_CERTS, contents, oid, certs, crls, verifiedSigners, untrustedSigners); if (status != 0) { RSA_PrintMessage ("Could not verify signer certs. (status = 0x%x)\n", status); RSA_PrintMessage ("Try again without CMSF_VERIFY_SIGNER_CERTS...\n"); C_ResetListObject (certs); C_ResetListObject (crls); C_ResetListObject (verifiedSigners); C_ResetListObject (untrustedSigners); status = C_ReadSignedDataMsg (ctx, &P7_PATH_CTX, database, dataMsg, cmsOptions, contents, oid, certs, crls, verifiedSigners, untrustedSigners); if (status == E_NOT_FOUND) RSA_PrintMessage ("Could not find signer's cert to verify signature.\n"); else if (status == E_VERIFY_ASN_SIGNATURE) RSA_PrintMessage ("Could not verify signature.\n"); if (status != 0) goto CLEANUP; } if (externMsg.len != 0) { /* if we had an external signature, reset contents for caller */ contents->data = NULL; contents->len = 0; } status = C_GetListObjectCount (certs, &certCount); if (status != 0) goto CLEANUP; RSA_PrintMessage ("The PKCS #7 message contains %u certificates.\n", certCount); for (i = 0; i < certCount; i++) { status = C_GetListObjectEntry (certs, i, (POINTER *)&certObj); if (status != 0) goto CLEANUP; status = C_GetCertDER (certObj, &certBer, &certBerLen); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (certBer, certBerLen, "Enter name of file to store cert binary"); if (status != 0 && status != RSA_DEMO_E_CANCEL) goto CLEANUP; } status = C_GetListObjectCount (crls, &crlCount); if (status != 0) goto CLEANUP; RSA_PrintMessage ("The PKCS #7 message contains %u CRLs.\n", crlCount); for (i = 0; i < crlCount; i++) { status = C_GetListObjectEntry (crls, i, (POINTER *)&crlObj); if (status != 0) goto CLEANUP; status = C_GetCRLDER (crlObj, &crlBer, &crlBerLen); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (crlBer, crlBerLen, "Enter name of file to store CRL binary"); if (status != 0 && status != RSA_DEMO_E_CANCEL) goto CLEANUP; } status = C_GetListObjectCount (verifiedSigners, &signerInfoCount); if (status != 0) goto CLEANUP; RSA_PrintMessage ("There are %u trusted signers.\n", signerInfoCount); for (i = 0; i < signerInfoCount; i++) { status = C_GetListObjectEntry (verifiedSigners, i, (POINTER *)&signerInfo); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\n--Trusted Signer #%u\n", i+1); status = RSA_PrintCertId (signerInfo->signerCertId); if (status != 0) goto CLEANUP; } status = C_GetListObjectCount (untrustedSigners, &signerInfoCount); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nThere are %u untrusted signers.\n", signerInfoCount); for (i = 0; i < signerInfoCount; i++) { status = C_GetListObjectEntry (untrustedSigners, i, (POINTER *)&signerInfo); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\n--Untrusted Signer #%u\n", i+1); status = RSA_PrintCertId (signerInfo->signerCertId); if (status != 0) goto CLEANUP; } status = RSA_WriteDataToFile (contents->data, contents->len, "\nEnter name of file to store extracted data"); if (status == RSA_DEMO_E_CANCEL) status = 0; /* it isn't required */ CLEANUP: if (status != 0) { /* contents->data is written if we don't have an external signature */ if (externMsg.len == 0) T_free (contents->data); contents->data = NULL; contents->len = 0; oid->data = NULL; oid->len = 0; RSA_PrintError ("DecomposeSignedDataMsg", status); } T_free (externMsg.data); C_UnbindService (&database); C_DestroyListObject (&certs); C_DestroyListObject (&crls); C_DestroyListObject (&verifiedSigners); C_DestroyListObject (&untrustedSigners); return status; } /* end DecomposeSignedDataMsg */ static int DecomposeEnvelopedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents) { int status = 0; char *nameString = NULL; SERVICE database = NULL; B_KEY_OBJ recipientPvtKey = NULL; CERT_OBJ recipientCert = NULL; RECIPIENT_INFO recipientInfo; oid->data = NULL; oid->len = 0; contents->data = NULL; contents->len = 0; RSA_PrintMessage ("Reading PKCS #7 Enveloped Data message...\n\n"); RSA_PrintMessage ("Supply the recipient's certificate and private key to "); RSA_PrintMessage ("open the enveloped\nmessage. "); status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &database); if (status != 0) goto CLEANUP; 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; status = C_ReadEnvelopedDataMsg (ctx, database, dataMsg, contents, oid, &recipientInfo, NULL, NULL, NULL, NULL); if (status != 0) goto CLEANUP; status = C_GetNameString (recipientInfo.info.keyTrans.recipCertId.id.issuerSerialNumber. issuerName, &nameString); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Recipient Info:\nIssuer = %s\n", nameString); RSA_PrintBuf ("Serial Number", recipientInfo.info.keyTrans.recipCertId. id.issuerSerialNumber.serialNumber.data, recipientInfo. info.keyTrans.recipCertId.id.issuerSerialNumber. serialNumber.len); status = RSA_WriteDataToFile (contents->data, contents->len, "Enter name of file to store decrypted message"); if (status == RSA_DEMO_E_CANCEL) status = 0; /* it isn't required */ CLEANUP: if (status != 0) RSA_PrintError ("DecomposeEnvelopedDataMsg", status); C_UnbindService (&database); C_FreeRecipientInfo (&recipientInfo); C_DestroyCertObject (&recipientCert); B_DestroyKeyObject (&recipientPvtKey); return status; } /* end DecomposeEnvelopedDataMsg */ static int DecomposeDigestedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents) { int status = 0; oid->data = NULL; oid->len = 0; contents->data = NULL; contents->len = 0; RSA_PrintMessage ("Reading PKCS #7 Digested Data message...\n"); status = C_ReadDigestedDataMsg (ctx, dataMsg, contents, oid); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (contents->data, contents->len, "Enter name of file to store extracted data"); if (status == RSA_DEMO_E_CANCEL) status = 0; /* it isn't required */ CLEANUP: if (status != 0) { T_free (contents->data); contents->data = NULL; contents->len = 0; oid->data = NULL; oid->len = 0; RSA_PrintError ("DecomposeDigestedDataMsg", status); } return status; } /* end DecomposeDigestedDataMsg */ static int DecomposeEncryptedDataMsg (CERTC_CTX ctx, ITEM *dataMsg, ITEM *oid, ITEM *contents) { int status = 0; ITEM keyData = {NULL, 0}; B_KEY_OBJ symmetricKey = NULL; oid->data = NULL; oid->len = 0; contents->data = NULL; contents->len = 0; RSA_PrintMessage ("Reading PKCS #7 Encrypted Data message...\n\n"); RSA_PrintMessage ("For this type of message, it is assumed that the "); RSA_PrintMessage ("recipient has access to the\nsymmetric decryption key."); RSA_PrintMessage (" Otherwise, a PKCS #7 enveloped message should be\n"); RSA_PrintMessage ("used.\n\n"); status = RSA_GetFileToAllocBuffer (&keyData.data, &keyData.len, "Enter name of file containing symmetric key bytes"); if (status != 0) goto CLEANUP; RSA_PrintBuf ("Key Bytes", keyData.data, keyData.len); status = B_CreateKeyObject (&symmetricKey); if (status != 0) goto CLEANUP; status = B_SetKeyInfo (symmetricKey, KI_Item, (POINTER)&keyData); if (status != 0) goto CLEANUP; status = C_ReadEncryptedDataMsg (ctx, dataMsg, symmetricKey, contents, oid, NULL); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (contents->data, contents->len, "Enter name of file to store decrypted data"); if (status == RSA_DEMO_E_CANCEL) status = 0; /* it isn't required */ CLEANUP: if (status != 0) { T_free (contents->data); contents->data = NULL; contents->len = 0; oid->data = NULL; oid->len = 0; RSA_PrintError ("DecomposeEncryptedDataMsg", status); } T_memset (keyData.data, 0, keyData.len); T_free (keyData.data); B_DestroyKeyObject (&symmetricKey); return status; } /* end DecomposeEncryptedDataMsg */ static int WriteDataMsg (CERTC_CTX ctx) { int status = 0; ITEM fileData = {NULL, 0}, dataMsg = {NULL, 0}; RSA_PrintMessage ("Enter name of file to encapsulate in data message\n"); RSA_PrintMessage ("(blank for empty message): "); status = RSA_GetFileToAllocBuffer (&fileData.data, &fileData.len, NULL); if (status == RSA_DEMO_E_CANCEL) status = 0; /* blank message, leave fileData empty */ else if (status != 0) goto CLEANUP; status = C_WriteDataMsg (ctx, &fileData, &dataMsg); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (dataMsg.data, dataMsg.len, "Enter name of file to store PKCS #7 data message"); CLEANUP: if (status != 0) RSA_PrintError ("WriteDataMsg", status); T_free (fileData.data); T_free (dataMsg.data); return status; } /* end WriteDataMsg */ static int WriteSignedDataMsg (CERTC_CTX ctx) { int status = 0; char command[RSA_DEMO_MAX_LINE_LEN]; ITEM fileData = {NULL, 0}, signedDataMsg = {NULL, 0}; SERVICE database = NULL; LIST_OBJ signerInfos = NULL, certs = NULL, crls = NULL; UINT4 cmsOptions = CMSF_NONE; unsigned char *certBer = NULL, *crlBer = NULL; unsigned int certBerLen = 0, crlBerLen = 0; CERT_OBJ certObj = NULL; CRL_OBJ crlObj = NULL; RSA_PrintMessage ("\nFor a certs-only message, or CRL-only message, it is "); RSA_PrintMessage ("not necessary to\nspecify signers. The file supplied "); RSA_PrintMessage ("here must be a BER-encoded PKCS #7\nContentInfo. In "); RSA_PrintMessage ("the case of a certs-only or CRLs-only message, a blank"); RSA_PrintMessage ("\nPKCS #7 Data message should be given. The most "); RSA_PrintMessage ("common scenario is for a\ntext message to be "); RSA_PrintMessage ("encapsulated in a PKCS #7 Data message first (option B"); RSA_PrintMessage ("\nin previous menu), then passed here to be signed.\n\n"); status = RSA_GetFileToAllocBuffer (&fileData.data, &fileData.len, "Enter name of PKCS #7 binary to sign (blank to cancel)"); if (status != 0) goto CLEANUP; status = RSA_GetRequiredCommand (command, sizeof (command), "Create a detached signature? (y/n)"); if (status != 0) goto CLEANUP; if (command[0] == 'y') cmsOptions |= CMSF_EXTERNAL_SIGNATURE; status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &database); if (status != 0) goto CLEANUP; status = C_CreateListObject (&signerInfos); if (status != 0) goto CLEANUP; status = GetSignerInfoList (ctx, database, signerInfos); if (status != 0) goto CLEANUP; status = C_CreateListObject (&certs); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOptionally supply certs for signed data message.\n"); for (;;) { status = RSA_GetFileToAllocBuffer (&certBer, &certBerLen, "Enter name of certificate binary (blank when finished)"); if (status == RSA_DEMO_E_CANCEL) break; else if (status != 0) goto CLEANUP; status = C_CreateCertObject (&certObj, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (certObj, certBer, certBerLen); if (status != 0) goto CLEANUP; status = C_AddCertToList (certs, certObj, NULL); if (status != 0) goto CLEANUP; T_free (certBer); certBer = NULL; C_DestroyCertObject (&certObj); } status = C_CreateListObject (&crls); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOptionally supply CRLs for signed data message.\n"); for (;;) { status = RSA_GetFileToAllocBuffer (&crlBer, &crlBerLen, "Enter name of CRL binary (blank when finished)"); if (status == RSA_DEMO_E_CANCEL) break; else if (status != 0) goto CLEANUP; status = C_CreateCRLObject (&crlObj, ctx); if (status != 0) goto CLEANUP; status = C_SetCRLBER (crlObj, crlBer, crlBerLen); if (status != 0) goto CLEANUP; status = C_AddCRLToList (crls, crlObj, NULL); if (status != 0) goto CLEANUP; T_free (crlBer); crlBer = NULL; C_DestroyCRLObject (&crlObj); } status = C_WriteSignedDataMsg (ctx, NULL, database, &fileData, cmsOptions, certs, crls, signerInfos, &signedDataMsg); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (signedDataMsg.data, signedDataMsg.len, "Enter name of file to store PKCS #7 signed data message"); CLEANUP: if (status != 0) RSA_PrintError ("WriteSignedDataMsg", status); T_free (fileData.data); T_free (certBer); T_free (signedDataMsg.data); C_DestroyCertObject (&certObj); C_DestroyCRLObject (&crlObj); C_DestroyListObject (&signerInfos); C_DestroyListObject (&certs); C_DestroyListObject (&crls); C_UnbindService (&database); /* note that nothing has been deleted from the database... */ return status; } /* end WriteSignedDataMsg */ static int WriteEnvelopedDataMsg (CERTC_CTX ctx) { int status = 0; ITEM fileData = {NULL, 0}, envelopedData = {NULL, 0}; SERVICE database = NULL; LIST_OBJ recipientInfos = NULL; ALGORITHM_IDENTIFIER contentEncryptionAlgId = {0, NULL}; int (*DestroyAlgParams) (POINTER *bufPtr) = NULL; RSA_PrintMessage ("\nThe file optionally supplied here must be a BER-"); RSA_PrintMessage ("encoded PKCS #7 ContentInfo.\nThe most common scenario "); RSA_PrintMessage ("is for a text message to be encapsulated in a PKCS #7\n"); RSA_PrintMessage ("Data message first (option B in previous menu), then "); RSA_PrintMessage ("passed here to be\nenveloped.\n\n"); status = RSA_GetFileToAllocBuffer (&fileData.data, &fileData.len, "Enter name of PKCS #7 binary to envelope (blank to cancel)"); if (status != 0) goto CLEANUP; status = C_BindService (ctx, SPT_DATABASE, RSA_DEMO_DATABASE_NAME, &database); if (status != 0) goto CLEANUP; status = C_CreateListObject (&recipientInfos); if (status != 0) goto CLEANUP; /* Get the certs of the recipients into the database */ status = GetRecipientInfoList (ctx, database, recipientInfos); if (status != 0) goto CLEANUP; status = RSA_ChooseSymEncAlgPrompt (ctx, &contentEncryptionAlgId, &DestroyAlgParams); if (status != 0) goto CLEANUP; /* If you want to use a particular symmetric key, a B_KEY_OBJ which has * been created and set with the appropriate key information can be passed * in as the sixth argument. On the other hand, if you want to have Cert-C * create a key for you, you can use the sixth argument to retrieve the key * that was used to encrypt the data message. Declare a B_KEY_OBJ (don't * call B_CreateKeyObject), be sure that it is set to NULL and pass it in * as the sixth argument. Following this call, that B_KEY_OBJ should be * treated as a read-only argument which can be used with B_GetKeyInfo and * KI_Item. Here, we let Cert-C generate the key and don't want the key * returned in the clear, so we just pass in NULL for the sixth argument. */ status = C_WriteEnvelopedDataMsg (ctx, database, &fileData, &contentEncryptionAlgId, recipientInfos, NULL, NULL, NULL, &envelopedData); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (envelopedData.data, envelopedData.len, "Enter name of file to store enveloped data"); CLEANUP: if (status != 0) RSA_PrintError ("WriteEnvelopedDataMsg", status); if (DestroyAlgParams) (*DestroyAlgParams) (&contentEncryptionAlgId.algorithmParam); T_free (fileData.data); T_free (envelopedData.data); C_DestroyListObject (&recipientInfos); C_UnbindService (&database); return status; } /* end WriteEnvelopedDataMsg */ static int WriteDigestedDataMsg (CERTC_CTX ctx) { int status = 0; ITEM fileData = {NULL, 0}, digestedData = {NULL, 0}; ALGORITHM_IDENTIFIER digestAlgId = {0, NULL}; RSA_PrintMessage ("\nThe file optionally supplied here must be a BER-"); RSA_PrintMessage ("encoded PKCS #7 ContentInfo.\nThe most common scenario "); RSA_PrintMessage ("is for a text message to be encapsulated in a PKCS #7\n"); RSA_PrintMessage ("Data message first (option B in previous menu), then "); RSA_PrintMessage ("passed here to be\ndigested.\n\n"); status = RSA_GetFileToAllocBuffer (&fileData.data, &fileData.len, "Enter name of PKCS #7 binary to digest (blank to cancel)"); if (status != 0) goto CLEANUP; status = RSA_ChooseDigestAlgorithmPrompt (&digestAlgId); if (status != 0) goto CLEANUP; status = C_WriteDigestedDataMsg (ctx, &fileData, digestAlgId.algorithmId, &digestedData); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (digestedData.data, digestedData.len, "Enter name of file to store digested data"); CLEANUP: if (status != 0) RSA_PrintError ("WriteDigestedDataMsg", status); T_free (fileData.data); T_free (digestedData.data); return status; } /* end WriteDigestedDataMsg */ static int WriteEncryptedDataMsg (CERTC_CTX ctx) { int status = 0; ITEM fileData = {NULL, 0}, encryptedData = {NULL, 0}; ALGORITHM_IDENTIFIER encryptionAlgId = {0, NULL}; B_KEY_OBJ encryptionKey = NULL; ITEM *keyInfo = NULL; int (*destroyAlgParams) (POINTER *algIdParams) = NULL; RSA_PrintMessage ("\nThe file optionally supplied here must be a BER-"); RSA_PrintMessage ("encoded PKCS #7 ContentInfo.\nThe most common scenario "); RSA_PrintMessage ("is for a text message to be encapsulated in a PKCS #7\n"); RSA_PrintMessage ("Data message first (option B in previous menu), then "); RSA_PrintMessage ("passed here to be\ndigested.\n\n"); status = RSA_GetFileToAllocBuffer (&fileData.data, &fileData.len, "Enter name of PKCS #7 binary to encrypt (blank to cancel)"); if (status != 0) goto CLEANUP; status = RSA_ChooseSymEncAlgPrompt (ctx, &encryptionAlgId, &destroyAlgParams); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nFor this type of PKCS #7 message, it is assumed that "); RSA_PrintMessage ("the symmetric key will\nbe accessible during "); RSA_PrintMessage ("decryption. Whether it is encrypted (digital envelope"); RSA_PrintMessage ("\nidea) and sent to another party, or stored securely "); RSA_PrintMessage ("somewhere is up to the\napplication. Here, for sample "); RSA_PrintMessage ("purposes, we generate a random symmetric key\nand save "); RSA_PrintMessage ("it to a file in the clear, making it available to "); RSA_PrintMessage ("decrypt this\nPKCS #7 EncryptedData message.\n\n"); status = RSA_GenerateSymmetricKey (ctx, encryptionAlgId, &encryptionKey); if (status != 0) goto CLEANUP; status = B_GetKeyInfo ((POINTER *)&keyInfo, encryptionKey, KI_Item); if (status != 0) goto CLEANUP; RSA_PrintBuf ("Symmetric key", keyInfo->data, keyInfo->len); status = RSA_WriteDataToFile (keyInfo->data, keyInfo->len, "Enter name of file to store symmetric key bytes"); if (status != 0) goto CLEANUP; status = C_WriteEncryptedDataMsg (ctx, &fileData, &encryptionAlgId, encryptionKey, NULL, &encryptedData); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (encryptedData.data, encryptedData.len, "Enter name of file to store PKCS #7 encrypted data message"); CLEANUP: if (status != 0) RSA_PrintError ("WriteEncryptedDataMsg", status); if (destroyAlgParams) (*destroyAlgParams) (&encryptionAlgId.algorithmParam); T_free (fileData.data); T_free (encryptedData.data); B_DestroyKeyObject (&encryptionKey); return status; } /* end WriteEncryptedDataMsg */ static int GetSignerInfoList (CERTC_CTX ctx, SERVICE database, LIST_OBJ signerInfos) { int status = 0, i = 0, signerInfoCount = 0; CERT_OBJ signerCert = NULL; B_KEY_OBJ signerPvtKey = NULL; CERT_FIELDS certFields; SIGNER_INFO signerInfo; unsigned char *signedAttribBer = NULL, *unsignedAttribBer = NULL; unsigned int signedAttribBerLen = 0, unsignedAttribBerLen = 0; signerInfo.signedAttributes = NULL; signerInfo.unsignedAttributes = NULL; status = RSA_GetInteger (&signerInfoCount, "Enter number of signers (blank to omit)"); if (status == RSA_DEMO_E_CANCEL) { status = 0; signerInfoCount = 0; } else if (status != 0) goto CLEANUP; for (i = 0; i < signerInfoCount; i++) { RSA_PrintMessage ("--Info for Signer #%i\n", i+1); status = C_CreateCertObject (&signerCert, ctx); if (status != 0) goto CLEANUP; status = B_CreateKeyObject (&signerPvtKey); if (status != 0) goto CLEANUP; /* prompt for the cert and private key */ status = RSA_GetCertAndPvtKey (ctx, signerCert, signerPvtKey); if (status != 0) goto CLEANUP; status = C_InsertCert (database, signerCert); 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; status = C_InsertPrivateKey (database, signerCert, signerPvtKey); if (status != 0) goto CLEANUP; /* 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, &signedAttribBerLen, 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, signedAttribBerLen); 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, &unsignedAttribBerLen, 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, unsignedAttribBerLen); if (status != 0) goto CLEANUP; } /* Other possibilities are C_AddUniqueSignerToList and * C_InsertSignerInList. */ status = C_AddSignerToList (signerInfos, &signerInfo, NULL); if (status != 0) goto CLEANUP; T_free (signedAttribBer); signedAttribBer = NULL; /* to avoid multiple frees in case of an error */ T_free (unsignedAttribBer); unsignedAttribBer = NULL; C_DestroyCertObject (&signerCert); B_DestroyKeyObject (&signerPvtKey); C_DestroyAttributesObject (&signerInfo.signedAttributes); C_DestroyAttributesObject (&signerInfo.unsignedAttributes); } RSA_PrintMessage ("\nFinished collecting Signer Information\n"); CLEANUP: if (status != 0) RSA_PrintError ("GetSignerInfoList", status); T_free (signedAttribBer); T_free (unsignedAttribBer); C_DestroyCertObject (&signerCert); B_DestroyKeyObject (&signerPvtKey); C_DestroyAttributesObject (&signerInfo.signedAttributes); C_DestroyAttributesObject (&signerInfo.unsignedAttributes); return status; } /* end GetSignerInfoList */ static int GetRecipientInfoList (CERTC_CTX ctx, SERVICE database, LIST_OBJ recipientInfos) { 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); 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 GetRecipientInfoList */