RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

req.c

/* $Id: req.c,v 1.67 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.
 *
 *
 */

#include "r_prod.h"

/* The function to create an issuer name string */
static int get_issuer_name_string(unsigned int max_str_len, char *str,
    int no_in);
/* The function to create a serial number */
static int get_serial_number(unsigned int max_buf_len, unsigned char *buf,
    unsigned int *out_len, int no_in);

/* The size of the issuer name string to hold user input */
#define NAME_STR_LEN                   512
/* The size of the user input for each item of the issuer name */
#define NAME_ITEM_STR_LEN              80

/* The size of the serial number buffer */
#define SERIAL_BUF_LEN                 32
/* The size of the input buffer for serial number */
#define SERIAL_INPUT_LEN               SERIAL_BUF_LEN * 2 + 1

/* The number of items in the name that is to be created from user input */
#define CERT_NAME_NUM_ITEMS            5

/*
 * The mapping of long name version strings of the name entry identifier
 * to default value
 */
static char *cert_name_items[CERT_NAME_NUM_ITEMS*3] =
{
    LN_commonName,              SN_commonName,              "Root Cert",
    LN_organizationName,        SN_organizationName,        "RSA Security",
    LN_localityName,            SN_localityName,            "Brisbane",
    LN_stateOrProvinceName,     SN_stateOrProvinceName,     "Qld",
    LN_countryName,             SN_countryName,             "AU",
};

/* Usage help message */
static char *req_usage[] =
{
    "usage: req [options]\n",
    "where options are:\n",
    " -in arg            - The certificate request filename to read\n",
    " -intype arg        - The input type - one of PKCS10",
    " (default is PKCS10)\n",
#ifndef NO_PEM
    " -inform arg        - The input format - one of BIN PEM",
    " (default is PEM)\n",
#else /* NO_PEM */
    " -inform arg        - The input format (BIN only)\n",
#endif /* !NO_PEM */
    " -out arg           - The file name to write\n",
#ifndef NO_PEM
    " -outform arg       - The output format - one of BIN PEM",
    " (default is PEM)\n",
#else /* NO_PEM */
    " -outform arg       - The output format (BIN only)\n",
#endif /* !NO_PEM */
    " -text              - Prints out text form of request\n",
    " -C                 - Prints the request in C format for inclusion in\n",
    "                      other code\n",
#ifndef NO_CFUNC
    " -Cf                - Prints a function in C that will return the\n",
    "                      specified certificate request\n",
#endif /* NO_CFUNC */
    " -hex               - Prints the request in hex format\n",
    " -codehex           - Prints the request in code hex format request\n",
    " -verify            - Verifies signature on request\n",
    " -key arg           - The public key file name to read\n",
#ifndef NO_PEM
    " -keyform arg       - The format of key - one of BIN PEM",
    " (default is PEM)\n",
#else /* NO_PEM */
    " -keyform arg       - The format of key (BIN only)\n",
#endif /* !NO_PEM */
    " -keytype arg       - The type of key - one of RSA DSA",
    " (default is RSA)\n",
    " -CA arg            - The name of the Certification Authority (CA)",
    " certificate file\n",
#ifndef NO_PEM
    " -CAform arg        - The format of CA certificate - one of BIN PEM",
    " (default is PEM)\n",
#else /* NO_PEM */
    " -CAform arg        - The format of CA certificate (BIN only)\n",
#endif /* !NO_PEM */
    " -CAtype arg        - The type of CA certificate - one of X509",
    " (default is X509)\n",
#ifndef NO_DSA
    " -sign_type arg     - The signature type to use - one of:\n",
    "                      RSA_MD2 RSA_MD5 RSA_SHA1 DSA_SHA1",
    " (default is RSA_SHA1)\n",
#else /* NO_DSA */
    " -sign_type arg     - The signature type to use - one of:\n",
    "                      RSA_MD2 RSA_MD5 RSA_SHA1 (default is RSA_SHA1)\n",
#endif /* !NO_DSA */
    " -x509              - Outputs an X.509 certificate instead of a",
    " certificate request\n",
    " -days arg          - The number of days the certificate is valid for\n",
    " -utc_notbefore arg - The time at which the certificate becomes valid\n",
    " -utc_notafter arg  - The time at which the certificate expires\n",
    " -no_in             - Indicates that no input is required for the",
    " creation of the name\n",
    " -eg                - The example usage\n",
    NULL
};

