| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: cm_sign_sm.c,v 1.23 2005/04/12 22:37:34 hfrancis 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_sign_sm.c * This sample demonstrates simple PKCS #7 signed data operations and * has the following steps: * * - Create a PKCS #7 signed data message. * - Write the signed message to a file * - Read the signed data message back from file into an R_CM structure. * - Verify the signed data message. * - Print various data fields from the signed data message. * - Print the verified message data. * * This is a fully self-contained program and therefore requires no * command line parameters. The data to be signed is defined as a text * string. */ #include "r_prod.h" #include "r_oid.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_out = NULL; BIO *bio_err = 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_PKEY_CTX *key_ctx = NULL; R_TIME_CTX *time_ctx = NULL; R_CERT *cert = NULL; R_PKEY *key = NULL; R_TIME *now = NULL; R_CR_ALG_ID id; R_CM_INDEX indx; R_CM_ATTR attr; char *cm_file; char cdata[]="Data to be signed - 1234567890"; unsigned char buf[128]; int is_verified; int var; R_CERT_TYPE certtype; unsigned int consumed_len; R_TITEM msg_data = { R_FLAG_SHARE_DATA, 0, NULL }; /* Set the defaults */ certtype = R_CERT_TYPE_X509; id = R_CR_ID_MD5; cm_file = "cm_sign_sm.out"; /* * 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); /* * 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, cryptographic message and time * contexts. These contexts are required if any R_CERT_*, R_PKEY_*, * R_CM_*, R_TIME* routines are used. */ /* Create a new key context */ if ((ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_PKEY_TYPE_RSA, &key_ctx)) != R_ERROR_NONE) { BIO_printf(bio_out, "Key context new failure\n"); goto end; } /* Create a new certificate context */ if ((ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, certtype, &cert_ctx)) != R_ERROR_NONE) { BIO_printf(bio_out, "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 time context */ if ((ret = R_TIME_CTX_new(lib_ctx, R_RES_FLAG_DEF, &time_ctx)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_TIME_CTX_new failure\n"); goto end; } /* * Create the public key object and load the key. Creating a private key * object enables the private key to be read from memory and converted into * an R_PKEY object. The key is defined in cm_sm.h. This is the private key * and is used to sign the data message. The corresponding certificate * is used to verify the signature. (See the next step) */ if ((ret = R_PKEY_from_binary( key_ctx, R_PKEY_FL_DEFAULT, R_PKEY_TYPE_RSA, sizeof(signer_priv_key_data), signer_priv_key_data, &consumed_len, &key)) != R_ERROR_NONE) { BIO_printf(bio_out, "R_PKEY_from_binary 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 (that is, cert==NULL), an R_CERT object is created. This * certificate is put into the PKCS #7 message and is used by the recipient * to verify the signed data. */ if ((ret = R_CERT_from_binary(cert_ctx, R_FLAG_SHARE_DATA, 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 signer certificate\n"); goto end; } /* * Read the message data into an R_CM object. The data is then ready to be * included into the final cryptographic message structure in later steps, * prior to signing 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 signed data object, using the following * sub-steps: * - Create a new signed data message object. * - Add signer information. * - Set the signing time attribute. * - Include the actual data (created in the previous step). */ /* Create a new signed data message */ BIO_printf(bio_out, "Create a new signed data message\n"); if ((ret = R_CM_new(ctx, R_CM_TYPE_SIGNED_DATA, &obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_CM_new failure (Signed Data)\n"); goto end; } /* Add the signer information */ if ((ret = R_CM_signer_add(obj, cert, key, id, &indx)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to add signer\n"); goto end; } /* The signer now owns the certificate and key */ cert = NULL; key = NULL; /* Create a new time object */ if ((ret = R_TIME_new(time_ctx, &now)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_TIME_new failure\n"); goto end; } /* Get the current time */ if ((ret = R_TIME_time(now)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_TIME_time failure\n"); goto end; } /* Export the current time in UTC format */ if ((ret = R_TIME_export(now, R_TIME_EXTERNAL_FORMAT_UTC, buf, &consumed_len, sizeof(buf))) != R_ERROR_NONE) { BIO_printf(bio_err, "R_TIME_export failure\n"); goto end; } /* Do not include the current time string terminating character ('\0') */ consumed_len -= 1; /* Set up the signing time attribute */ R_CM_ATTR_signing_time_set_data(&attr, R_TIME_EXTERNAL_FORMAT_UTC, buf, consumed_len); attr.security = R_CM_ATTR_SECURITY_AUTHENTICATED; /* Set the signing time attribute against the signer */ if ((ret = R_CM_signer_set_attribute(obj, indx, R_CM_INFO_SIGNER_ATTR_SIGNING_TIME, &attr)) != R_ERROR_NONE) { BIO_printf(bio_err, "R_CM_signer_set_attribute failure\n"); goto end; } /* Set the message 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; } /* Sign the data */ if ((ret = R_CM_sign(obj, R_CM_INDEX_ALL)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to sign\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; } /* Write out the signed data message */ if ((ret = R_CM_write(obj, bio_file, R_FORMAT_BINARY, NULL, R_CM_ENCODING_FORMAT_WRAPPED)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to write signedData to file\n"); goto end; } /* The new R_CM is created later */ if (obj != NULL) { R_CM_free(obj); obj = NULL; } /* * Close the output file. This file must be read from, and this requires * that the file be already closed. */ if (bio_file != NULL) { BIO_free(bio_file); bio_file = NULL; } BIO_printf(bio_out, "Signed Data written to file %s\n", cm_file); /* Read the signed data message from file */ /* Open the input BIO */ if ((bio_file = BIO_new_file(cm_file, "rb")) == NULL) { BIO_printf(bio_err, "Failed to open file: %s\n", cm_file); ret = R_ERROR_ALLOC_FAILURE; goto end; } if ((ret = R_CM_read(ctx, bio_file, R_FORMAT_BINARY, R_CM_ENCODING_FORMAT_WRAPPED, &obj)) != R_ERROR_NONE) { BIO_printf(bio_err, "Failed to read cryptographic message from file %s\n", (cm_file != NULL) ? cm_file : "<unknown>"); goto end; } BIO_printf(bio_out, "Signed Data successfully read back from the file %s\n", cm_file); /* * Verify the signed data message. The verification has two return values. * The first is the verification routine return and the second is the * verification status. */ if ((ret = R_CM_signature_verify(obj, R_CM_INDEX_ALL, NULL, &is_verified)) != R_ERROR_NONE) { BIO_printf(bio_out, "R_CM_signature_verify failed\n"); goto end; } if (!is_verified) { ret = R_ERROR_FAILED; BIO_printf(bio_out, "Verify data failed\n"); goto end; } BIO_printf(bio_out, "Signature Verified!\n"); /* Print out the data and some of the signed data message fields */ BIO_printf(bio_out, "Now print some of the data fields!\n"); /* The message type */ 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); } /* The version */ 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 certificates. That is, the number of additional * certificates included. */ var = 0; R_CM_get_info(obj, R_CM_INFO_CERT_COUNT, &var); BIO_printf(bio_out, "Certificate count: %d\n", var); /* The number of Certificate Revocation Lists (CRLs) */ var = 0; R_CM_get_info(obj, R_CM_INFO_CRL_COUNT, &var); BIO_printf(bio_out, "CRL count: %d\n", var); /* The number of signers. That is, the number of signer_info blocks. */ var = 0; R_CM_get_info(obj, R_CM_INFO_SIGNER_COUNT, &var); BIO_printf(bio_out, "Signer count: %d\n", var); BIO_printf(bio_out, "And now for the data - it should be the same as the original data!\n"); /* Add the actual data that was signed */ if (R_CM_get_info(obj, R_CM_INFO_DATA, &msg_data) == R_ERROR_NONE) { BIO_printf(bio_out, "The message data is: %s\n", msg_data.data); } 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 (cert != NULL) { R_CERT_free(cert); } if (cert_ctx != NULL) { R_CERT_CTX_free(cert_ctx); } if (key != NULL) { R_PKEY_free(key); } if (key_ctx != NULL) { R_PKEY_CTX_free(key_ctx); } if (now != NULL) { R_TIME_free(now); } if (time_ctx != NULL) { R_TIME_CTX_free(time_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)); }