RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm_open_strm.c

/* $Id: cm_open_strm.c,v 1.32 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 opening a PKCS #7 enveloped data message using
 * the streaming interface.
 *
 * In its simplest form this sample will decrypt the enveloped data.
 * The optional switches can be used to print the data and/or recipient
 * information from the enveloped data message.
 *
 * See cm_env.c to generate PKCS #7 enveloped data messages.
 *
 * For example, to:
 *
 * Open and decrypt a PKCS #7 enveloped data message (data within message):
 *   cm_open_strm -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_strm -cm_msg env.data -enc_msg data.env -keys rec_priv.key
 *           -certs recipient.cert -print_data -print_recipient
 *
 * 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"

#define BUF_LEN         256

/* The usage help message. */
static char *cm_open_usage[] =
{
    "usage: cm_open [options]\n",
    "where options are:\n",
    " -cm_msg file         - The file containing the cryptographic message\n",
    " -out file            - The file to write the decrypted data into\n",
    " -no_ci               - The cryptographic message has no ContentInfo\n",
    " -cert file           - The certificate file\n",
    " -certtype encoding   - The encoding of the certificate - only X509\n",
    "                        (default) supported\n",
#ifdef NO_PEM
    " -certform format     - The format of the certificate (BIN only)\n",
#else
    " -certform format     - The format of the certificate\n"
    "                        - one of BIN (default), PEM\n",
#endif /* NO_PEM */
    " -key file            - The private key matching the certificate\n",
    " -keytype encoding    - The encoding of the key - one of RSA (default)\n",
#ifdef NO_PEM
    " -keyform format      - The format of the key (BIN only)\n",
#else
    " -keyform format      - The format of the key\n"
    "                        - 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 */
    NULL
};


/*
 * 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.<br>
 *            See @ref R_ERROR_IDS for valid values.
 */