static char *req_example_usage[] =
{
    "Prints out the request in text form\n",
    "req -in req.in -text\n",
    "\n",
    "Verifies the signature of the request\n",
    "req -in req.in -key signer.key -verify\n",
    "\n",
#ifndef NO_PEM
    "Converts requests between formats (PEM to BIN)\n",
    "req -in req.in -inform PEM -out req.bin -outform BIN\n",
    "\n",
#endif /* NO_PEM */
    "Creates a certificate from an existing request\n",
    "req -in req.in -out cert.out -key ca.key -CA ca.cert -x509 -days 200\n",
    "\n",
    "where: req.in     = existing request\n",
    "       signer.key = key used to sign the request (see reqgen command)\n",
    "       req.bin    = same as req.in but in binary format\n",
    "       cert.out   = the certificate generated from the request req.in\n",
    "       ca.key     = CA's private key - used to sign the created",
    " certificate\n",
    "       ca.cert    = CA's cert - used to retrieve the issuer name etc\n",
    "\n",
    NULL
};

/* Global variables */
/* Standard error output stream */
BIO *bio_err;
/* Standard input stream */
BIO *bio_in;

/*
 * 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;         /* The return value */
    BIO *bio_out = NULL;            /* The standard output stream */
    BIO *bio_req_in = NULL;         /* The request data input stream */
    BIO *bio_req_out = NULL;        /* The request file output stream */
    R_LIB_CTX *lib_ctx = NULL;      /* The library context */
    R_CERT_REQ_CTX *req_ctx = NULL; /* The certificate request context */
    R_CERT_REQ *req = NULL;         /* The certificate request */
    R_CERT_CTX *cert_ctx = NULL;    /* The certificate context */
    R_CERT *cert = NULL;            /* The certificate */
    R_CERT *ca = NULL;              /* The Certification Authority (CA)
                                     * certificate */
    R_CERT_NAME *name=NULL;         /* The issuer name of new certificate */
    R_PKEY_CTX *pkey_ctx = NULL;    /* The public key context */
    R_PKEY *pkey = NULL;            /* The public key */
    R_TIME_CTX *time_ctx = NULL;    /* The time module context */
    R_TIME *na_time = NULL;         /* The notAfter time */
    R_TIME *nb_time = NULL;         /* The notBefore time */
    int req_type;                   /* The type of certificate request */
    R_FORMAT req_form;              /* The format of the certificate request */
    R_FORMAT out_form;              /* The output format of the certificate
                                     * request */
    int key_type;                   /* The type of public key */
    R_FORMAT key_form;              /* The format of the public key */
    int sign_type;                  /* The signature type with which to sign
                                     * the new certificate */
    int ca_type;                    /* The type of CA certificate */
    R_FORMAT ca_form;               /* The format of the CA certificate */
    char *pkeyfile = NULL;          /* The public key file name */
    char *cafile = NULL;            /* The CA certificate file name */
    char *reqfile = NULL;           /* The certificate request file name */
    char *outfile = NULL;           /* The output file name */
    int text;                       /* Display the text output of the
                                     * request */
    int do_c;                       /* Display the C version */
#ifndef NO_CFUNC
    int do_cf;                      /* Display the C code version of the
                                     * request */
