RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm_sign_sm.c

/* $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));
}


Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 072-001001-2100-001-000 - 2.1