| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: cm_env_sm.c,v 1.22 2005/02/08 22:55:30 jmckee Exp $ */ /* * Copyright (C) 1998-2003 RSA Security Inc. * * This file shall only be used to demonstrate how to interface to an * RSA Security Inc. licensed development product. * * You have a royalty-free right to use, reproduce and distribute this * demonstration file, provided that you agree that RSA Security Inc. * has no warranty, implied or otherwise, or liability for this * demonstration file (including any modified version). This software * is provided "as is" without warranties or representations of any * kind. RSA Security disclaims all conditions and warranties, statutory * and otherwise, both express and implied, with respect to the software, * its quality and performance, including but not limited to, all * implied warranties of merchantability, fitness for a particular * purpose, title and noninfringement of third party rights. Without * limiting the foregoing, RSA Security does not warrant that the * software is error-free or that errors in the product will be * corrected. You agree that RSA Security shall not be liable for any * direct, indirect, incidental, special, consequential, punitive or * other damages whatsoever resulting from your use of this software * or any modified version. * * */ /* * @file cm_env_sm.c * This sample demonstrates PKCS #7 enveloped data operations, and contains * the following steps: * * - Creates a PKCS #7 enveloped data message. * - Writes the enveloped message to a file. * - Reads the enveloped data message back from file into an R_CM structure. * - Decrypts the data message. * - Prints various data fields from the enveloped data message. * - Finally prints the actual data. * * This is a self-contained program and therefore requires no command line * parameters. The data to envelope is defined as a text string. */ #include "r_prod.h" #include "cm_com.h" #include "cm_sm.h" /* * Main sample program entry point * * @param argc [In] The number of arguments typed on the command line. * @param argv [In] The array of individual arguments from the command line. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. */ int main(int argc, char **argv) { int ret = R_ERROR_NONE; BIO *bio_err = NULL; BIO *bio_out = NULL; BIO *bio_file = NULL; R_RES_LIST *res_list = NULL; R_LIB_CTX *lib_ctx = NULL; R_CM_CTX *ctx = NULL; R_CM *obj = NULL; R_CM *data_obj = NULL; R_CERT_CTX *cert_ctx = NULL; R_CERT *cert = NULL; R_PKEY_CTX *pkey_ctx = NULL; R_PKEY *pkey = NULL; char *cm_file; char cdata[]="Data to envelope - 0987654321"; int enc_alg; unsigned int consumed_len; int index; R_CM_ENCODING format; unsigned char buf[128]; int var; /* Set the defaults */ enc_alg = R_CR_ID_DES_CBC; cm_file = "cm_env_sm.out"; /* Create an output channel */ bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); bio_out = BIO_new_fp(stdout, BIO_NOCLOSE); if ((bio_err == NULL) || (bio_out == NULL)) { ret = R_ERROR_ALLOC_FAILURE; goto end; } BIO_set_flags(bio_err, BIO_FLAGS_FLUSH_ON_WRITE); BIO_set_flags(bio_out, BIO_FLAGS_FLUSH_ON_WRITE); /* * Retrieve the default resource list and create a library context to * provide access to all configurable aspects of the library */ /* Retrieve the default resource list */ res_list = PRODUCT_DEFAULT_RESOURCE_LIST(); /* Create a new library context */ if ((ret = PRODUCT_LIBRARY_NEW(res_list, R_RES_FLAG_DEF, &lib_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Library new failure\n"); goto end; } /* * Create the certificate, public key and cryptographic message contexts. * These contexts are required if any R_CERT_CTX *, R_PKEY_CTX * and * R_CM_CTX * routines are used. */ /* Create a new certificate context */ if ((ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_CERT_TYPE_X509, &cert_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Certificate context new failure\n"); goto end; } /* Create a new cryptographic message context */ if ((ret = R_CM_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_CM_TYPE_DEFAULT, &ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_CM_CTX_new failure\n"); goto end; } /* Create a new private key context */ if ((ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_PKEY_TYPE_RSA, &pkey_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Private key context new failure\n"); goto end; } /* * Create a certificate object. Read the binary representation of the * certificate and convert it to an R_CERT object. If the object does not * exist (for example, cert == NULL) then memory for an R_CERT object is * allocated. The public key of the certificate is used to encrypt the * enveloped data for a particular recipient. */ if ((ret = R_CERT_from_binary(cert_ctx, R_FLAG_SHARE_NONE, R_CERT_TYPE_X509, sizeof(signer_certificate), signer_certificate, &consumed_len, &cert)) != R_ERROR_NONE) { BIO_printf(bio_out, "R_CERT_from_binary failure for CA certificate\n"); goto end; } /* * Read the message data into a cryptographic message object. The data is * then in a convenient form to be included into the final cryptographic * message structure in later steps, prior to encrypting the message. */ BIO_printf(bio_out, "The Original Message Data is: %s\n", cdata); /* Parse the data from the string into the R_CM object */ if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_NONE, R_CM_TYPE_DATA, R_CM_ENCODING_FORMAT_RAW, sizeof(cdata), (unsigned char *)cdata, &consumed_len, &data_obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to read message data\n"); goto end; } /* * Create and configure the enveloped data object. The sub-steps involved * are as follows: * - Create a new enveloped data message object. * - Add recipient information. * - Include the actual data (created in the previous step). */ /* Create a new enveloped data message */ if ((ret = R_CM_new(ctx, R_CM_TYPE_ENVELOPED_DATA, &obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_CM_new failure (Enveloped Data)\n"); goto end; } /* Add the recipient information */ if ((ret = R_CM_recipient_add(obj, cert, NULL)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to add recipient\n"); goto end; } /* * This certificate is required to open the enveloped message, so the * reference count must be incremented. The R_CM is responsible for * freeing any certificates used to create the cryptographic message. */ R_CERT_inc_reference(cert); /* Set the data against the object */ if ((ret = R_CM_content_from_R_CM(obj, R_FLAG_SHARE_DATA, data_obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Setting the content failed\n"); goto end; } /* * Encrypt the data in the enveloped data message for all recipients */ /* Let the cryptographic message object generate its own symmetric key */ if ((ret = R_CM_encrypt(obj, enc_alg, R_CM_INDEX_ALL)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to encrypt\n"); goto end; } /* Output the message */ /* Open the output BIO */ if ((bio_file = BIO_new_file(cm_file, "wb")) == NULL) { BIO_printf(bio_err, "Failed to open file: %s\n", cm_file); ret = R_ERROR_ALLOC_FAILURE; goto end; } /* Select the correct formatting for the enveloped data message */ format = R_CM_ENCODING_FORMAT_WRAPPED; /* Write out the enveloped data message and free the objects to reuse */ if ((ret = R_CM_write(obj, bio_file, R_FORMAT_BINARY, NULL, format)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to write envelopedData to file\n"); goto end; } BIO_printf(bio_out, "Encrypted data successfully written to file\n"); /* The next two R_CM objects will be created later */ if (obj != NULL) { R_CM_free(obj); obj = NULL; } if (data_obj != NULL) { R_CM_free(data_obj); data_obj = NULL; } /* Close the BIO as the next read requires the file to close */ if( bio_file != NULL ) { BIO_free( bio_file ); bio_file = NULL; } /* Read the enveloped data message from file */ /* Select the correct formatting for the enveloped data message */ format = R_CM_ENCODING_FORMAT_WRAPPED; if ((ret = R_CM_read_file(ctx, cm_file, R_FORMAT_BINARY, format, &obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to read cryptographic message from file %s\n", cm_file); goto end; } BIO_printf(bio_out, "Enveloped data successfully read back from file\n"); /* * Create the key object. Read the binary representation of the private key * and convert it into an R_PKEY object. The private key is used to decrypt * the enveloped data for a particular recipient. */ if ((ret = R_PKEY_from_binary( pkey_ctx, R_PKEY_FL_DEFAULT, R_PKEY_TYPE_RSA, sizeof(signer_priv_key_data), (unsigned char *)signer_priv_key_data, &consumed_len, &pkey)) != R_ERROR_NONE) { BIO_printf(bio_out, "R_PKEY_from_binary failure - for private key\n"); goto end; } /* * Decrypt the enveloped data message. Retrieve the index of the recipient * to decrypt based on the certificate matching the private key. Note the * decryption requires that the symmetric key is decrypted with the * private key of the recipient. Then the symmetric key is used to * decrypt the encrypted message. The private key of the recipient is read * from memory below. This key is passed to R_CM_decrypt(). The certificate * (whose public key signed the symmetric key during the creation of the * enveloped data message) is used to look up the list of recipient_infos * to find the index of this particular recipient (in this simple sample * there is only one recipient). This index is passed to R_CM_decrypt() * with the corresponding private key in order to decrypt the correct * symmetric key, which in turn is used to decrypt the encrypted message * data. */ /* * Retrieve the index of this recipient. Note there is only one in this * sample. */ if ((ret = R_CM_get_index(obj, cert, &index)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to look up the recipient certificate\n"); goto end; } /* Decrypt the data */ if ((ret = R_CM_decrypt(obj, pkey, index)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to decrypt with key\n"); goto end; } BIO_printf(bio_out, "Data successfully decrypted with the key\n"); /* Print out the data and a portion of the signed data message fields */ BIO_printf(bio_out, "Print some of the data fields!\n"); /* The message type. In this sample, it is enveloped data */ if (R_CM_get_info(obj, R_CM_INFO_TYPE, &var) == R_ERROR_NONE) { R_CM_TYPE_to_string(var, sizeof(buf), (char *)buf); BIO_printf(bio_out, "Message type: %d (%s)\n", var, buf); } if (R_CM_get_info(obj, R_CM_INFO_VERSION, &var) == R_ERROR_NONE) { BIO_printf(bio_out, "Message version: %d\n", var); } /* The number of recipients. In this sample, there is one. */ var = 0; R_CM_get_info(obj, R_CM_INFO_RECIPIENT_COUNT, &var); BIO_printf(bio_out, "Recipient count: %d\n", var); /* The encryption algorithm */ if (R_CM_get_info(obj, R_CM_INFO_ENCRYPTION_ALG, &var) == R_ERROR_NONE) { if (R_CR_ID_to_string(var, sizeof(buf), (char *)buf) == R_ERROR_NONE) { BIO_printf(bio_out, "Content encryption algorithm: %d (%s)\n", var, buf); } } /* Retrieve the content, returning the decrypted message data */ if ((ret = R_CM_content_to_R_CM(obj, R_FLAG_SHARE_DATA, &data_obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Retrieve content data failure\n"); goto end; } BIO_printf(bio_out, "The decrypted data is as follows:\n"); if ((ret = R_CM_write(data_obj, bio_out, R_FORMAT_TEXT, NULL, R_CM_ENCODING_FORMAT_RAW)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to write decrypted data\n"); } end: /* * Clean up. Report errors if there is an output stream using both the * error and the string representation. Destroy the dynamically allocated * objects and return an exit code. */ if ((ret != R_ERROR_NONE) && (bio_err != NULL)) { BIO_printf(bio_err, "ERROR: (%d) %s\n", ret, R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret)); } if (obj != NULL) { R_CM_free(obj); } if (data_obj != NULL) { R_CM_free(data_obj); } if (ctx != NULL) { R_CM_CTX_free(ctx); } if (pkey != NULL) { R_PKEY_free(pkey); } if (pkey_ctx != NULL) { R_PKEY_CTX_free(pkey_ctx); } /* Free the certificate, as the reference was incremented earlier */ if (cert != NULL) { R_CERT_free(cert); } if (cert_ctx != NULL) { R_CERT_CTX_free(cert_ctx); } if (bio_file != NULL) { BIO_free(bio_file); } if (lib_ctx != NULL) { PRODUCT_LIBRARY_FREE(lib_ctx); } if (bio_out != NULL) { BIO_free(bio_out); } if (bio_err != NULL) { BIO_free(bio_err); } return(R_ERROR_EXIT_CODE(ret)); }