#endif
    int do_hex;                     /* Display the hexadecimal version */
    int do_codehex;                 /* Display the Code version in hexadecimal
                                     * format */
    int verify;                     /* Verify the certificate request */
    int x509;                       /* Create an X.509 certificate from the
                                     * request */
    long days;                      /* The number of days for which the
                                     * certificate will be valid */
    int no_in = 0;                  /* Indicates to not retrieve input
                                     * - use defaults */
    char *not_after = NULL;         /* The notAfter time as a string */
    char *not_before = NULL;        /* The notBefore time as a string */
    unsigned char serial_number[SERIAL_BUF_LEN];    /* The serial number */
    char name_str[NAME_STR_LEN];    /* The certificate name as a string */
    R_ITEM item;                    /* The item of data */
    int verified;                   /* The request was verified */
    int badop = 0;                  /* A bad command line option */
    char *str;                      /* A pointer to the character string */

    /* Initialize the global variables */
    bio_err = NULL;
    bio_in = NULL;

    /* Set the defaults */
    req_type = R_CERT_REQ_TYPE_PKCS10;
#ifndef NO_PEM
    req_form = R_FORMAT_PEM;
    out_form = R_FORMAT_PEM;
    key_form = R_FORMAT_PEM;
    ca_form = R_FORMAT_PEM;
#else /* NO_PEM */
    req_form = R_FORMAT_BINARY;
    out_form = R_FORMAT_BINARY;
    key_form = R_FORMAT_BINARY;
    ca_form = R_FORMAT_BINARY;
#endif /* !NO_PEM */
    key_type = R_PKEY_TYPE_RSA;
    sign_type = R_CR_ID_SHA1_RSA;
    ca_type = R_CERT_TYPE_X509;
    text = 0;
    do_c = 0;
#ifndef NO_CFUNC
    do_cf = 0;