int main(int argc, char **argv)
{
    int               ret       = R_ERROR_NONE;
    int               err;
    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       *pkey_ctx  = NULL;
    R_CERT           *cert      = NULL;
    R_PKEY           *pkey      = NULL;
    BIO              *bio_err   = NULL;
    BIO              *bio_out   = NULL;
    BIO              *bio_file  = NULL;
    BIO              *bio_cm    = NULL;
    char             *cm_file;
    char             *data_file;
    char             *out_file;
    char             *keyfile;
    char             *certfile;
    char             *str;
    R_CERT_TYPE       certtype;
    R_PKEY_TYPE       keytype;
    R_FORMAT          certform;
    R_FORMAT          keyform;
    int               detached;
    int               print_data;
    int               print_rec;
    int               single;
    int               no_ci;
    int               len;
    unsigned char     buf[BUF_LEN];

    /* Set the defaults */
    cm_file    = NULL;
    out_file   = NULL;
    certfile   = NULL;
    data_file  = NULL;
    certtype   = R_CERT_TYPE_X509;
    certform   = R_FORMAT_BINARY;
    keyfile    = NULL;
    keytype    = R_PKEY_TYPE_RSA;
    keyform    = R_FORMAT_BINARY;
    detached   = 0;
    print_data = 0;
    print_rec  = 0;
    single     = 0;
    no_ci      = 0;

    res_list = PRODUCT_DEFAULT_RESOURCE_LIST();

    /*
     * Create BIO to stderr. 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);
    if (bio_err == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto end;
    }

    /* 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, "-data_msg") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            data_file = *(++argv);
        }
        else if (Strcmp(*argv, "-out") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            out_file = *(++argv);
        }
        else if (Strcmp(*argv, "-no_ci") == 0)
        {
            no_ci = 1;
        }
        else if (Strcmp(*argv, "-key") == 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, "-cert") == 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
        {
            BIO_printf(bio_err, "Unknown option %s\n", *argv);
            goto bad;
        }
        argc--;
        argv++;
    }

    /* Simple checks first */
    if (cm_file == NULL)
    {
        BIO_printf(bio_err, "A cryptographic message file is required\n");
        ret = R_ERROR_FAILED;
        goto bad;
    }

    if ((certfile == NULL) || (keyfile == NULL))
    {
        BIO_printf(bio_err,
            "A recipient certificate and key are required.\n");
        ret = R_ERROR_FAILED;
        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.
     */

    /* 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_err, "The 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, "The 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;
    }

    /* Create a cryptographic message object */
    if ((ret = R_CM_new(ctx, R_CM_TYPE_ENVELOPED_DATA, &obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err,
            "Failed to create a cryptographic message object.\n");
        goto end;
    }

    /*
     * Assign the cryptographic message object to the BIO. For streaming a
     * stack of BIOs is formed and the data streamed through each BIO to
     * perform a specific part of the envelope decryption.
     */
    ret = R_CM_to_BIO(obj, R_RES_FLAG_DEF, &bio_cm);
    if (ret != R_ERROR_NONE)
    {
        BIO_printf(bio_err,
            "Failed to create a BIO from cryptographic message object.\n");
        goto end;
    }

    /*
     * The no_ci option notifies the cryptographic message BIO that the message
     * to be opened has no outer ContentInfo. It is supplied so that nested
     * cryptographic messages can be opened, since the nested messages must
     * have the ContentInfo removed.
     *
     * This sample assumes the message has type ENVELOPED_DATA. Therefore an
     * additional call to BIO_set_content_type() is not required.
     */
    if (no_ci)
    {
        BIO_set_unwrapped(bio_cm);
    }

    /*
     * Create the envelope decryption input stream. Create the input BIO to
     * read the enveloped data and push the cryptographic message filter on top
     * of the input stream.
     */
    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_FAILED;
        goto end;
    }

    /* Push the cryptographic message BIO onto the input stream */
    BIO_push(bio_cm, bio_file);

    /*
     * Open the file to write the decrypted enveloped message content into
     */
    if (out_file != NULL)
    {
        if ((bio_out = BIO_new_file(out_file, "wb")) == NULL)
        {
            BIO_printf(bio_err,
                       "Failed to open the output file: %s\n", out_file);
            ret = R_ERROR_FAILED;
            goto end;
        }
    }

    /*
     * Read in the certificate and key, and place them against the BIO. The
     * certificate and key are used to decrypt the symmetric key that was used
     * to decrypt the enveloped data.
     */

    /* Read in the certificate from file */
    if ((ret = R_CERT_read_file(cert_ctx, certfile, certtype, certform,
         &cert)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Certificate from file failure: %s\n", certfile);
        goto end;
    }

    /*
     * Set the recipient certificate to use when decrypting the enveloped
     * data header
     */
    err = BIO_set_cert(bio_cm, cert);
    if (err != 1)
    {
        BIO_printf(bio_err, "Failed to set the cert against BIO\n");
        ret = R_ERROR_FAILED;
        goto end;
    }

    /* Read in the key from file */
    if ((ret = R_PKEY_from_file(pkey_ctx, &pkey, keyfile, keytype,
        keyform)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "The private key from file failure: %s\n", keyfile);
        goto end;
    }

    /*
     * Set the asymmetric key to use when decrypting the enveloped data
     * header
     */
    err = BIO_set_asym_key(bio_cm, pkey);
    if (err != 1)
    {
        BIO_printf(bio_err, "Failed to set the key against BIO\n");
        ret = R_ERROR_FAILED;
        goto end;
    }

    /*
     * Read all application data from the input stream. Decrypt the data as it
     * is streamed in and output the decrypted data to the output stream. Only
     * one recipient can be decrypted at a time when using the streaming
     * interface.
     */

    /* Read all application data */
    while ((len = BIO_read(bio_cm, (char *)buf, BUF_LEN)) != 0)
    {
        /* Stop reading data if an unrecoverable error occurs */
        if ((len < -1) || ((len == -1) && (!BIO_should_retry(bio_cm))))
        {
            BIO_printf(bio_err, "Failure while reading data\n");
            ret = R_ERROR_IO;
            goto end;
        }

        /* Dump out any Bytes that are returned in the buffer */
        if (len > 0)
        {
            if (bio_out != NULL)
            {
                BIO_write(bio_out, (char *)buf, len);
            }

            if (print_data)
            {
                BIO_dump(bio_err, buf, len);
            }
        }
    }

    /* Print the encrypted data message if requested */
    if (print_rec == 1)
    {
        /*
         * Print the enveloped data message. If print_data != 0 then the
         * data has been printed to bio_err.
         */
        if ((ret = R_CM_write(obj, bio_err, R_FORMAT_TEXT, &print_data,
            0)) != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Write the envelopedData data failure\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 (bio_cm != NULL)
    {
        BIO_free(bio_cm);
    }

    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);
    }

    if (cert != NULL)
    {
        R_CERT_free(cert);
    }

    /*
     * The certificate context cannot be freed until the store has been emptied
     * as certificates in the store hold a reference to the certificate context
     */
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }

    if (bio_file != NULL)
    {
        BIO_free(bio_file);
    }

    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }

    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }

    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