| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: p7stream.c,v 1.6 2004/03/02 05:18:41 gsingh Exp $ */ /* p7stream.c ** Copyright (c) 2001-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. ** ** Use Cert-C to make or read a PKCS #7 message. Since Cert-C does not have ** the capability to stream PKCS #7 messages (instead requiring that the ** entire message be present in memory to pass to one function), this sample ** attempts a workaround for this problem. We show the creation of PKCS #7 ** SignedData and EnvelopedData, as well as the verification and decryption ** of those messages. ** ** Possible scenarios we'd have to add to this testcase: ability to handle ** signed-then-enveloped or enveloped-then-signed messages, encapsulated ** signed data messages, etc. Keep in mind that this is a temporary ** workaround until PKCS #7 streaming is incorporated into Cert-C. ** ** 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). ** ** Also when compiling, define the symbol RSA_MEMUSE_WATERMARKS to enable ** memory-use sizing of the runtime environment, and logging at completion. */ #include "certc.h" #include "filelog.h" #include "demoutil.h" #include "p7util.h" #include "certutil.h" #include "imdb.h" #include "rsacsp.h" #include "pkixpath.h" #ifdef RSA_MEMUSE_WATERMARKS # include "rsamuse.h" #endif #ifdef _MSC_VER # pragma warning (disable: 171) /* invalid type conversion (often of very similar ptrs) */ #endif /* Number of bytes to read from a file at a time. */ #define RSA_FILE_BLOCK_SIZE 100 #define RSA_MAX_SIG_LEN 256 /* for a 2048 bit key */ /* Upper bound for symmetric cipher block size. */ #define RSA_MAX_BLOCK_SIZE 16 /* 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 WriteSignedDataMsg (CERTC_CTX ctx); static int WriteEnvelopedDataMsg (CERTC_CTX ctx); /* This procedure prompts the user for the signer's cert and private key. * It assembles the BER-encoded SignerInfo, which contains the signature. * For simplicity, we use SHA1 with RSA to create the signature. We also * do not use any authenticated or unauthenticated attributes (using * authenticated attributes affects the computation of the signature). * This testcase can be modified to incorporate those features. * * Note that the calling function must call T_free on signerInfoBer.data! */ static int GetSignerInfoBer (CERTC_CTX ctx, CERT_OBJ signer, ITEM *signerInfoBer); /* This procedure takes a CERT_OBJ and produces the BER-encoded * IssuerAndSerialNumber sequence. * * Note that the calling application must call T_free on issuerSerial.data. */ static int EncodeIssuerSerial (CERTC_CTX ctx, CERT_OBJ cert, ITEM *issuerSerial); /* This procedure calculates the signature of the inputFile contents and * writes out the PKCS #7 data message to the outputFile. The BER-encoded * encryptedDigest is returned in encryptedDigest. For simplicity, this * function uses SHA1 with RSA to create the signature. * * Note that the calling application must call T_free on encryptedDigest.data. */ static int GetEncryptedDigest (CERTC_CTX ctx, B_KEY_OBJ privateKey, ITEM *encryptedDigest); /* 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); /* Used by WriteEnvelopedDataMsg, this procedure uses the given secretKey and * contentEncryptionAlgId containing the symmetric algorithm and paramters * to encrypt the data in inputFile, writing the output as the * encryptedContent (OCTET STRING using implicit tag [0]). */ static int OutputEncryptedContent (CERTC_CTX ctx, FILE *inputFile, FILE *outputFile, B_KEY_OBJ secretKey, ALGORITHM_IDENTIFIER *contentEncryptionAlgId); /* 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 }; # ifdef RSA_MEMUSE_WATERMARKS T_setStackBase((POINTER)&status); # endif 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 signed data message\n"); RSA_PrintMessage (" C - Create PKCS #7 enveloped 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 = WriteSignedDataMsg (ctx); break; case 'c': case 'C': status = WriteEnvelopedDataMsg (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 ("p7stream", status); # ifdef RSA_MEMUSE_WATERMARKS { char msg[128]; (void)sprintf(msg, "Max toolkit stack/heap usage = %d/%d\n", T_getMaxStackUse(), T_getMaxHeapUse()); RSA_PrintMessage(msg); } # endif DestroyGlobalPathCtx (); C_FinalizeCertC (&ctx); return status; } /* end main */ static int WriteSignedDataMsg (CERTC_CTX ctx) { int status = 0; CERT_OBJ signer = NULL; LIST_OBJ signedData = NULL, certs = NULL, signerInfoList = NULL; ITEM signerInfoBer = {NULL, 0}, signerInfos = {NULL, 0}; ITEM certBer = {NULL, 0}, certificates = {NULL, 0}; unsigned char version[] = { 0x02, 0x01, 0x01 }; /* just SHA1 for now */ unsigned char digestAlgs[] = { 0x31, 0x0B, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00 }; /* says we're signing a PKCS #7 data */ unsigned char dataContent[] = { 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; ITEM versionItem, digestAlgsItem, dataContentItem; ITEM signedDataBer = {NULL, 0}, content = {NULL, 0}; unsigned char signedDataOid[] = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; ITEM contentType = {NULL, 0}, signedDataMsgBer = {NULL, 0}; LIST_OBJ contentInfoList = NULL; status = C_CreateCertObject (&signer, ctx); if (status != 0) goto CLEANUP; /* Let's collect the signer's cert and private key and calculate the signature. Here, we just have one signer. */ status = GetSignerInfoBer (ctx, signer, &signerInfoBer); if (status != 0) goto CLEANUP; /* Get our certificates list for the SignedData. For now, we will include the signer's cert only. */ status = C_CreateListObject (&certs); if (status != 0) goto CLEANUP; status = C_GetCertDER (signer, &certBer.data, &certBer.len); if (status != 0) goto CLEANUP; status = C_AddItemToList (certs, &certBer, NULL); if (status != 0) goto CLEANUP; /* This is our certificates [0] */ status = C_DEREncodeList (ctx, 0, VTC_CONTEXT | VTC_CONSTRUCTED, certs, &certificates.data, &certificates.len); if (status != 0) goto CLEANUP; /* No CRLs (for now, anyway) */ status = C_CreateListObject (&signerInfoList); if (status != 0) goto CLEANUP; status = C_AddItemToList (signerInfoList, &signerInfoBer, NULL); if (status != 0) goto CLEANUP; /* Put together our SignerInfos SET */ status = C_DEREncodeList (ctx, VT_SET, VTC_UNIVERSAL, signerInfoList, &signerInfos.data, &signerInfos.len); if (status != 0) goto CLEANUP; /* Now that we have all the pieces, let's assemble our SignedData */ status = C_CreateListObject (&signedData); if (status != 0) goto CLEANUP; /* Since we've stated up front that this testcase uses SHA1 with RSA only, it's simple enough to hard-code this information. A future modification could involve using C_WriteSignedDataMsg to create a "shell" using some temporary data to sign and we'd just swap out the signerInfos in the "dummy" message with the real SignerInfos that we calculate. Using a "shell" would also be good to handle the case where the info we sign isn't a PKCS #7 */ versionItem.data = version; versionItem.len = sizeof (version); digestAlgsItem.data = digestAlgs; digestAlgsItem.len = sizeof (digestAlgs); dataContentItem.data = dataContent; dataContentItem.len = sizeof (dataContent); status = C_AddItemToList (signedData, &versionItem, NULL); if (status != 0) goto CLEANUP; status = C_AddItemToList (signedData, &digestAlgsItem, NULL); if (status != 0) goto CLEANUP; status = C_AddItemToList (signedData, &dataContentItem, NULL); if (status != 0) goto CLEANUP; status = C_AddItemToList (signedData, &certificates, NULL); if (status != 0) goto CLEANUP; status = C_AddItemToList (signedData, &signerInfos, NULL); if (status != 0) goto CLEANUP; status = C_DEREncodeList (ctx, VT_SEQUENCE, VTC_UNIVERSAL, signedData, &signedDataBer.data, &signedDataBer.len); if (status != 0) goto CLEANUP; /* Now assemble the ContentInfo for our Signed Data */ status = C_CreateListObject (&contentInfoList); if (status != 0) goto CLEANUP; contentType.data = signedDataOid; contentType.len = sizeof (signedDataOid); status = C_AddItemToList (contentInfoList, &contentType, NULL); if (status != 0) goto CLEANUP; /* We need the content [0] containing the signedDataBer */ status = C_DEREncodeTagAndValue (ctx, 0, VTC_CONTEXT | VTC_CONSTRUCTED, signedDataBer.data, signedDataBer.len, 0, NULL, &content.len); if (status != 0) goto CLEANUP; content.data = T_malloc (content.len); if (content.data == NULL) { status = RSA_DEMO_E_ALLOC; goto CLEANUP; } status = C_DEREncodeTagAndValue (ctx, 0, VTC_CONTEXT | VTC_CONSTRUCTED, signedDataBer.data, signedDataBer.len, content.len, content.data, &content.len); if (status != 0) goto CLEANUP; status = C_AddItemToList (contentInfoList, &content, NULL); if (status != 0) goto CLEANUP; status = C_DEREncodeList (ctx, VT_SEQUENCE, VTC_UNIVERSAL, contentInfoList, &signedDataMsgBer.data, &signedDataMsgBer.len); if (status != 0) goto CLEANUP; /* This signed data message doesn't need to be streamed to output, since it is a detached signature */ status = RSA_WriteDataToFile (signedDataMsgBer.data, signedDataMsgBer.len, "Enter name of file to store detached signature"); CLEANUP: if (status != 0) RSA_PrintError ("WriteSignedDataMsg", status); T_free (certificates.data); T_free (content.data); T_free (signerInfoBer.data); T_free (signerInfos.data); T_free (signedDataBer.data); T_free (signedDataMsgBer.data); C_DestroyCertObject (&signer); C_DestroyListObject (&certs); C_DestroyListObject (&signedData); C_DestroyListObject (&signerInfoList); C_DestroyListObject (&contentInfoList); return status; } /* end WriteSignedDataMsg */ static int WriteEnvelopedDataMsg (CERTC_CTX ctx) { int status = 0; FILE *inputFile = NULL, *outputFile = NULL; SERVICE database = NULL; LIST_OBJ recipientInfos = NULL; ALGORITHM_IDENTIFIER contentEncryptionAlgId = {0, NULL}; B_KEY_OBJ secretKey = NULL; char *temp = "temporary arbitrary data"; ITEM sampleData = {NULL, 0}, sampleDataMsg = {NULL, 0}; ITEM sampleEnvelopedData = {NULL, 0}; int tag = 0; unsigned int tagClass = 0; ITEM *berItem = NULL, envelopedDataBer = {NULL, 0}; LIST_OBJ contentInfo = NULL, envelopedData = NULL; LIST_OBJ encryptedContentInfo = NULL; /* This header information includes the ContentInfo SEQUENCE, the enveloped data content type, the content [0] tag, the EnvelopedData SEQUENCE, the EnvelopedData version. */ unsigned char p7Header[] = { 0x30, 0x80, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x03, 0xA0, 0x80, 0x30, 0x80, 0x02, 0x01, 0x00 }; /* EncryptedContentInfo header */ unsigned char eciHeader[] = { 0x30, 0x80 }; /* Resolve the indefinite-length encoding used by the ContentInfo SEQUENCE tag, the content [0] tag, the EnvelopedData SEQUENCE, and the EncryptedContentInfo SEQUENCE. */ unsigned char p7Footer[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int (*DestroyAlgParams) (POINTER *bufPtr) = NULL; 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; /* Create a "dummy" data message, representing the type of message that we are going to envelope. This affects the value that appears in the EncryptedContentInfo's ContentType field. */ sampleData.data = (POINTER)temp; sampleData.len = T_strlen (temp); status = C_WriteDataMsg (ctx, &sampleData, &sampleDataMsg); if (status != 0) goto CLEANUP; status = B_CreateKeyObject (&secretKey); if (status != 0) goto CLEANUP; status = C_WriteEnvelopedDataMsg (ctx, database, &sampleDataMsg, &contentEncryptionAlgId, recipientInfos, secretKey, NULL, NULL, &sampleEnvelopedData); if (status != 0) goto CLEANUP; /* Obtain handles for input and output */ status = RSA_OpenDataFilePrompt (&inputFile, RSA_DEMO_READ_BINARY, "Enter name of file containing content to encrypt"); if (status != 0) goto CLEANUP; status = RSA_OpenDataFilePrompt (&outputFile, RSA_DEMO_WRITE_BINARY, "Enter name of file to store PKCS #7 enveloped data message"); if (status != 0) goto CLEANUP; /* Output everything up to the recipientInfos */ status = RSA_AppendDataToFile (outputFile, p7Header, sizeof (p7Header)); if (status != 0) goto CLEANUP; status = C_CreateListObject (&contentInfo); if (status != 0) goto CLEANUP; status = C_BERDecodeList (ctx, sampleEnvelopedData.data, sampleEnvelopedData.len, &tag, &tagClass, contentInfo); if (status != 0) goto CLEANUP; /* get pointer to content */ status = C_GetListObjectEntry (contentInfo, 1, (POINTER *)&berItem); if (status != 0) goto CLEANUP; status = C_BERDecodeTagAndValue (ctx, berItem->data, berItem->len, &tag, &tagClass, &envelopedDataBer.data, &envelopedDataBer.len); if (status != 0) goto CLEANUP; status = C_CreateListObject (&envelopedData); if (status != 0) goto CLEANUP; /* get pointer to recipientInfos and output it */ status = C_BERDecodeList (ctx, envelopedDataBer.data, envelopedDataBer.len, &tag, &tagClass, envelopedData); if (status != 0) goto CLEANUP; status = C_GetListObjectEntry (envelopedData, 1, (POINTER *)&berItem); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, berItem->data, berItem->len); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, eciHeader, sizeof (eciHeader)); if (status != 0) goto CLEANUP; /* get pointer to EncryptedContentInfo */ status = C_GetListObjectEntry (envelopedData, 2, (POINTER *)&berItem); if (status != 0) goto CLEANUP; status = C_CreateListObject (&encryptedContentInfo); if (status != 0) goto CLEANUP; status = C_BERDecodeList (ctx, berItem->data, berItem->len, &tag, &tagClass, encryptedContentInfo); if (status != 0) goto CLEANUP; /* Get EncryptedContentInfo.contentType */ status = C_GetListObjectEntry (encryptedContentInfo, 0, (POINTER *)&berItem); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, berItem->data, berItem->len); if (status != 0) goto CLEANUP; status = C_GetListObjectEntry (encryptedContentInfo, 1, (POINTER *)&berItem); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, berItem->data, berItem->len); if (status != 0) goto CLEANUP; /* Read in the data from inputFile, writing out blocks of encrypted data to outputFile */ status = OutputEncryptedContent (ctx, inputFile, outputFile, secretKey, &contentEncryptionAlgId); if (status != 0) goto CLEANUP; /* Output the final portion to resolve the indefinite-length encodings */ status = RSA_AppendDataToFile (outputFile, p7Footer, sizeof (p7Footer)); if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("WriteEnvelopedDataMsg", status); RSA_CloseDataFile (&inputFile); RSA_CloseDataFile (&outputFile); if (DestroyAlgParams) (*DestroyAlgParams) (&contentEncryptionAlgId.algorithmParam); T_free (sampleDataMsg.data); T_free (sampleEnvelopedData.data); B_DestroyKeyObject (&secretKey); C_DestroyListObject (&recipientInfos); C_DestroyListObject (&contentInfo); C_DestroyListObject (&envelopedData); C_DestroyListObject (&encryptedContentInfo); C_UnbindService (&database); return status; } /* end WriteEnvelopedDataMsg */ static int GetSignerInfoBer (CERTC_CTX ctx, CERT_OBJ signer, ITEM *signerInfoBer) { int status = 0; B_KEY_OBJ privateKey = NULL; LIST_OBJ signerInfoList = NULL; unsigned char version1[] = {0x02, 0x01, 0x01}; unsigned char sha1AlgId[] = { 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00 }; unsigned char rsaAlgId[] = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; ITEM versionItem, digestAlgId, encAlgId; ITEM issuerSerial = {NULL, 0}, encryptedDigest = {NULL, 0}; signerInfoBer->data = NULL; signerInfoBer->len = 0; status = B_CreateKeyObject (&privateKey); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply the signer's cert and private key.\n"); status = RSA_GetCertAndPvtKey (ctx, signer, privateKey); if (status != 0) goto CLEANUP; /* Use this list object to hold the contents of the SignerInfo */ status = C_CreateListObject (&signerInfoList); if (status != 0) goto CLEANUP; /* version 1 */ versionItem.data = version1; versionItem.len = sizeof (version1); status = C_AddItemToList (signerInfoList, &versionItem, NULL); if (status != 0) goto CLEANUP; /* Add issuer name and serial number to identify signer's cert */ status = EncodeIssuerSerial (ctx, signer, &issuerSerial); if (status != 0) goto CLEANUP; status = C_AddItemToList (signerInfoList, &issuerSerial, NULL); if (status != 0) goto CLEANUP; /* We could also query Crypto-C using AI_SHA1/AI_SHA1_BER to get the SHA1 algorithm ID, but for the purposes of this example, we just hard code it. */ digestAlgId.data = sha1AlgId; digestAlgId.len = sizeof (sha1AlgId); status = C_AddItemToList (signerInfoList, &digestAlgId, NULL); if (status != 0) goto CLEANUP; /* No authenticated attributes */ /* Same story for the RSA Encryption algorithm identifier */ encAlgId.data = rsaAlgId; encAlgId.len = sizeof (rsaAlgId); status = C_AddItemToList (signerInfoList, &encAlgId, NULL); if (status != 0) goto CLEANUP; /* Calculate the signature */ status = GetEncryptedDigest (ctx, privateKey, &encryptedDigest); if (status != 0) goto CLEANUP; status = C_AddItemToList (signerInfoList, &encryptedDigest, NULL); if (status != 0) goto CLEANUP; /* No unauthenticated attributes */ status = C_DEREncodeList (ctx, VT_SEQUENCE, VTC_UNIVERSAL, signerInfoList, &signerInfoBer->data, &signerInfoBer->len); CLEANUP: if (status != 0) { T_free (signerInfoBer->data); signerInfoBer->data = NULL; signerInfoBer->len = 0; RSA_PrintError ("GetSignerInfoBer", status); } T_free (issuerSerial.data); T_free (encryptedDigest.data); C_DestroyListObject (&signerInfoList); B_DestroyKeyObject (&privateKey); return status; } /* end GetSignerInfoBer */ static int EncodeIssuerSerial (CERTC_CTX ctx, CERT_OBJ cert, ITEM *issuerSerial) { int status = 0; int tag = 0; unsigned int tagClass = 0; unsigned int issuerNameIndex = 0, serialNumberIndex = 0; ITEM tbsCertBer = {NULL, 0}; LIST_OBJ tbsCert = NULL, issuerSerialList = NULL; ITEM *firstEntry = NULL, *serial = NULL, *issuerBer = NULL; issuerSerial->data = NULL; issuerSerial->len = 0; status = C_CreateListObject (&tbsCert); if (status != 0) goto CLEANUP; status = C_CreateListObject (&issuerSerialList); if (status != 0) goto CLEANUP; status = C_GetCertInnerDER (cert, &tbsCertBer.data, &tbsCertBer.len); if (status != 0) goto CLEANUP; status = C_BERDecodeList (ctx, tbsCertBer.data, tbsCertBer.len, &tag, &tagClass, tbsCert); if (status != 0) goto CLEANUP; /* Detect whether or not the version number is present. A version number is present if the first entry in the tbsCertBer has a [0] tag. */ status = C_GetListObjectEntry (tbsCert, 0, (POINTER *)&firstEntry); if (status != 0) goto CLEANUP; if (firstEntry->data[0] == 0xa0) { /* version number is present */ issuerNameIndex = 3; serialNumberIndex = 1; } else { /* version number is not present */ issuerNameIndex = 2; serialNumberIndex = 0; } status = C_GetListObjectEntry (tbsCert, issuerNameIndex, (POINTER *)&issuerBer); if (status != 0) goto CLEANUP; status = C_AddItemToList (issuerSerialList, issuerBer, NULL); if (status != 0) goto CLEANUP; status = C_GetListObjectEntry (tbsCert, serialNumberIndex, (POINTER *)&serial); if (status != 0) goto CLEANUP; status = C_AddItemToList (issuerSerialList, serial, NULL); if (status != 0) goto CLEANUP; status = C_DEREncodeList (ctx, VT_SEQUENCE, VTC_UNIVERSAL, issuerSerialList, &issuerSerial->data, &issuerSerial->len); CLEANUP: if (status != 0) { T_free (issuerSerial->data); issuerSerial->data = NULL; issuerSerial->len = 0; RSA_PrintError ("EncodeIssuerSerial", status); } C_DestroyListObject (&tbsCert); C_DestroyListObject (&issuerSerialList); return status; } /* end EncodeIssuerSerial */ static int GetEncryptedDigest (CERTC_CTX ctx, B_KEY_OBJ privateKey, ITEM *encryptedDigest) { int status = 0, finished = 0; FILE *inputFile = NULL, *outputFile = NULL; B_ALGORITHM_OBJ signObj = NULL; B_ALGORITHM_CHOOSER chooser; unsigned char dataToSign[RSA_FILE_BLOCK_SIZE], signature[RSA_MAX_SIG_LEN]; unsigned int dataToSignLen = 0, signatureLen = 0; /* The p7DataHeader consists of the SEQUENCE tag, PKCS #7 Data OID, content [0] tag, and the OCTET STRING tag. Note that we have a constructed OCTET STRING tag because we can't use indefinite-length encoding with primitives. */ unsigned char p7DataHeader[] = { 0x30, 0x80, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01, 0xA0, 0x80, 0x24, 0x80 }; /* Resolve the indefinite-length encoding used by the SEQUENCE tag, the content [0] tag, and the OCTET STRING tag. */ unsigned char p7DataFooter[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* for graceful cleanup in case of error */ encryptedDigest->data = NULL; encryptedDigest->len = 0; status = RSA_OpenDataFilePrompt (&inputFile, RSA_DEMO_READ_BINARY, "Enter name of file to sign"); if (status != 0) goto CLEANUP; RSA_PrintMessage ("In this example, we will create a detached signature."); RSA_PrintMessage (" We need to\nstore the PKCS #7 Data message "); RSA_PrintMessage ("containing the data to sign.\n"); status = RSA_OpenDataFilePrompt (&outputFile, RSA_DEMO_WRITE_BINARY, "Enter name of file to store PKCS #7 Data message"); if (status != 0) goto CLEANUP; /* output the PKCS #7 data header to begin the outputFile */ status = RSA_AppendDataToFile (outputFile, p7DataHeader, sizeof (p7DataHeader)); if (status != 0) goto CLEANUP; /* set up our algorithm object for signing */ status = B_CreateAlgorithmObject (&signObj); if (status != 0) goto CLEANUP; status = B_SetAlgorithmInfo (signObj, AI_SHA1WithRSAEncryption, NULL); if (status != 0) goto CLEANUP; status = C_GetChooser (ctx, &chooser); if (status != 0) goto CLEANUP; status = B_SignInit (signObj, privateKey, chooser, NULL); if (status != 0) goto CLEANUP; /* digest a block of data and output the contents for the PKCS #7 data until we finish going through the data to sign */ while (finished == 0) { /* something's wrong if we need more than 10 bytes to encode the octet string tag and length. */ unsigned char berTag[10]; unsigned int berTagLen; status = RSA_GetDataFromFile (inputFile, sizeof (dataToSign), dataToSign, &dataToSignLen); if (status == 0) finished = 1; /* last block */ else if (status != RSA_DEMO_E_MORE_DATA) goto CLEANUP; status = B_SignUpdate (signObj, dataToSign, dataToSignLen, NULL); if (status != 0) goto CLEANUP; /* Use C_DEREncodeTagAndValue here to get the tag and length bytes only. */ /* Remember that our PKCS #7 data contained a constructed OCTET STRING. Here, we finish what we started... */ status = C_DEREncodeTagAndValue (ctx, VT_OCTET_STRING, VTC_UNIVERSAL, NULL, dataToSignLen, sizeof (berTag), berTag, &berTagLen); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, berTag, berTagLen); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, dataToSign, dataToSignLen); if (status != 0) goto CLEANUP; } /* output the PKCS #7 data header to finish the outputFile */ status = RSA_AppendDataToFile (outputFile, p7DataFooter, sizeof (p7DataFooter)); if (status != 0) goto CLEANUP; status = B_SignFinal (signObj, signature, &signatureLen, sizeof (signature), NULL, NULL); if (status != 0) goto CLEANUP; /* the encryptedDigest is an OCTET STRING containing the signature */ status = C_DEREncodeString (ctx, VT_OCTET_STRING, VTC_UNIVERSAL, signature, signatureLen, &encryptedDigest->data, &encryptedDigest->len); CLEANUP: if (status != 0) { T_free (encryptedDigest->data); encryptedDigest->data = NULL; encryptedDigest->len = 0; RSA_PrintError ("GetEncryptedDigest", status); } B_DestroyAlgorithmObject (&signObj); RSA_CloseDataFile (&inputFile); RSA_CloseDataFile (&outputFile); return status; } /* end GetEncryptedDigest */ 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 */ static int OutputEncryptedContent (CERTC_CTX ctx, FILE *inputFile, FILE *outputFile, B_KEY_OBJ secretKey, ALGORITHM_IDENTIFIER *contentEncryptionAlgId) { int status = 0, finished = 0; B_ALGORITHM_OBJ encryptor = NULL; B_ALGORITHM_CHOOSER chooser; unsigned char dataToEncrypt[RSA_FILE_BLOCK_SIZE]; unsigned char cipherBlock[RSA_FILE_BLOCK_SIZE + RSA_MAX_BLOCK_SIZE]; unsigned int dataToEncryptLen = 0, cipherBlockLen = 0; /* EncryptedContentInfo.encryptedContent [0] */ unsigned char header[] = { 0xA0, 0x80 }; /* resolve the indefinite-length encoding used in the header */ unsigned char footer [] = { 0x00, 0x00 }; status = RSA_AppendDataToFile (outputFile, header, sizeof (header)); if (status != 0) goto CLEANUP; status = B_CreateAlgorithmObject (&encryptor); if (status != 0) goto CLEANUP; switch (contentEncryptionAlgId->algorithmId) { case EAI_DES3: status = B_SetAlgorithmInfo (encryptor, AI_DES_EDE3_CBCPadIV8, contentEncryptionAlgId->algorithmParam); break; case EAI_RC2: status = B_SetAlgorithmInfo (encryptor, AI_RC2_CBCPad, contentEncryptionAlgId->algorithmParam); break; case EAI_RC4: status = B_SetAlgorithmInfo (encryptor, AI_RC4, contentEncryptionAlgId->algorithmParam); break; case EAI_RC5: status = B_SetAlgorithmInfo (encryptor, AI_RC5_CBCPad, contentEncryptionAlgId->algorithmParam); break; case EAI_DES: status = B_SetAlgorithmInfo (encryptor, AI_DES_CBCPadIV8, contentEncryptionAlgId->algorithmParam); break; default: /* explicitly add support for other EAI_* identifiers as they become available */ status = RSA_DEMO_E_NOT_IMPLEMENTED; } if (status != 0) goto CLEANUP; status = C_GetChooser (ctx, &chooser); if (status != 0) goto CLEANUP; status = B_EncryptInit (encryptor, secretKey, chooser, NULL); if (status != 0) goto CLEANUP; /* encrypt a block of data and output the contents in blocks */ while (finished == 0) { /* something's wrong if we need more than 10 bytes to encode the octet string tag and length */ unsigned char berTag[10]; unsigned int berTagLen; status = RSA_GetDataFromFile (inputFile, sizeof (dataToEncrypt), dataToEncrypt, &dataToEncryptLen); if (status == 0) finished = 1; /* last block */ else if (status != RSA_DEMO_E_MORE_DATA) goto CLEANUP; status = B_EncryptUpdate (encryptor, cipherBlock, &cipherBlockLen, sizeof (cipherBlock), dataToEncrypt, dataToEncryptLen, NULL, NULL); if (status != 0) goto CLEANUP; /* Use C_DEREncodeTagAndValue to get the tag and lenght bytes only. Remember that these primitive OCTET STRINGS are contained in the encryptedContent constructed OCTET STRINGS... */ status = C_DEREncodeTagAndValue (ctx, VT_OCTET_STRING, VTC_UNIVERSAL, NULL, cipherBlockLen, sizeof (berTag), berTag, &berTagLen); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, berTag, berTagLen); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, cipherBlock, cipherBlockLen); if (status != 0) goto CLEANUP; if (finished == 1) { /* take care of the last block */ status = B_EncryptFinal (encryptor, cipherBlock, &cipherBlockLen, sizeof (cipherBlock), NULL, NULL); if (status != 0) goto CLEANUP; status = C_DEREncodeTagAndValue (ctx, VT_OCTET_STRING, VTC_UNIVERSAL, NULL, cipherBlockLen, sizeof (berTag), berTag, &berTagLen); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, berTag, berTagLen); if (status != 0) goto CLEANUP; status = RSA_AppendDataToFile (outputFile, cipherBlock, cipherBlockLen); if (status != 0) goto CLEANUP; } } status = RSA_AppendDataToFile (outputFile, footer, sizeof (footer)); if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("OutputEncryptedContent", status); B_DestroyAlgorithmObject (&encryptor); return status; } /* end OutputEncryptedContent */