#endif
    do_hex = 0;
    do_codehex = 0;
    verify = 0;
    x509 = 0;
    days = 30;

    /*
     * 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);
    if (bio_err == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto done;
    }
    bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (bio_out == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto done;
    }
    /* Create the input channel */
    bio_in = BIO_new_fp(stdin, BIO_NOCLOSE);
    if (bio_in == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto done;
    }


    /* Parse the command line parameters */

    /* Skip the program name */
    argc--;
    argv++;

    /* Process all command line options */
    while (argc >= 1)
    {
        if      (Strcmp(*argv, "-in") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            reqfile = *argv;
        }
        else if (Strcmp(*argv, "-intype") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if ((ret = R_CERT_REQ_TYPE_from_string(str,
                                                   &req_type)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unknown request type %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-inform") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if ((ret = R_FORMAT_from_string(str, &req_form)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unknown request format %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-out") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            outfile = *argv;
        }
        else if (Strcmp(*argv, "-outform") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if ((ret = R_FORMAT_from_string(str, &out_form)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unknown output format %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-text") == 0)
        {
            text = 1;
        }
        /* C code version to display */
        else if (Strcmp(*argv, "-C") == 0)
        {
            do_c = 1;
        }
#ifndef NO_CFUNC
        /* C code version in fields to display */
        else if (Strcmp(*argv, "-Cf") == 0)
        {
            do_cf = 1;
        }
#endif /* !NO_CFUNC */
        /* The hexadecimal version to display */
        else if (Strcmp(*argv, "-hex") == 0)
        {
            do_hex = 1;
        }
        /* C code version in hexadecimal to display */
        else if (Strcmp(*argv, "-codehex") == 0)
        {
            do_codehex = 1;
        }
        else if (Strcmp(*argv, "-verify") == 0)
        {
            verify = 1;
        }
        else if (Strcmp(*argv, "-key") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            pkeyfile = *argv;
        }
        else if (Strcmp(*argv, "-keyform") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if (R_FORMAT_from_string(str, &key_form) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unknown pkey format %s\n", str);
                ret = R_ERROR_FAILED;
                goto done;
            }
        }
        else if (Strcmp(*argv, "-keytype") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if (R_PKEY_TYPE_from_string(&key_type, str) != R_ERROR_NONE)
            {
                goto bad;
            }
            if (key_type == R_PKEY_TYPE_UNKNOWN)
            {
                BIO_printf(bio_err, "Unknown pkey type %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-CA") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            cafile = *argv;
        }
        else if (Strcmp(*argv, "-CAform") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if ((ret = R_FORMAT_from_string(str, &ca_form)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unknown cert format %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-CAtype") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if ((ret = R_CERT_TYPE_from_string(str, &ca_type)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unknown cert type %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-sign_type") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            if (Strcmp(str, "RSA_MD2") == 0)
            {
                sign_type = R_CR_ID_MD2_RSA;
            }
            else if (Strcmp(str, "RSA_MD5") == 0)
            {
                sign_type = R_CR_ID_MD5_RSA;
            }
            else if (Strcmp(str, "RSA_SHA1") == 0)
            {
                sign_type = R_CR_ID_SHA1_RSA;
            }
#ifndef NO_DSA
            else if (Strcmp(str, "DSA_SHA1") == 0)
            {
                sign_type = R_CR_ID_SHA1_DSA;
            }
#endif
            else
            {
                BIO_printf(bio_err, "Unknown signature type %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-x509") == 0)
        {
            x509 = 1;
        }
        else if (Strcmp(*argv, "-days") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            str = *argv;
            days = atoi(str);
            if (days < 0)
            {
                BIO_printf(bio_err, "Days cannot be negative: %d\n", days);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-utc_notafter") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            not_after = *argv;
        }
        else if (Strcmp(*argv, "-utc_notBefore") == 0)
        {
            if ((--argc < 1) || (Strncmp(*(++argv), "-", 1) == 0))
            {
                goto bad;
            }
            not_before = *argv;
        }
        else if (Strcmp(*argv, "-no_in") == 0)
        {
            no_in = 1;
        }
        else if (Strcmp(*argv,"-eg") == 0)
        {
            char **egp;
            for (egp = req_example_usage; (*egp) != NULL; egp++)
            {
                BIO_printf(bio_out, *egp);
            }
            goto done;
        }
        else
        {
            BIO_printf(bio_err, "unknown option %s\n", *argv);
            badop = 1;
            break;
        }
        argc--;
        argv++;
    }

    /* Perform simple checks */
    if ((x509 == 1) && (pkeyfile == NULL))
    {
        BIO_printf(bio_err,
            "When issuing a certificate a signing key is required\n");
        badop = 1;
    }

    /* Display help menu if an invalid command line option was entered */
    if (badop)
    {
        char **pp;

bad: ;
        for (pp = req_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_err, *pp);
        }
        goto done;
    }

    /*
     * Create the library context. Retrieve the default resource list and
     * create a library context to provide access to all configurable aspects
     * of the library.
     */
    if ((ret = PRODUCT_LIBRARY_NEW(PRODUCT_DEFAULT_RESOURCE_LIST(), 0,
         &lib_ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create the library context\n");
        goto done;
    }

    /*
     * Create a certificate request context. The context is required if any
     * R_CERT_REQ_* routines are used.
     */
    ret = R_CERT_REQ_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_CERT_REQ_TYPE_PKCS10,
              &req_ctx);

    if (ret != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create an R_CERT_REQ_CTX\n");
        goto done;
    }

    /* Open the input file */
    if (reqfile == NULL)
    {
        /* No certificate request file specified */
        BIO_printf(bio_err, "No input file specified\n");
        goto done;
    }
    else
    {
        /* Open the request file */
        bio_req_in = BIO_new_file(reqfile, "rb");
    }
    if (bio_req_in == NULL)
    {
        ret = R_ERROR_NOT_FOUND;
        BIO_printf(bio_err, "Unable to open the input file %s\n", reqfile);
        goto done;
    }

    /*
     * Read the certificate request from the input file into a new certificate
     * object. The certificate request object (R_CERT_REQ) stores all the
     * certificate request information.
     */
    ret = R_CERT_REQ_read(req_ctx, bio_req_in, req_type, req_form, &req);
    if (ret != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to read the request\n");
        goto done;
    }

    /*
     * Read the signer's PKEY from file. The signer's key is used to sign the
     * certificate request. If a certificate request is not being generated
     * then no key is supplied.
     */
    if (pkeyfile != NULL)
    {
        /* Create a public key context in order to create the public key */
        ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, key_type,
            &pkey_ctx);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to create an R_PKEY_CTX\n");
            goto done;
        }

        /* Create a public key from the data in the file */
        ret = R_PKEY_from_file(pkey_ctx, &pkey, pkeyfile, key_type, key_form);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed to open the key file: %s\n", pkeyfile);
            goto done;
        }
    }

    /*
     * Create a certificate context and load the Certification Authority (CA)
     * certificate (in the case of generating a certificate)
     */
    if ((cafile != NULL) || (x509 == 1))
    {
        ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, ca_type, &cert_ctx);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to create an R_CERT_CTX\n");
            goto done;
        }
    }
    /* Read the CA certificate from file */
    if (cafile != NULL)
    {
        ret = R_CERT_read_file(cert_ctx, cafile, ca_type, ca_form, &ca);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                       "Unable to read the CA certificate file: %s\n", cafile);
            goto done;
        }
    }

    /*
     * Verify the certificate request signature, if requested. The signer's key
     * is required to verify the signature. This key is either provided as a
     * command line argument or in the case of a self-signed request the key is
     * obtained from the request itself.
     */
    if (verify == 1)
    {
        /*
         * Retrieve the key from the certificate request if none are loaded.
         * This is the self signed case .
         */
        if (pkey == NULL)
        {
            ret = R_CERT_REQ_public_key_to_R_PKEY(req, R_FLAG_SHARE_DATA,
                &pkey);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err,
                           "Unable to extract the key from the request\n");
                goto done;
            }
        }

        /*
         * Perform the verification. The return value will be an error code on
         * failure.
         */
        ret = R_CERT_REQ_verify(req, pkey, &verified);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "R_CERT_REQ_verify failure\n");
            goto done;
        }
        else if (verified == 1)
        {
            BIO_printf(bio_err, "verify OK\n");
        }
        else
        {
            BIO_printf(bio_err, "The certificate request verification"
                " failed\n");
            goto done;
        }
    }

    /*
     * Issue a certificate from the request if requested. This requires the
     * following steps:
     *     - Obtain all the time information.
     *     - Create the certificate object based on an original request. This
     *       provides some of the information for the certificate.
     *     - Obtain the remaining information and set it against the
     *       certificate object.
     *     - Sign the certificate.
     * If generating a WTLS certificate, do not include the steps that set
     * the serial number.
     */
    if (x509 == 1)
    {
        /* Create the time context */
        ret = R_TIME_CTX_new(lib_ctx, R_RES_FLAG_DEF, &time_ctx);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to create an R_TIME_CTX\n");
            goto done;
        }

        /* Create the notBefore time object */
        ret = R_TIME_new(time_ctx, &nb_time);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to create the time object\n");
            goto done;
        }

        if (not_before == NULL)
        {
            /* Retrieve the current time */
            ret = R_TIME_time(nb_time);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unable to retrieve the current time\n");
                goto done;
            }
        }
        else
        {
            /* Import the Universal Time, Coordinated (UTC) notBefore time */
            ret = R_TIME_import(nb_time, R_TIME_EXTERNAL_FORMAT_UTC,
                (unsigned char *)not_before, Strlen(not_before));
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unable to import the notBefore time\n");
                goto done;
            }
        }

        /* Copy the notBefore time */
        ret = R_TIME_dup(nb_time, &na_time);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to create the time object\n");
            goto done;
        }

        if (not_after == NULL)
        {
            /* Adjust the notAfter time by the number of days */
            ret = R_TIME_offset(na_time, na_time, 60*60*24*days);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unable to offset the time\n");
                goto done;
            }
        }
        else
        {
            /* Import the Universal Time, Coordinated (UTC) notAFter time */
            ret = R_TIME_import(na_time, R_TIME_EXTERNAL_FORMAT_UTC,
                (unsigned char *) not_after, Strlen(not_after));
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Unable to import the notAfter time\n");
                goto done;
            }
        }

        /*
         * Issue a new certificate. The certificate request contains the
         * subject name and public key. This information can be entered
         * directly into the certificate. Before signing, the serial number,
         * issuer name and validity times are added to complete the
         * certificate.
         */

        /* Create a certificate based on the information in the request */
        ret = R_CERT_REQ_to_R_CERT(req, cert_ctx, ca_type, &cert);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                       "Failed to create the certificate from the request\n");
            goto done;
        }

        /* Retrieve the serial number from use */
        ret = get_serial_number(SERIAL_BUF_LEN, serial_number, &item.len,
            no_in);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed to retrieve the serial number\n");
            goto done;
        }
        item.data = serial_number;

        /* Store the serial number in the certificate */
        ret = R_CERT_set_info(cert, R_CERT_INFO_SERIAL_NUMBER, &item);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed to set the certificate"
                " serial number\n");
            goto done;
        }

        /* Use the subject name from a CA certificate as the issuer */
        if (cafile != NULL)
        {
            ret = R_CERT_subject_name_to_R_CERT_NAME(ca,
                R_FLAG_SHARE_DATA, &name);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err,
                    "Failed to retrieve the subject name from a CA cert\n");
                goto done;
            }
        }
        /* Retrieve the issuer name from the user */
        else
        {
            /* Retrieve the issuer name as a string */
            ret = get_issuer_name_string(NAME_STR_LEN, name_str, no_in);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Failed to obtain issuer name from"
                    " user\n");
                goto done;
            }
            /* Convert the string into an R_CERT_NAME structure */
            ret = R_CERT_NAME_from_string(cert_ctx, name_str, &name);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Failed to convert the string to a"
                    " name\n");
                goto done;
            }
        }

        /* Store the issuer name */
        ret = R_CERT_set_info(cert, R_CERT_INFO_ISSUER_R_CERT_NAME, name);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed to set the issuer name to the"
                " certificate\n");
            goto done;
        }

        /* Store the notBefore validity time as a UTC string */
        ret = R_CERT_not_before_from_R_TIME(cert, nb_time);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                "Failed to set the notBefore time to the certificate\n");
            goto done;
        }

        /* Store the notAfter validity time as a UTC string */
        ret = R_CERT_not_after_from_R_TIME(cert, na_time);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                "Failed to set the notAfter time to the certificate\n");
            goto done;
        }

        /*
         * Sign the certificate. All the certificate information is now in the
         * certificate object. Sign the certificate information with the
         * private key (pkey) using the specified signature algorithm
         * (sign_type).
         */
        ret = R_CERT_sign(cert, pkey, sign_type);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed to sign the certificate\n");
            goto done;
        }
    }

    /*
     * Print the certificate request information. A number of possible output
     * formats can be specified and are available below.
     */
    if (text)
    {
        ret = R_CERT_REQ_write(req, bio_out, R_FORMAT_TEXT, NULL);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to print the request\n");
            goto done;
        }
    }
    /* Display the certificate request in C code format */
    if (do_c)
    {
        ret = R_CERT_REQ_write(req, bio_out, R_FORMAT_CODE_FIELDS,
            NULL);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                "Unable to print the code format of the certificate"
                " request\n");
            goto done;
        }
    }
