| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: cm_open.c,v 1.48 2005/08/08 05:33:30 jlevander 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 * This sample demonstrates how to decrypt a PKCS #7 enveloped data message. * * In its simplest form this sample will decrypt the enveloped data. Use the * optional switches to print the data and/or recipient information from the * enveloped data message. * * See cm_env for how to generate PKCS #7 enveloped data messages. * * For example, to: * * Open and decrypt a PKCS #7 enveloped data message (data within message): * cm_open -cm_msg env.data -keys rec_priv.key -certs recipient.cert * * Decrypt a PKCS #7 enveloped data message (encrypted data in a separate * file): * The original data is printed along with the recipient information. * cm_open -cm_msg env.data -enc_msg data.env -keys rec_priv.key * -certs recipient.cert -print_data -print_recipients * * where: env.data = The output file where PKCS #7 data is written. * rec_priv.key = The private key of the recipient (matches the * certificate). * recipient.cert = The recipient's certificate. * data.env = The detached file containing only the encrypted * data. * */ #include "r_prod.h" #include "cm_com.h" /* Usage help message */ static char *cm_open_usage[] = { "usage: cm_open [options]\n", "where options are:\n", " -cm_msg file - File containing the cryptographic message\n", " -enc_msg file - File containing the encrypted data if it's not\n", " part of the cryptographic message\n", " -single - The cryptographic message and detached data are\n", " in the same file, the cryptographic message\n", " first followed by the detached data\n", " -wrapped - The detached message is wrapped and should be\n", " unwrapped before use\n", " -certs list - List of certificates (colon separated)\n", " -certtype encoding - Encoding of the certificates - only X509\n", " (default) supported\n", #ifdef NO_PEM " -certform format - Format of the certificates (BIN only)\n", #else " -certform format - Format of the certificates\n" " - one of BIN (default), PEM\n", #endif /* NO_PEM */ " -keys list - List of private keys matching certificates\n", " (colon separated)\n", " -keytype encoding - Encoding of the keys - one of RSA (default)\n", #ifdef NO_PEM " -keyform format - Format of the keys (BIN only)\n", #else " -keyform format - Format of the keys - one of BIN (default), PEM\n", #endif /* NO_PEM */ " -print_recipient - Print the certificate information for each of\n", " the recipients in the cryptographic message\n", " -print_data - Print the enveloped data of the cryptographic\n", " message\n", #ifdef NO_SOFTWARE_CRYPTO " -no_fips140 - Use non FIPS140 crypto implementations\n", #endif /* NO_SOFTWARE_CRYPTO */ " -eg - Example usage\n", NULL }; static char *cm_open_example_usage[] = { "Open and decrypt a PKCS #7 enveloped data message (data within message)\n", "cm_open -cm_msg env.data -keys rec_priv.key -certs recipient.cert\n", "\n", "Decrypt a PKCS #7 enveloped data message (encrypted data in a separate" "file)\n", "The original data is printed along with the recipient information\n", "cm_open -cm_msg env.data -enc_msg data.env -keys rec_priv.key\n", " -certs recipient.cert -print_data -print_recipients\n", "\n", "where: env.data = Output file where PKCS #7 data is written\n", " rec_priv.key = Private key of the recipient (matches the cert)" "\n", " recipient.cert = Recipient's certificate\n", " data.env = Detached file containing only the encrypted data" "\n", "\n", NULL }; int decrypt_recipients(BIO *bio_err, R_CERT_CTX *cert_ctx, R_PKEY_CTX *pkey_ctx, R_CM *obj, char *certs, R_CERT_TYPE certtype, R_FORMAT certform, char *keys, R_PKEY_TYPE keytype, R_FORMAT keyform); /* * 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; 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_CM *tmp_obj = NULL; R_CERT_CTX *cert_ctx = NULL; R_PKEY_CTX *pkey_ctx = NULL; R_TITEM titem = { R_FLAG_SHARE_DATA, 0, NULL }; BIO *bio_err = NULL; BIO *bio_out = NULL; BIO *bio_file = NULL; char *cm_file; char *encfile; char *out_file; char *keyfile; char *certfile; char *str; unsigned char *cm_data; unsigned char *msg_data; unsigned int cm_len; unsigned int msg_len; unsigned int num_used; R_CERT_TYPE certtype; R_PKEY_TYPE keytype; R_FORMAT certform; R_FORMAT keyform; R_CM_ENCODING format; int print_data; int print_rec; int single; int wrapped; /* Set the defaults */ cm_file = NULL; out_file = NULL; certfile = NULL; encfile = NULL; cm_data = NULL; msg_data = NULL; format = R_CM_ENCODING_FORMAT_RAW; certtype = R_CERT_TYPE_X509; certform = R_FORMAT_BINARY; keyfile = NULL; keytype = R_PKEY_TYPE_RSA; keyform = R_FORMAT_BINARY; print_data = 0; print_rec = 0; single = 0; wrapped = 0; res_list = PRODUCT_DEFAULT_RESOURCE_LIST(); /* * Create BIOs to stderr and stdout. BIOs are the Basic Input/Output * mechanism provided by RSA and are recommended for all input and output * from applications. */ 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); /* Parse the command line parameters */ /* Skip the program name */ argc--; argv++; /* Process all command line options */ while (argc >= 1) { if (Strcmp(*argv, "-cm_msg") == 0) { if (--argc < 1) { goto bad; } cm_file = *(++argv); } else if (Strcmp(*argv, "-enc_msg") == 0) { if (--argc < 1) { goto bad; } encfile = *(++argv); } else if (Strcmp(*argv, "-single") == 0) { single = 1; } else if (Strcmp(*argv, "-wrapped") == 0) { wrapped = 1; } else if (Strcmp(*argv, "-keys") == 0) { if (--argc < 1) { goto bad; } keyfile = *(++argv); } else if (Strcmp(*argv, "-keytype") == 0) { if (--argc < 1) { goto bad; } str = *(++argv); if ((ret = R_PKEY_TYPE_from_string(&keytype, str)) != R_ERROR_NONE) { BIO_printf(bio_err, "Unknown key type %s\n", str); goto bad; } } else if (Strcmp(*argv, "-keyform") == 0) { if (--argc < 1) { goto bad; } str = *(++argv); if ((ret = R_FORMAT_from_string(str, &keyform)) != R_ERROR_NONE) { BIO_printf(bio_err, "Unknown key format %s\n", str); goto bad; } } else if (Strcmp(*argv, "-certs") == 0) { if (--argc < 1) { goto bad; } certfile = *(++argv); } else if (Strcmp(*argv, "-certtype") == 0) { if (--argc < 1) { goto bad; } str = *(++argv); if ((ret = R_CERT_TYPE_from_string(str, &certtype)) != R_ERROR_NONE) { BIO_printf(bio_err, "Unknown certificate type %s\n", str); goto bad; } } else if (Strcmp(*argv, "-certform") == 0) { if (--argc < 1) { goto bad; } str = *(++argv); if ((ret = R_FORMAT_from_string(str, &certform)) != R_ERROR_NONE) { BIO_printf(bio_err, "Unknown certificate format %s\n", str); goto bad; } } else if (Strcmp(*argv, "-print_data") == 0) { print_data = 1; } else if (Strcmp(*argv, "-print_recipient") == 0) { print_rec = 1; } #ifdef NO_SOFTWARE_CRYPTO else if (Strcmp(*argv, "-no_fips140") == 0) { res_list = PRODUCT_NON_FIPS_140_MODE_RESOURCE_LIST(); } #endif /* NO_SOFTWARE_CRYPTO */ else if (Strcmp(*argv,"-eg") == 0) { char **egp; for (egp = cm_open_example_usage; (*egp) != NULL; egp++) { BIO_printf(bio_out, *egp); } goto end; } else { BIO_printf(bio_err, "Unknown option %s\n", *argv); goto bad; } argc--; argv++; } /* Simple checks first */ if (cm_file == NULL) { BIO_printf(bio_out, "Input file required\n"); goto bad; } /* Display the help menu if an invalid command line option was entered */ if (0) { char **pp; bad: for (pp = cm_open_usage; (*pp != NULL); pp++) { BIO_printf(bio_err, *pp); } goto end; } /* * Create a library context to provide access to all configurable aspects * of the library */ /* Create a new library context using the default resource list */ 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 public key, certificate and cryptographic message contexts. These * contexts are required if any R_CERT_*, R_PKEY_*, or R_CM_* routines are * used. */ if ((ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, certtype, &cert_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Certificate context new failure\n"); goto end; } /* Create a new key context */ if ((ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, keytype, &pkey_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Private key 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; } /* * Load the cryptographic message file data into a cryptographic * message object. */ /* Read the file into a buffer. The data will be in binary format. */ if ((ret = data_from_file(bio_out, cm_file, &cm_data, &cm_len)) != R_ERROR_NONE) { goto end; } /* * Do a from binary on the data to load into a cryptographic message * object */ if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_DATA, R_CM_TYPE_UNKNOWN, R_CM_ENCODING_FORMAT_WRAPPED, cm_len, cm_data, &num_used, &obj)) != R_ERROR_NONE) { BIO_printf(bio_out, "R_CM_from_binary failure\n"); goto end; } /* * Access the data to decrypt. Determine where the data resides so that, if * required, the decrypted data can be added to the cryptographic message * object before decryption. * * If the data is detached but in the same file then the data follows the * cryptographic message. Therefore, point to the data after the * cryptographic message that was read above. * * If the data is detached but in a seperate file then read the data from * the specified file. * * If the data is attached then it is inside the cryptographic message * and no further action needs to be taken. */ /* The detached message is appended to the cryptographic message file */ if (single == 1) { /* Set the data message with the remainder of the data */ msg_data = cm_data + num_used; msg_len = cm_len - num_used; } /* The detached message is stored in a separate file */ else if (encfile != NULL) { if ((ret = data_from_file(bio_out, encfile, &msg_data, &msg_len)) != R_ERROR_NONE) { goto end; } } /* * The detached encrypted data is now available. Unwrap it, if necessary, * and add it to the cryptographic message object. */ if (msg_data != NULL) { /* * The data must be detached. Therefore, if it is wrapped, unwrap * it now. */ if (wrapped != 0) { /* Do a from binary on the data in order to unwrap the data */ if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_DATA, R_CM_TYPE_UNKNOWN, R_CM_ENCODING_FORMAT_WRAPPED, msg_len, msg_data, &num_used, &tmp_obj)) != R_ERROR_NONE) { BIO_printf(bio_out, "Failed to unwrap encrypted data\n"); goto end; } if ((ret = R_CM_get_info(tmp_obj, R_CM_INFO_DATA, &titem)) != R_ERROR_NONE) { BIO_printf(bio_out, "Failed to retrieve unwrapped encrypted data\n"); goto end; } } else { titem.data = msg_data; titem.len = msg_len; } titem.type = R_FLAG_SHARE_NONE; /* Add the detached encrypted data to the R_CM object */ if ((ret = R_CM_set_info(obj, R_CM_INFO_ENC_DATA, &titem)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to set encrypted data into R_CM\n"); goto end; } } /* * Decrypt the data. Each of the certificates provided on the command line * (certfile) is searched for within the cryptographic message object. The * index of the certificate is used to retrieve the corresponding encrypted * symmetric key used to encrypt the original data. This key is decrypted * and the key is used to decrypt the message in the cryptographic message * object to produce the original data. */ if ((ret = decrypt_recipients(bio_err, cert_ctx, pkey_ctx, obj, certfile, certtype, certform, keyfile, keytype, keyform)) != R_ERROR_NONE) { goto end; } /* Display the recipient details if requested */ if (print_rec == 1) { /* * Print the enveloped data message. If print_data != 0 then the * encrypted data will also be printed to bio_out. */ if ((ret = R_CM_write(obj, bio_err, R_FORMAT_TEXT, &print_data, 0)) != R_ERROR_NONE) { BIO_printf(bio_err, "Write envelopedData data failure\n"); goto end; } } /* Print the decrypted data if requested */ if (print_data == 1) { /* Retrieve the content, returning the decrypted 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; } 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"); goto end; } } 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 (tmp_obj != NULL) { R_CM_free(tmp_obj); } if (data_obj != NULL) { R_CM_free(data_obj); } if (cm_data != NULL) { Free(cm_data); } if ((encfile != NULL) && (msg_data != NULL)) { Free(msg_data); } if (ctx != NULL) { R_CM_CTX_free(ctx); } if (pkey_ctx != NULL) { R_PKEY_CTX_free(pkey_ctx); } /* * The certificate context cannot be freed until the store has been emptied * as the certificates in the store hold a reference back to the * certificate context */ if (cert_ctx != NULL) { R_CERT_CTX_free(cert_ctx); } if (lib_ctx != NULL) { PRODUCT_LIBRARY_FREE(lib_ctx); } if (bio_file != NULL) { BIO_free(bio_file); } if (bio_out != NULL) { BIO_free(bio_out); } if (bio_err != NULL) { BIO_free(bio_err); } return(R_ERROR_EXIT_CODE(ret)); } /* * Decrypts the enveloped data message and verifies the result for each * recipient. * * @param bio_err [In] The BIO for error messages. * @param cert_ctx [In] The certificate context. * @param key_ctx [In] The key context. * @param obj [In] The object into which to add the recipients. * @param certs [In] A colon separated list of certificate files. * @param certtype [In] The certificate type. * @param certform [In] The certificate format. * @param keys [In] A colon separated list of key files. * @param keytype [In] The key type. * @param keyform [In] The key format. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. */ int decrypt_recipients(BIO *bio_err, R_CERT_CTX *cert_ctx, R_PKEY_CTX *pkey_ctx, R_CM *obj, char *certs, R_CERT_TYPE certtype, R_FORMAT certform, char *keys, R_PKEY_TYPE keytype, R_FORMAT keyform) { int ret = R_ERROR_NONE; R_CERT *cert = NULL; R_PKEY *pkey = NULL; char *cert_str = NULL; char *key_str = NULL; int cert_len = 0; int key_len = 0; int index; if ((certs == NULL) || (keys == NULL)) { BIO_printf(bio_err, "Missing recipient information\n"); ret = R_ERROR_NULL_ARG; goto err; } cert_len = R_STR_token(&certs, ':', NULL); key_len = R_STR_token(&keys, ':', NULL); while ((cert_len > 0) && (key_len > 0)) { if ((cert_str = (char *)Malloc(cert_len + 1)) == NULL) { ret = R_ERROR_ALLOC_FAILURE; goto err; } if ((key_str = (char *)Malloc(key_len + 1)) == NULL) { ret = R_ERROR_ALLOC_FAILURE; goto err; } R_STR_token(&certs, ':', cert_str); R_STR_token(&keys, ':', key_str); /* Read in the certificate from file */ if ((ret = R_CERT_read_file(cert_ctx, cert_str, certtype, certform, &cert)) != R_ERROR_NONE) { BIO_printf(bio_err, "Certificate from file failure: %s\n", cert_str); goto err; } /* Read in the key from file */ if ((ret = R_PKEY_from_file(pkey_ctx, &pkey, key_str, keytype, keyform)) != R_ERROR_NONE) { BIO_printf(bio_err, "Private key from file failure: %s\n", key_str); goto err; } /* Retrieve the index of the recipient in the cryptographic message */ if ((ret = R_CM_get_index(obj, cert, &index)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to lookup recipient %s\n", cert_str); goto err; } /* Decrypt for the recipient at a particular index */ if ((ret = R_CM_decrypt(obj, pkey, index)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to decrypt with key: %s\n", key_str); goto err; } if (cert != NULL) { R_CERT_free(cert); cert = NULL; } if (pkey != NULL) { R_PKEY_free(pkey); pkey = NULL; } /* Free allocated strings */ Free(cert_str); cert_str = NULL; Free(key_str); key_str = NULL; cert_len = R_STR_token(&certs, ':', NULL); key_len = R_STR_token(&keys, ':', NULL); } err: /* There should be an equal number of certificates and keys */ if ((cert_len > 0) || (key_len > 0)) { BIO_printf(bio_err, "Mismatch in the number of certs and keys\n"); ret = R_ERROR_FAILED; } if (cert != NULL) { R_CERT_free(cert); cert = NULL; } if (pkey != NULL) { R_PKEY_free(pkey); pkey = NULL; } if (cert_str != NULL) { Free(cert_str); } if (key_str != NULL) { Free(key_str); } return(ret); }