#ifndef NO_CFUNC
    /* Display the certificate in C code fields format */
    if (do_cf)
    {
        ret = R_CERT_REQ_write(req, bio_out, R_FORMAT_CODE_BINARY,
            NULL);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                "Unable to print the code fields format of the certificate"
                " request\n");
            goto done;
        }
    }
#endif /* !NO_CFUNC */
    /* Display the certificate request in hexadecimal format */
    if (do_hex)
    {
        ret = R_CERT_REQ_write(req, bio_out, R_FORMAT_HEX, NULL);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                "Unable to display the hexadecimal format of a certificate"
                " request\n");
            goto done;
        }
    }

    /* Display the certificate request in code hexadecimal format */
    if (do_codehex)
    {
        ret = R_CERT_REQ_write(req, bio_out, R_FORMAT_CODE_HEX, NULL);
        if (ret != R_ERROR_NONE)
        {
            BIO_printf(bio_err,
                "Unable to print the code in hexadecimal format of a"
                " certificate request\n");
            goto done;
        }
    }

    /*
     * Write the certificate to file. Open the output stream and print the
     * certificate into the stream.
     */
    if (outfile != NULL)
    {
        bio_req_out = BIO_new_file(outfile, "wb");
        if (bio_req_out == NULL)
        {
            BIO_printf(bio_err, "Unable to open the file: %s\n", outfile);
            ret = R_ERROR_ALLOC_FAILURE;
            goto done;
        }
    }
    else
    {
        bio_req_out = bio_out;
        bio_out = NULL;
    }

    /* Print the PEM format only to stdout */
    if (!((out_form == R_FORMAT_BINARY) && (outfile == NULL)))
    {
        /* Output the certificate to file */
        if (x509 == 1)
        {
            ret = R_CERT_write(cert, bio_req_out, out_form, NULL);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Failed to write the certificate\n");
            }
        }
        /* Output the certificate request to file */
        else
        {
            ret = R_CERT_REQ_write(req, bio_req_out, out_form, NULL);
            if (ret != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Failed to write the request\n");
            }
        }
    }

done:
    /*
     * 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_printf(bio_out, "ERROR: (%d) %s\n", ret,
            R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret));
    }

    if (pkey != NULL)
    {
        R_PKEY_free(pkey);
    }
    if (pkey_ctx != NULL)
    {
        R_PKEY_CTX_free(pkey_ctx);
    }

    if (name != NULL)
    {
        R_CERT_NAME_free(name);
    }
    if (ca != NULL)
    {
        R_CERT_free(ca);
    }
    if (cert != NULL)
    {
        R_CERT_free(cert);
    }
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }

    if (req != NULL)
    {
        R_CERT_REQ_free(req);
    }
    if (req_ctx != NULL)
    {
        R_CERT_REQ_CTX_free(req_ctx);
    }

    if (nb_time != NULL)
    {
        R_TIME_free(nb_time);
    }
    if (na_time != NULL)
    {
        R_TIME_free(na_time);
    }
    if (time_ctx != NULL)
    {
        R_TIME_CTX_free(time_ctx);
    }

    if (bio_req_in != NULL)
    {
        BIO_free(bio_req_in);
    }
    if (bio_req_out != NULL)
    {
        BIO_free(bio_req_out);
    }

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

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

    return(R_ERROR_EXIT_CODE(ret));
}


/*
 * Creates an issuer name string. If the option no_in was specified, the name
 * values are the default. Otherwise, the values come from the user.
 *
 * @param  max_str_len [In]  The amount of memory allocated to string.
 * @param  str         [In]  The issuer name.
 * @param  no_in       [In]  Indicates that no user input is required.
 *
 * @returns  R_ERROR_NONE indicates success.<br>
 *           See @ref R_ERROR_IDS for valid values.
 */
static int get_issuer_name_string(unsigned int max_str_len, char *str,
    int no_in)
{
    int ret=R_ERROR_NONE;                       /* The return value */
    int i;                                      /* The iterator over name
                                                 * entries */
    MS_STATIC char in_str[NAME_ITEM_STR_LEN];   /* The name entry value */
    char *p;                                    /* A pointer into a string */

    /* Make the issuer name string empty */
    str[0] = '\0';

    /* Create an issuer name string with each of the entries completed */
    for (i=0;i<CERT_NAME_NUM_ITEMS;i++)
    {
        /* Put in a separator between successive entries */
        if (i != 0)
        {
            Strcat(str, ", ");
        }

        /* Enter a long name of the entry type */
        Strcat(str, cert_name_items[i*3+1]);
        Strcat(str, "=");

        /* Retrieve the name entry value as the default, or from the user */
        p=cert_name_items[i*3+2];
        if (no_in == 0)
        {
            /* Print out the prompt */
            BIO_printf(bio_err, "Enter %s [default %s]: ", cert_name_items[i*3],
                       p);

            /* Empty the string and enter the user's input */
            in_str[0]=0;
            BIO_gets(bio_in, in_str, NAME_ITEM_STR_LEN);

            /* Remove the trailing carriage return(s)/line feed(s) */
            while (in_str[Strlen(in_str)-1] == '\n' ||
                   in_str[Strlen(in_str)-1] == '\r')
            {
                in_str[Strlen(in_str)-1] = '\0';
            }


            /* Use the entered string if required */
            if (in_str[0] != '\0')
            {
                p=in_str;
            }
         }

         /* Enter the entry value */
         Strcat(str, p);
    }

    /* The return error value */
    return(ret);
}

/*
 * Creates a serial number. If the option no_in was specified, the serial
 * number value is the default. Otherwise, the value comes from the user.
 *
 * @param  max_buf_len [In]  The amount of memory allocated to buffer.
 * @param  buf         [In]  The serial number.
 * @param  out_len     [In]  The amount of data going into buffer.
 * @param  no_in       [In]  Indicates that no user input is wanted.
 *
 * @returns  R_ERROR_NONE indicates success.<br>
 *           See @ref R_ERROR_IDS for valid values.
 */
static int get_serial_number(unsigned int max_buf_len, unsigned char *buf,
    unsigned int *out_len, int no_in)
{
    int ret = R_ERROR_NONE;                     /* The return value */
    MS_STATIC char in_str[SERIAL_INPUT_LEN];    /* The string to read into */
    int d;                                      /* A digit */
    int s;                                      /* The step value */
    int i;                                      /* The iterator over input
                                                 * string */

    /* The default value of serial number is zero */
    buf[0] = 0;
    *out_len = 1;

    /* Only retrieve the input if required */
    if (no_in == 0)
    {
        /* Display the prompt */
        BIO_printf(bio_err, "Enter Serial Number [default 0]: ");

        /* Empty the string and enter input */
        in_str[0]=0;
        BIO_gets(bio_in, in_str, SERIAL_INPUT_LEN);

        /* Remove the trailing carriage return(s)/line feed(s) */
        while (in_str[Strlen(in_str)-1] == '\n' ||
               in_str[Strlen(in_str)-1] == '\r')
        {
            in_str[Strlen(in_str)-1] = '\0';
        }

        /* Convert the user input into a Byte array */
        if (in_str[0] != '\0')
        {
            /*
             * If there is an uneven number of characters then the first one
             * is the low nibble of the Byte. The step value resolves this.
             */
            s = (Strlen(in_str) & 1);

            /*
             * Process each character in the input string until the end of the
             * string or the end of the buffer
             */
            for (i=0; (in_str[i] != '\0') &&
                      ((unsigned int)((s+i)/2) <= max_buf_len); i++)
            {
                /* Store the value and make sure it is a hexadecimal value */
                d = in_str[i];
                if (!isxdigit(d))
                {
                    ret = R_ERROR_FAILED;
                    goto done;
                }

                /* Convert the hexadecimal digit to a number */
                if ((d >= '0') && (d <= '9'))
                {
                    d -= '0';
                }
                else if ((d >= 'a') && (d <= 'f'))
                {
                    d -= 'a' - 10;
                }
                else
                {
                    d -= 'A' - 10;
                }

                /* If this is the nibble in the appropriate place */
                if (((s+i) & 1) == 0)
                {
                    buf[(s+i)>>1] = d << 4;
                }
                else
                {
                    buf[(s+i)>>1] += d;
                }
            }

            /* Calculate the amount of data in the buffer */
            *out_len = ((s + Strlen(in_str)) >> 1);
        }
    }

done:
    /* The return error value */
    return(ret);
}


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