Enc Dec MAC Output Public Key as CSR

NSS Sample Code 5: Encryption/Decryption and MAC and output Public as a CSR.

Generates encryption/mac keys and outputs public key as certificate signing request

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* NSPR Headers */
#include
#include
#include
#include
#include
#include
#include

/* NSS headers */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/* our samples utilities */
#include "util.h"

#define BUFFERSIZE            80
#define DIGESTSIZE            16
#define PTEXT_MAC_BUFFER_SIZE 96
#define CIPHERSIZE            96
#define BLOCKSIZE             32
#define DEFAULT_KEY_BITS      1024


#define CIPHER_HEADER         "-----BEGIN CIPHER-----"
#define CIPHER_TRAILER        "-----END CIPHER-----"
#define ENCKEY_HEADER         "-----BEGIN WRAPPED ENCKEY-----"
#define ENCKEY_TRAILER        "-----END WRAPPED ENCKEY-----"
#define MACKEY_HEADER         "-----BEGIN WRAPPED MACKEY-----"
#define MACKEY_TRAILER        "-----END WRAPPED MACKEY-----"
#define IV_HEADER             "-----BEGIN IV-----"
#define IV_TRAILER            "-----END IV-----"
#define MAC_HEADER            "-----BEGIN MAC-----"
#define MAC_TRAILER           "-----END MAC-----"
#define PAD_HEADER            "-----BEGIN PAD-----"
#define PAD_TRAILER           "-----END PAD-----"
#define LAB_HEADER            "-----BEGIN KEY LABEL-----"
#define LAB_TRAILER           "-----END KEY LABEL-----"
#define PUBKEY_HEADER         "-----BEGIN PUB KEY -----"
#define PUBKEY_TRAILER        "-----END PUB KEY -----"
#define NS_CERTREQ_HEADER     "-----BEGIN NEW CERTIFICATE REQUEST-----"
#define NS_CERTREQ_TRAILER    "-----END NEW CERTIFICATE REQUEST-----"


typedef enum {
    GEN_CSR,
    ENCRYPT,
    DECRYPT,
    UNKNOWN
} CommandType;

typedef enum {
   SYMKEY = 0,
   MACKEY = 1,
   IV     = 2,
   MAC    = 3,
   PAD    = 4,
   PUBKEY = 5,
   LAB    = 6
} HeaderType;

/* This is conditionalized because PORT_ErrorToString was introduced with nss 3.13.
 * Though PR_ErrorToString was available, support for it in nss wasn't.
 * FIXME: samples should determine the version of nss that's available and refuse
 * to run if not 3.13 or higher.
 */
#ifndef PORT_ErrorToString
#ifndef SEC_ERROR_BASE
#define SEC_ERROR_BASE                          (-0x2000)
#define PORT_ErrorToString(err) PR_ErrorToString((err), PR_LANGUAGE_I_DEFAULT)
#endif
#endif


/*
 * Print usage message and exit
 */
static void
Usage(const char *progName)
{
    fprintf(stderr, "\nUsage:  %s -c  -d  [-z ] "
            "[-p  | -f ] -s  -r  -i  -o  \n\n",
            progName);
    fprintf(stderr, "%-20s  Specify 'G' for generating RSA keypair for wrapping\n\n",
             "G");
    fprintf(stderr, "%-20s  Specify 'E' for encrypt operation\n\n",
             "E");
    fprintf(stderr, "%-20s  Specify 'D' for decrypt operation\n\n",
             "D");
    fprintf(stderr, "%-20s  Specify db directory path\n\n",
             "-d ");
    fprintf(stderr, "%-20s  Specify db password [optional]\n\n",
             "-p ");
    fprintf(stderr, "%-20s  Specify db password file [optional]\n\n",
             "-f ");
    fprintf(stderr, "%-20s  Specify noise file name [optional]\n\n",
             "-z ");
    fprintf(stderr, "%-21s Specify subject\n\n",
             "-s ");
    fprintf(stderr, "%-21s Specify certficate request file name\n\n",
             "-r ");
    fprintf(stderr, "%-21s Specify an input file name\n\n",
             "-i ");
    fprintf(stderr, "%-21s Specify an output file name\n\n",
             "-o ");
    fprintf(stderr, "%-7s For encrypt, it takes  as an input file and produces\n",
             "Note :");
    fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n",
             "");
    fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n",
             "");
    fprintf(stderr, "%-7s as input files and produces  as a final output file.\n\n",
             "");
    exit(-1);
}


/* Map option letter enumerated commad type */
static CommandType option2Command(const char* c)
{
    switch (*c) {
    case 'G': return GEN_CSR;
    case 'E': return ENCRYPT;
    case 'D': return DECRYPT;
    default:  return UNKNOWN;
    }
}

/*
 * Wrap the symkey using public key
 */
SECStatus
WrapKey(PK11SymKey* key, SECKEYPublicKey *pubKey, SECItem **wrappedKey)
{
    SECStatus rv;
    SECItem *data = (SECItem *)PORT_ZAlloc(sizeof(SECItem));

    if (!data) {
        PR_fprintf(PR_STDERR, "Error while allocating memory\n");
        rv = SECFailure;
        goto cleanup;
    }

    data->len = SECKEY_PublicKeyStrength(pubKey);
    data->data = (unsigned char*)PORT_ZAlloc((data->len)*sizeof(unsigned int));

    if (!data->data) {
        PR_fprintf(PR_STDERR, "Error while allocating memory\n");
        rv = SECFailure;
        goto cleanup;
    }

    rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, key, data);
    if (rv != SECSuccess) {
        rv = SECFailure;
    } else {
        *wrappedKey = data;
        return SECSuccess;
    }

cleanup:
    if (data) {
        SECITEM_FreeItem(data, PR_TRUE);
    }
    return rv;
}

/*
 * Generate a Symmetric Key
 */
PK11SymKey *
GenerateSYMKey(PK11SlotInfo  *slot, CK_MECHANISM_TYPE mechanism,
               int keySize, SECItem *keyID, secuPWData *pwdata)
{
    SECStatus      rv;
    PK11SymKey    *key;

    /* Generate the symmetric key */
    key = PK11_TokenKeyGen(slot, mechanism,
            NULL, keySize, keyID, PR_FALSE, pwdata);

    if (!key) {
        PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n");
    }

    return key;
}

/*
 * MacInit
 */
SECStatus
MacInit(PK11Context *ctx)
{
    SECStatus rv = PK11_DigestBegin(ctx);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n");
    }
    return rv;
}

/*
 * MacUpdate
 */
SECStatus
MacUpdate(PK11Context *ctx,
          unsigned char *msg, unsigned int msgLen)
{
    SECStatus rv = PK11_DigestOp(ctx, msg, msgLen);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n");
    }
    return rv;
}

/*
 * Finalize MACing
 */
SECStatus
MacFinal(PK11Context *ctx,
         unsigned char *mac, unsigned int *macLen, unsigned int maxLen)
{
    SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n");
    }
    return SECSuccess;
}

/*
 * Compute Mac
 */
SECStatus
ComputeMac(PK11Context *ctxmac,
           unsigned char *ptext, unsigned int ptextLen,
           unsigned char *mac, unsigned int *macLen,
           unsigned int maxLen)
{
    SECStatus rv = MacInit(ctxmac);
    if (rv != SECSuccess) return rv;
    rv = MacUpdate(ctxmac, ptext, ptextLen);
    if (rv != SECSuccess) return rv;
    rv = MacFinal(ctxmac, mac, macLen, maxLen);
    return rv;
}

/*
 * WriteToHeaderFile
 */
SECStatus
WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type,
                  PRFileDesc *outFile)
{
    SECStatus      rv;
    const char    *header;
    const char    *trailer;

    switch (type) {
    case SYMKEY:
        header = ENCKEY_HEADER;
        trailer = ENCKEY_TRAILER;
        break;
    case MACKEY:
        header =  MACKEY_HEADER;
        trailer = MACKEY_TRAILER;
        break;
    case IV:
        header = IV_HEADER;
        trailer = IV_TRAILER;
        break;
    case MAC:
        header = MAC_HEADER;
        trailer = MAC_TRAILER;
        break;
    case PAD:
        header = PAD_HEADER;
        trailer = PAD_TRAILER;
        break;
    case PUBKEY:
        header = PUBKEY_HEADER;
        trailer = PUBKEY_TRAILER;
        break;
    case LAB:
        header = LAB_HEADER;
        trailer = LAB_TRAILER;
        PR_fprintf(outFile, "%s\n", header);
        PR_fprintf(outFile, "%s\n", buf);
        PR_fprintf(outFile, "%s\n\n", trailer);
        return SECSuccess;
        break;
        default:
        return SECFailure;
    }

    PR_fprintf(outFile, "%s\n", header);
    PrintAsAscii(outFile, buf, len);
    PR_fprintf(outFile, "%s\n\n", trailer);
    return SECSuccess;
}

/*
 * Initialize for encryption or decryption - common code
 */
PK11Context *
CryptInit(PK11SymKey *key,
          unsigned char *iv, unsigned int ivLen,
          CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation)
{
    SECItem ivItem = { siBuffer, iv, ivLen };
    PK11Context *ctx = NULL;

    SECItem *secParam = PK11_ParamFromIV(type, &ivItem);
    if (secParam == NULL) {
        PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n");
        return NULL;
    }
    ctx = PK11_CreateContextBySymKey(type, operation, key, secParam);
    if (ctx == NULL) {
        PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n");
        goto cleanup;

    }
cleanup:
    if (secParam) {
        SECITEM_FreeItem(secParam, PR_TRUE);
    }
    return ctx;
}

/*
 * Common encryption and decryption code
 */
SECStatus
Crypt(PK11Context *ctx,
      unsigned char *out, unsigned int *outLen, unsigned int maxOut,
      unsigned char *in, unsigned int inLen)
{
    SECStatus rv;

    rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv);
        goto cleanup;
    }

cleanup:
    if (rv != SECSuccess) {
        return rv;
    }
    return SECSuccess;
}

/*
 * Decrypt
 */
SECStatus
Decrypt(PK11Context *ctx,
        unsigned char *out, unsigned int *outLen, unsigned int maxout,
        unsigned char *in, unsigned int inLen)
{
    return Crypt(ctx, out, outLen, maxout, in, inLen);
}

/*
 * Encrypt
 */
SECStatus
Encrypt(PK11Context* ctx,
        unsigned char *out, unsigned int *outLen, unsigned int maxout,
        unsigned char *in, unsigned int inLen)
{
    return Crypt(ctx, out, outLen, maxout, in, inLen);
}

/*
 * EncryptInit
 */
PK11Context *
EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen,
            CK_MECHANISM_TYPE type)
{
    return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT);
}

/*
 * DecryptInit
 */
PK11Context *
DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen,
            CK_MECHANISM_TYPE type)
{
    return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT);
}

/*
 * Read cryptographic parameters from the header file
 */
SECStatus
ReadFromHeaderFile(const char *fileName, HeaderType type,
                   SECItem *item, PRBool isHexData)
{
    SECStatus      rv;
    SECItem        filedata;
    SECItem        outbuf;
    unsigned char *nonbody;
    unsigned char *body;
    char          *header;
    char          *trailer;
    PRFileDesc    *file = NULL;

    outbuf.type = siBuffer;
    file = PR_Open(fileName, PR_RDONLY, 0);
    if (!file) {
        PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName);
        return SECFailure;
    }
    switch (type) {
    case PUBKEY:
        header = PUBKEY_HEADER;
        trailer = PUBKEY_TRAILER;
        break;
    case SYMKEY:
        header = ENCKEY_HEADER;
        trailer = ENCKEY_TRAILER;
        break;
    case MACKEY:
        header = MACKEY_HEADER;
        trailer = MACKEY_TRAILER;
        break;
    case IV:
        header = IV_HEADER;
        trailer = IV_TRAILER;
        break;
    case MAC:
        header = MAC_HEADER;
        trailer = MAC_TRAILER;
        break;
    case PAD:
        header = PAD_HEADER;
        trailer = PAD_TRAILER;
        break;
    case LAB:
        header = LAB_HEADER;
        trailer = LAB_TRAILER;
        break;
    default:
        PR_Close(file);
        return SECFailure;
    }

    rv = FileToItem(&filedata, file);
    nonbody = (char *)filedata.data;
    if (!nonbody) {
        PR_fprintf(PR_STDERR, "unable to read data from input file\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* check for headers and trailers and remove them */
    char *trail = NULL;
    if ((body = strstr(nonbody, header)) != NULL) {
        char *trail = NULL;
        nonbody = body;
        body = PORT_Strchr(body, '\n');
        if (!body)
            body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */
        if (body)
            trail = strstr(++body, trailer);
        if (trail != NULL) {
            *trail = '\0';
        } else {
            PR_fprintf(PR_STDERR,  "input has header but no trailer\n");
            PORT_Free(filedata.data);
            return SECFailure;
        }
    } else {
        /* headers didn't exist */
        body = nonbody;
        if (body) {
            trail = strstr(++body, trailer);
            if (trail != NULL) {
                PR_fprintf(PR_STDERR,
                    "input has no header but has trailer\n");
                PORT_Free(filedata.data);
                return SECFailure;
            }
        }
    }

cleanup:
    PR_Close(file);
    ATOB_ConvertAsciiToItem(item, body);
    return SECSuccess;
}

/*
 * Generate the private key
 */
SECKEYPrivateKey *
GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
                   int publicExponent, const char *noiseFileName,
                   SECKEYPublicKey **pubkeyp, const char *pqgFile,
                   secuPWData *pwdata)
{
    CK_MECHANISM_TYPE  mechanism;
    SECOidTag          algtag;
    PK11RSAGenParams   rsaparams;
    void              *params;
    SECKEYPrivateKey  *privKey    = NULL;
    SECStatus          rv;
    unsigned char      randbuf[BLOCKSIZE + 1];

    rv = GenerateRandom(randbuf, BLOCKSIZE);
    if (rv != SECSuccess) {
        fprintf(stderr, "Error while generating the random numbers : %s\n",
                PORT_ErrorToString(rv));
        goto cleanup;
    }
    PK11_RandomUpdate(randbuf, BLOCKSIZE);
    switch (keytype) {
        case rsaKey:
            rsaparams.keySizeInBits = size;
            rsaparams.pe            = publicExponent;
            mechanism               = CKM_RSA_PKCS_KEY_PAIR_GEN;
            algtag                  = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
            params                  = &rsaparams;
            break;
        default:
            goto cleanup;
    }
    fprintf(stderr, "\n\n");
    fprintf(stderr, "Generating key.  This may take a few moments...\n\n");
    privKey = PK11_GenerateKeyPair(slot, mechanism, params, pubkeyp,
                                       PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/,
                                       pwdata);
cleanup:
    return privKey;
}

/*
 * Extract the public key request from CSR
 */
SECKEYPublicKey *
ExtractPublicKeyFromCertRequest(const char *inFileName, PRBool ascii)
{
    CERTSignedData signedData;
    SECItem reqDER;
    CERTCertificateRequest *certReq = NULL;
    SECStatus rv                    = SECSuccess;
    PRArenaPool *arena              = NULL;
    SECKEYPublicKey *publicKey      = NULL;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        rv = SECFailure;
        goto cleanup;
    }

    rv = ReadDERFromFile(&reqDER, inFileName, ascii);
    if (rv) {
        rv = SECFailure;
        goto cleanup;
    }
    certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc
               (arena, sizeof(CERTCertificateRequest));
    if (!certReq) {
        rv = SECFailure;
        goto cleanup;
    }
    certReq->arena = arena;

    /* Since cert request is a signed data, must decode to get the inner
       data
    */
    PORT_Memset(&signedData, 0, sizeof(signedData));
    rv = SEC_ASN1DecodeItem(arena, &signedData,
                            SEC_ASN1_GET(CERT_SignedDataTemplate), &reqDER);
    if (rv) {
        rv = SECFailure;
        goto cleanup;
    }
    rv = SEC_ASN1DecodeItem(arena, certReq,
                            SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data);
    if (rv) {
        rv = SECFailure;
        goto cleanup;
    }
    rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData,
                &certReq->subjectPublicKeyInfo, NULL /* wincx */);
    publicKey = SECKEY_ExtractPublicKey(&certReq->subjectPublicKeyInfo);

cleanup:
    if (reqDER.data) {
        SECITEM_FreeItem(&reqDER, PR_FALSE);
    }
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }
    return publicKey;
}

/*
 * Get the private key corresponding to public key
 */
SECKEYPrivateKey *
GetRSAPrivateKey(PK11SlotInfo    *slot,
                secuPWData       *pwdata,
                SECKEYPublicKey  *pubKey)
{
    SECKEYPrivateKey         *privKey   = NULL;
    SECItem                  *cka_id;

    if (slot == NULL) {
        fprintf(stderr, "Empty Slot\n");
        goto cleanup;
    }
    if (PK11_Authenticate(slot, PR_TRUE, pwdata) != SECSuccess) {
        fprintf(stderr, "could not authenticate to token %s.",
                PK11_GetTokenName(slot));
        goto cleanup;
    }
    cka_id  = &pubKey->u.rsa.modulus;
    cka_id  = PK11_MakeIDFromPubKey(cka_id);
    privKey = PK11_FindKeyByKeyID(slot, cka_id, pwdata);
cleanup:
    return privKey;
}

/*
 *  Generate the certificate request with subject
 */
static SECStatus
CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType,
        SECOidTag hashAlgTag, CERTName *subject, PRBool ascii,
        const char *certReqFileName)
{
    CERTSubjectPublicKeyInfo *spki          = NULL;
    CERTCertificateRequest   *cr            = NULL;
    SECItem                  *encoding      = NULL;
    SECOidTag                 signAlgTag;
    SECItem                   result;
    SECStatus                 rv            = SECSuccess;
    PRInt32                   numBytes;
    void                     *extHandle;
    PRArenaPool              *arena         = NULL;
    PRFileDesc               *outFile       = NULL;

    /*  Open the certificate request file to write */
    outFile = PR_Open(certReqFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660);
    if (!outFile) {
        PR_fprintf(PR_STDERR,
                   "unable to open \"%s\" for writing (%ld, %ld).\n",
                   certReqFileName, PR_GetError(), PR_GetOSError());
        goto cleanup;
    }
    /* Create info about public key */
    spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
    if (!spki) {
        PR_fprintf(PR_STDERR, "unable to create subject public key\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* Generate certificate request */
    cr = CERT_CreateCertificateRequest(subject, spki, NULL);
    if (!cr) {
        PR_fprintf(PR_STDERR, "unable to make certificate request\n");
        rv = SECFailure;
        goto cleanup;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena) {
        fprintf(stderr, "out of memory");
        rv = SECFailure;
        goto cleanup;
    }

    extHandle = CERT_StartCertificateRequestAttributes(cr);
    if (extHandle == NULL) {
        PORT_FreeArena (arena, PR_FALSE);
        rv = SECFailure;
        goto cleanup;
    }

    CERT_FinishExtensions(extHandle);
    CERT_FinishCertificateRequestAttributes(cr);

    /* Der encode the request */
    encoding = SEC_ASN1EncodeItem(arena, NULL, cr,
                                  SEC_ASN1_GET(CERT_CertificateRequestTemplate));
    if (encoding == NULL) {
        PR_fprintf(PR_STDERR, "der encoding of request failed\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* Sign the request */
    signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
    if (signAlgTag == SEC_OID_UNKNOWN) {
        PR_fprintf(PR_STDERR, "unknown Key or Hash type\n");
        rv = SECFailure;
        goto cleanup;
    }
    rv = SEC_DerSignData(arena, &result, encoding->data, encoding->len,
                         privk, signAlgTag);
    if (rv) {
        PR_fprintf(PR_STDERR, "signing of data failed\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* Encode request in specified format */
    if (ascii) {
        char *obuf;
        char *name, *email, *org, *state, *country;
        SECItem *it;
        int total;

        it = &result;

        obuf = BTOA_ConvertItemToAscii(it);
        total = PL_strlen(obuf);

        name = CERT_GetCommonName(subject);
        if (!name) {
            name = strdup("(not specified)");
        }

        email = CERT_GetCertEmailAddress(subject);
        if (!email)
            email = strdup("(not specified)");

        org = CERT_GetOrgName(subject);
        if (!org)
            org = strdup("(not specified)");

        state = CERT_GetStateName(subject);
        if (!state)
            state = strdup("(not specified)");

        country = CERT_GetCountryName(subject);
        if (!country)
            country = strdup("(not specified)");

        PR_fprintf(outFile,
                   "\nCertificate request generated by Netscape certutil\n");
        PR_fprintf(outFile, "Common Name: %s\n", name);
        PR_fprintf(outFile, "Email: %s\n", email);
        PR_fprintf(outFile, "Organization: %s\n", org);
        PR_fprintf(outFile, "State: %s\n", state);
        PR_fprintf(outFile, "Country: %s\n\n", country);

        PR_fprintf(outFile, "%s\n", NS_CERTREQ_HEADER);
        numBytes = PR_Write(outFile, obuf, total);
        if (numBytes != total) {
            PR_fprintf(PR_STDERR, "write error\n");
            return SECFailure;
        }
        PR_fprintf(outFile, "\n%s\n", NS_CERTREQ_TRAILER);
        if (obuf) {
            PORT_Free(obuf);
        }
    } else {
        numBytes = PR_Write(outFile, result.data, result.len);
        if (numBytes != (int)result.len) {
            PR_fprintf(PR_STDERR, "write error\n");
            rv = SECFailure;
	    goto cleanup;
        }
    }
cleanup:
    if (spki) {
    	SECKEY_DestroySubjectPublicKeyInfo(spki);
    }
    if (cr) {
        CERT_DestroyCertificateRequest (cr);
    }
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }
    if (outFile) {
    	PR_Close(outFile);
    }
    return rv;
}

/*
 * Mac and Encrypt the input file content
 */
SECStatus
EncryptAndMac(PRFileDesc *inFile,
              PRFileDesc *headerFile,
              PRFileDesc *encFile,
              PK11SymKey *ek,
              PK11SymKey *mk,
              unsigned char *iv, unsigned int ivLen,
              PRBool ascii)
{
    SECStatus      rv;
    unsigned char  ptext[BLOCKSIZE];
    unsigned int   ptextLen;
    unsigned char  mac[DIGESTSIZE];
    unsigned int   macLen;
    unsigned int   nwritten;
    unsigned char  encbuf[BLOCKSIZE];
    unsigned int   encbufLen;
    SECItem        noParams = { siBuffer, NULL, 0 };
    PK11Context   *ctxmac = NULL;
    PK11Context   *ctxenc = NULL;
    unsigned int   pad[1];
    SECItem        padItem;
    unsigned int   paddingLength = 0;

    static unsigned int firstTime = 1;
    int j;

    ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams);
    if (ctxmac == NULL) {
        PR_fprintf(PR_STDERR, "Can't create MAC context\n");
        rv = SECFailure;
        goto cleanup;
    }
    rv = MacInit(ctxmac);
    if (rv != SECSuccess) {
        goto cleanup;
    }

    ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC);

    /* read a buffer of plaintext from input file */
    while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) {
        /* Encrypt using it using CBC, using previously created IV */
        if (ptextLen != BLOCKSIZE) {
            paddingLength = BLOCKSIZE - ptextLen;
            for ( j=0; j < paddingLength; j++) {
                ptext[ptextLen+j] = (unsigned char)paddingLength;
            }
            ptextLen = BLOCKSIZE;
        }
        rv  = Encrypt(ctxenc,
                encbuf, &encbufLen, sizeof(encbuf),
                ptext, ptextLen);
        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "Encrypt Failure\n");
            goto cleanup;
        }

        /* save the last block of ciphertext as the next IV */
        iv = encbuf;
        ivLen = encbufLen;

        /* write the cipher text to intermediate file */
        nwritten = PR_Write(encFile, encbuf, encbufLen);
        /*PR_Assert(nwritten == encbufLen);*/

        rv = MacUpdate(ctxmac, ptext, ptextLen);
        if (rv != SECSuccess)
            goto cleanup;
    }

    rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "MacFinal Failure\n");
        goto cleanup;
    }
    if (macLen == 0) {
        PR_fprintf(PR_STDERR, "Bad MAC length\n");
        rv = SECFailure;
        goto cleanup;
    }
    WriteToHeaderFile(mac, macLen, MAC, headerFile);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Write MAC Failure\n");
        goto cleanup;
    }

    pad[0] = paddingLength;
    padItem.type = siBuffer;
    padItem.data = (unsigned char *)pad;
    padItem.len  = sizeof(pad[0]);

    WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Write PAD Failure\n");
        goto cleanup;
    }

    rv = SECSuccess;

cleanup:
    if (ctxmac != NULL) {
        PK11_DestroyContext(ctxmac, PR_TRUE);
    }
    if (ctxenc != NULL) {
        PK11_DestroyContext(ctxenc, PR_TRUE);
    }

    return rv;
}

/*
 * Decrypt and Verify MAC
 */
SECStatus
DecryptAndVerifyMac(PRFileDesc *outFile,
    PRFileDesc *inFile, unsigned int inFileLength,
    SECItem *cItem, SECItem *macItem,
    PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem)
{
    SECStatus      rv;
    unsigned char  decbuf[64];
    unsigned int   decbufLen;

    unsigned char  ptext[BLOCKSIZE];
    unsigned int   ptextLen = 0;
    unsigned char  ctext[64];
    unsigned int   ctextLen;
    unsigned char  newmac[DIGESTSIZE];
    unsigned int   newmacLen                 = 0;
    unsigned int   newptextLen               = 0;
    unsigned int   count                     = 0;
    unsigned int   temp                      = 0;
    unsigned int   blockNumber               = 0;
    SECItem        noParams = { siBuffer, NULL, 0 };
    PK11Context   *ctxmac = NULL;
    PK11Context   *ctxenc = NULL;

    unsigned char iv[BLOCKSIZE];
    unsigned int ivLen = ivItem->len;
    unsigned int paddingLength;
    int j;

    memcpy(iv, ivItem->data, ivItem->len);
    paddingLength = (unsigned int)padItem->data[0];

    ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams);
    if (ctxmac == NULL) {
        PR_fprintf(PR_STDERR, "Can't create MAC context\n");
        rv = SECFailure;
        goto cleanup;
    }

    rv = MacInit(ctxmac);
    if (rv != SECSuccess) goto cleanup;

    ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC);

    while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) {

        count += ctextLen;

        /* decrypt cipher text buffer using CBC and IV */

        rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf),
                     ctext, ctextLen);

        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "Decrypt Failure\n");
            goto cleanup;
        }

        if (decbufLen == 0) break;

        rv = MacUpdate(ctxmac, decbuf, decbufLen);
        if (rv != SECSuccess) { goto cleanup; }
        if (count == inFileLength) {
            decbufLen = decbufLen-paddingLength;
        }

        /* write the plain text to out file */
        temp = PR_Write(outFile, decbuf, decbufLen);
        if (temp != decbufLen) {
            PR_fprintf(PR_STDERR, "write error\n");
            rv = SECFailure;
            break;
        }

        blockNumber++;
    }

    if (rv != SECSuccess) { goto cleanup; }

    rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac));
    if (rv != SECSuccess) { goto cleanup; }

    if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) {
        rv = SECSuccess;
    } else {
        PR_fprintf(PR_STDERR, "Check MAC : Failure\n");
        PR_fprintf(PR_STDERR, "Extracted : ");
        PrintAsAscii(PR_STDERR, macItem->data, macItem->len);
        PR_fprintf(PR_STDERR, "Computed  : ");
        PrintAsAscii(PR_STDERR, newmac, newmacLen);
        rv = SECFailure;
    }
cleanup:
    if (ctxmac) {
        PK11_DestroyContext(ctxmac, PR_TRUE);
    }
    if (ctxenc) {
        PK11_DestroyContext(ctxenc, PR_TRUE);
    }

    return rv;
}

/*
 * Open intermediate file, read in IV, wrapped encryption key,
 * wrapped MAC key, MAC, PAD and public key from header file
 */
SECStatus
GetDataFromHeader(const char *headerFileName,
                  SECItem *ivItem,
                  SECItem *wrappedEncKeyItem,
                  SECItem *wrappedMacKeyItem,
                  SECItem *macItem,
                  SECItem *padItem,
                  SECKEYPublicKey **pubKey)
{
    SECStatus rv = SECSuccess;
    CERTSubjectPublicKeyInfo *keyInfo = NULL;
    SECItem pubKeyData;

    /* Read in the IV into item from the header file */
    rv = ReadFromHeaderFile(headerFileName, IV, ivItem, PR_TRUE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n");
        goto cleanup;
    }

    rv = ReadFromHeaderFile(headerFileName, SYMKEY, wrappedEncKeyItem, PR_TRUE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR,
        "Could not retrieve wrapped AES key from header file\n");
        goto cleanup;
    }
    /* Read in the MAC key into item from the header file */
    rv = ReadFromHeaderFile(headerFileName, MACKEY, wrappedMacKeyItem, PR_TRUE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR,
        "Could not retrieve wrapped MAC key from header file\n");
        goto cleanup;
    }

    /* Get the public key from header file */
    rv = ReadFromHeaderFile(headerFileName, PUBKEY, &pubKeyData, PR_TRUE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR,
        "Could not retrieve public key from header file\n");
        goto cleanup;
    }
    keyInfo    = SECKEY_DecodeDERSubjectPublicKeyInfo(&pubKeyData);
    if (!keyInfo) {
        PR_fprintf(PR_STDERR, "Could not decode public key\n");
        rv = SECFailure;
        goto cleanup;
    }
    *pubKey = SECKEY_ExtractPublicKey(keyInfo);
    if (*pubKey == NULL) {
        PR_fprintf(PR_STDERR, "Error while getting RSA public key\n");
        rv = SECFailure;
        goto cleanup;
    }
    /* Read in the Mac into item from the header file */
    rv = ReadFromHeaderFile(headerFileName, MAC, macItem, PR_TRUE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR,
        "Could not retrieve MAC from cipher file\n");
        goto cleanup;
    }
    if (macItem->data == NULL) {
        PR_fprintf(PR_STDERR, "MAC has NULL data\n");
        rv = SECFailure;
        goto cleanup;
    }
    if (macItem->len == 0) {
        PR_fprintf(PR_STDERR, "MAC has data has 0 length\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* Read in the PAD into item from the header file */
    rv = ReadFromHeaderFile(headerFileName, PAD, padItem, PR_TRUE);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR,
        "Could not retrieve PAD detail from header file\n");
        goto cleanup;
    }

cleanup:
    return rv;
}


/*
 * DecryptFile
 */
SECStatus
DecryptFile(PK11SlotInfo *slot,
             const char   *outFileName,
             const char   *headerFileName,
             char         *encryptedFileName,
             secuPWData   *pwdata,
             PRBool       ascii)
{
    /*
     * The DB is open read only and we have authenticated to it
     * open input file, read in header, get IV and wrapped keys and
     * public key
     * Unwrap the wrapped keys
     * loop until EOF(input):
     *     read a buffer of ciphertext from input file,
     *     Save last block of ciphertext
     *     decrypt ciphertext buffer using CBC and IV,
     *     compute and check MAC, then remove MAC from plaintext
     *     replace IV with saved last block of ciphertext
     *     write the plain text to output file
     * close files
     * report success
     */

    SECStatus           rv;
    SECItem             ivItem;
    SECItem             wrappedEncKeyItem;
    SECItem             wrappedMacKeyItem;
    SECItem             cipherItem;
    SECItem             macItem;
    SECItem             padItem;
    SECKEYPublicKey    *pubKey              = NULL;
    PK11SymKey         *encKey              = NULL;
    PK11SymKey         *macKey              = NULL;
    SECKEYPrivateKey   *privKey             = NULL;
    PRFileDesc         *outFile             = NULL;
    PRFileDesc         *inFile              = NULL;
    unsigned int       inFileLength         = 0;

    /* open intermediate file, read in header, get IV, public key and
     * CKA_IDs of two keys from it
     */
    rv = GetDataFromHeader(headerFileName,
                           &ivItem,
                           &wrappedEncKeyItem,
                           &wrappedMacKeyItem,
                           &macItem,
                           &padItem,
                           &pubKey);
    if (rv != SECSuccess) {
        goto cleanup;
    }

    /* find private key from the DB token using public key */
    privKey = GetRSAPrivateKey(slot, pwdata, pubKey);
    if (privKey == NULL) {
        PR_fprintf(PR_STDERR, "Can't find private key\n");
        rv = SECFailure;
        goto cleanup;
    }

    encKey = PK11_PubUnwrapSymKey(privKey, &wrappedEncKeyItem,
                                  CKM_AES_CBC, CKA_ENCRYPT, 0);
    if (encKey == NULL) {
        PR_fprintf(PR_STDERR, "Can't unwrap the encryption key\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */
    macKey = PK11_PubUnwrapSymKey(privKey, &wrappedMacKeyItem,
                                  CKM_MD5_HMAC, CKA_SIGN, 160/8);
    if (macKey == NULL) {
        PR_fprintf(PR_STDERR, "Can't unwrap the Mac key\n");
        rv = SECFailure;
        goto cleanup;
    }

    /*  Open the input file.  */
    inFile = PR_Open(encryptedFileName, PR_RDONLY , 0);
    if (!inFile) {
        PR_fprintf(PR_STDERR,
                   "Unable to open \"%s\" for writing.\n",
                   encryptedFileName);
        return SECFailure;
    }
    /*  Open the output file.  */
    outFile = PR_Open(outFileName,
                      PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660);
    if (!outFile) {
        PR_fprintf(PR_STDERR,
                   "Unable to open \"%s\" for writing.\n",
                   outFileName);
        return SECFailure;
    }
    inFileLength = FileSize(encryptedFileName);

    if (rv == SECSuccess) {
        /* Decrypt and Remove Mac */
        rv = DecryptAndVerifyMac(outFile, inFile, inFileLength,
                &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem);
        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n");
        }
    }

cleanup:
    if (encKey) {
        PK11_FreeSymKey(encKey);
    }
    if (macKey) {
        PK11_FreeSymKey(macKey);
    }
    if (privKey) {
        SECKEY_DestroyPrivateKey(privKey);
    }
    if (pubKey) {
        SECKEY_DestroyPublicKey(pubKey);
    }
    return rv;
}

/*
 * EncryptFile
 */
SECStatus
EncryptFile(PK11SlotInfo *slot,
             const char   *inFileName,
             const char   *certReqFileName,
             const char   *headerFileName,
             const char   *encryptedFileName,
             const char   *noiseFileName,
             secuPWData   *pwdata,
             PRBool       ascii)
{
    /*
     * The DB is open for read/write and we have authenticated to it.
     * Read public key from certificate request
     * generate a symmetric AES key as a session object.
     * generate a second key to use for MACing, also a session object.
     * generate a random value to use as IV for AES CBC
     * open an input file and an output file,
     * Wrap the symmetric and MAC keys using public key
     * write a header to the output that identifies the two wrapped keys
     * and public key
     * loop until EOF(input)
     *    read a buffer of plaintext from input file,
     *    MAC it, append the MAC to the plaintext
     *    encrypt it using CBC, using previously created IV,
     *    store the last block of ciphertext as the new IV,
     *    write the cipher text to intermediate file
     *    close files
     *    report success
     */
    SECStatus           rv;
    SECKEYPublicKey    *pubKey        = NULL;
    SECItem            *pubKeyData    = NULL;
    PRFileDesc         *inFile        = NULL;
    PRFileDesc         *headerFile    = NULL;
    PRFileDesc         *encFile       = NULL;

    unsigned char      *encKeyId = (unsigned char *) "Encrypt Key";
    unsigned char      *macKeyId = (unsigned char *) "MAC Key";
    SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) };
    SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) };

    unsigned char       iv[BLOCKSIZE];
    SECItem             ivItem;
    PK11SymKey         *encKey        = NULL;
    PK11SymKey         *macKey        = NULL;
    SECItem            *wrappedEncKey = NULL;
    SECItem            *wrappedMacKey = NULL;
    unsigned char       c;

    pubKey = ExtractPublicKeyFromCertRequest(certReqFileName, ascii);
    if (pubKey == NULL) {
        PR_fprintf(PR_STDERR, "Error while getting RSA public key\n");
        rv = SECFailure;
        goto cleanup;
    }
    /* generate a symmetric AES key as a token object. */
    encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata);
    if (encKey == NULL) {
        PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* generate a second key to use for MACing, also a token object. */
    macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, &macKeyID, pwdata);
    if (macKey == NULL) {
        PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n");
        rv = SECFailure;
        goto cleanup;
    }

    /* Wrap encrypt key */
    rv = WrapKey(encKey, pubKey, &wrappedEncKey);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n");
        goto cleanup;
    }

    /* Wrap Mac key */
    rv = WrapKey(macKey, pubKey, &wrappedMacKey);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Error while wrapping Mac key\n");
        goto cleanup;
    }

    if (noiseFileName) {
        rv = SeedFromNoiseFile(noiseFileName);
        if (rv != SECSuccess) {
            PORT_SetError(PR_END_OF_FILE_ERROR);
            return SECFailure;
        }
        rv = PK11_GenerateRandom(iv, BLOCKSIZE);
        if (rv != SECSuccess) {
            goto cleanup;
        }

    } else {
        /* generate a random value to use as IV for AES CBC */
        GenerateRandom(iv, BLOCKSIZE);
    }

    headerFile = PR_Open(headerFileName,
                         PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660);
    if (!headerFile) {
        PR_fprintf(PR_STDERR,
                   "Unable to open \"%s\" for writing.\n",
                   headerFileName);
        return SECFailure;
    }
    encFile = PR_Open(encryptedFileName,
                      PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660);
    if (!encFile) {
        PR_fprintf(PR_STDERR,
                   "Unable to open \"%s\" for writing.\n",
                   encryptedFileName);
        return SECFailure;
    }
    /* write to a header file the IV and the CKA_IDs
     * identifying the two keys
     */
    ivItem.type = siBuffer;
    ivItem.data = iv;
    ivItem.len = BLOCKSIZE;

    rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n",
                   headerFileName);
        goto cleanup;
    }

    rv = WriteToHeaderFile(wrappedEncKey->data, wrappedEncKey->len, SYMKEY, headerFile);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Error writing wrapped AES key to cipher file - %s\n",
        encryptedFileName);
        goto cleanup;
    }
    rv = WriteToHeaderFile(wrappedMacKey->data, wrappedMacKey->len, MACKEY, headerFile);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Error writing wrapped MAC key to cipher file - %s\n",
                   headerFileName);
        goto cleanup;
    }

    pubKeyData = SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey);
    rv = WriteToHeaderFile(pubKeyData->data, pubKeyData->len, PUBKEY, headerFile);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Error writing wrapped AES key to cipher file - %s\n",
                   headerFileName);
        goto cleanup;
    }

    /*  Open the input file.  */
    inFile = PR_Open(inFileName, PR_RDONLY, 0);
    if (!inFile) {
        PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n",
                   inFileName);
        return SECFailure;
    }

    /* Macing and Encryption */
    if (rv == SECSuccess) {
        rv = EncryptAndMac(inFile, headerFile, encFile,
                encKey, macKey, ivItem.data, ivItem.len, ascii);
        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "Failed : Macing and Encryption\n");
            goto cleanup;
        }
    }

cleanup:
    if (inFile) {
        PR_Close(inFile);
    }
    if (headerFile) {
        PR_Close(headerFile);
    }
    if (encFile) {
        PR_Close(encFile);
    }
    if (encKey) {
        PK11_FreeSymKey(encKey);
    }
    if (macKey) {
        PK11_FreeSymKey(macKey);
    }
    if (wrappedEncKey) {
        SECITEM_FreeItem(wrappedEncKey, PR_TRUE);
    }
    if (wrappedMacKey) {
        SECITEM_FreeItem(wrappedMacKey, PR_TRUE);
    }
    if (pubKey) {
        SECKEY_DestroyPublicKey(pubKey);
    }
    if (pubKeyData) {
        SECITEM_FreeItem(pubKeyData, PR_TRUE);
    }
    return rv;
}

/*
 * Create certificate request with subject
 */
SECStatus CreateCertificateRequest(PK11SlotInfo *slot,
                                   const char   *dbdir,
                                   secuPWData   *pwdata,
                                   CERTName     *subject,
                                   const char   *certReqFileName,
                                   PRBool       ascii)
{
    SECStatus rv;
    SECKEYPrivateKey    *privkey         = NULL;
    SECKEYPublicKey     *pubkey          = NULL;
    KeyType             keytype          = rsaKey;
    int                 keysize          = DEFAULT_KEY_BITS;
    int                 publicExponent   = 0x010001;
    SECOidTag           hashAlgTag       = SEC_OID_UNKNOWN;

    privkey = GeneratePrivateKey(keytype, slot, keysize,
                                 publicExponent, NULL,
                                 &pubkey, NULL, pwdata);
    if (privkey == NULL) {
        PR_fprintf(PR_STDERR, "unable to generate key(s)\n");
        rv = SECFailure;
        goto cleanup;
    }
    privkey->wincx = pwdata;
    PORT_Assert(pubkey != NULL);

    rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject,
                 ascii, certReqFileName);

    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "Failed to create Certificate Request\n");
    }
cleanup:
    if (privkey) {
        SECKEY_DestroyPrivateKey(privkey);
    }
    if (pubkey) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    return rv;
}

/*
 * This example illustrates basic encryption/decryption and MACing
 * Generates the RSA key pair as token object and outputs public key as cert request.
 * Generates the encryption/mac keys as session objects.
 * Encrypts/MACs the input file using encryption keys and outputs the encrypted
 * contents into intermediate header file.
 * Extracts the public key from cert request file and Wraps the encryption keys using
 * RSA public key and outputs wrapped keys and public key into intermediate header file.
 * Reads the intermediate headerfile for wrapped keys,RSA public key and encrypted
 * contents and decrypts into output file.
 *
 * How this sample is different from sample 4 ?
 *
 * 1. Generate same keys as sample 4, outputs public key as cert request.
 * 2. Like sample 4, except that it reads in public key from cert request file instead
 *    of looking it up by label name, and writes public key into header instead of a
 *    label name. Rest is the same.
 * 3. Like sample 4, except that it reads in RSA public key, and then finds matching
 *    private key (by key ID).  Rest is the same.
 */
int
main(int argc, char **argv)
{
    SECStatus           rv;
    SECStatus           rvShutdown;
    PLOptState          *optstate;
    PLOptStatus         status;
    char                headerFileName[50];
    char                encryptedFileName[50];
    PK11SlotInfo        *slot                = NULL;
    PRBool              ascii                = PR_FALSE;
    CommandType         cmd                  = UNKNOWN;
    PRFileDesc          *inFile              = NULL;
    PRFileDesc          *outFile             = NULL;
    char                *subjectStr          = NULL;
    CERTName            *subject             = NULL;
    const char          *dbdir               = NULL;
    const char          *inFileName          = NULL;
    const char          *outFileName         = NULL;
    const char          *certReqFileName     = NULL;
    const char          *noiseFileName       = NULL;
    secuPWData          pwdata               = { PW_NONE, 0 };

    char * progName = strrchr(argv[0], '/');
    progName = progName ? progName + 1 : argv[0];

    /* Parse command line arguments */
    optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a:s:r:");
    while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
        switch (optstate->option) {
        case 'a':
            ascii = PR_TRUE;
            break;
        case 'c':
            cmd = option2Command(optstate->value);
            break;
        case 'd':
            dbdir = strdup(optstate->value);
            break;
        case 'f':
            pwdata.source = PW_FROMFILE;
            pwdata.data = strdup(optstate->value);
            break;
        case 'p':
            pwdata.source = PW_PLAINTEXT;
            pwdata.data = strdup(optstate->value);
            break;
        case 'i':
            inFileName = strdup(optstate->value);
            break;
        case 'o':
            outFileName = strdup(optstate->value);
            break;
        case 'r':
            certReqFileName = strdup(optstate->value);
            break;
        case 's':
            subjectStr  = strdup(optstate->value);
            subject     = CERT_AsciiToName(subjectStr);
            break;
        case 'z':
            noiseFileName = strdup(optstate->value);
            break;
        default:
            Usage(progName);
            break;
        }
    }
    PL_DestroyOptState(optstate);

    if (cmd == UNKNOWN || !dbdir) {
        Usage(progName);
    }

    /* For intermediate header file, choose filename as inputfile name
       with extension ".header" */
    strcpy(headerFileName, progName);
    strcat(headerFileName, ".header");

    /* For intermediate encrypted file, choose filename as inputfile name
       with extension ".enc" */
    strcpy(encryptedFileName, progName);
    strcat(encryptedFileName, ".enc");
    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);

    /* Open DB for read/write and authenticate to it. */
    rv = NSS_InitReadWrite(dbdir);
    if (rv != SECSuccess) {
        PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n");
        goto cleanup;
    }

    PK11_SetPasswordFunc(GetModulePassword);
    slot = PK11_GetInternalKeySlot();
    rv = PK11_Authenticate(slot, PR_TRUE, &pwdata);
    if (rv != SECSuccess) {
         PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n",
                    PK11_GetTokenName(slot));
         goto cleanup;
    }

    switch (cmd) {
    case GEN_CSR:

        /* Validate command for Generate CSR */
        if (!certReqFileName || !subject) {
            Usage(progName);
        }
        /*
         * Generate the cert request and save it
         * in a file so public key can be retrieved later to wrap the symmetric key
         */
        rv = CreateCertificateRequest(slot, dbdir, &pwdata, subject, certReqFileName, ascii);
        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "Create Certificate Request: Failed\n");
            goto cleanup;
        }
        break;
    case ENCRYPT:
        /* Validate command for Encrypt */
        if (!certReqFileName && !inFileName) {
            Usage(progName);
        }

        /*
         * Read cert request from a file and extract public key
         * Generates an AES encryption key, session object
         * Generates a MAC key, session object
         * Wraps each of those keys with RSA public key
         * Write wrapped keys and public key into intermediate header file
         * Encryption and MACing loop
         * Destroy session keys
         * Close files
         */
        rv = EncryptFile(slot, inFileName, certReqFileName,
                         headerFileName, encryptedFileName,
                         noiseFileName, &pwdata, ascii);
        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "EncryptFile : Failed\n");
            return SECFailure;
        }
        break;
    case DECRYPT:
        /* Validate command for Decrypt */
        if (!inFileName && !outFileName) {
            Usage(progName);
        }
        /*
         * Reads intermediate header including public key and wrapped keys
         * Finds RSA private key corresponding to the public key
         * unwraps two keys, creating session key objects
         * Decryption and MAC checking loop to write to output file
         * Destroy session keys
         * CLose files
         */
        rv = DecryptFile(slot,
                  outFileName, headerFileName,
                  encryptedFileName, &pwdata, ascii);
        if (rv != SECSuccess) {
            PR_fprintf(PR_STDERR, "DecryptFile : Failed\n");
            return SECFailure;
        }
        break;
    }

cleanup:
    if (slot) {
        PK11_FreeSlot(slot);
    }
    rvShutdown = NSS_Shutdown();
    if (rvShutdown != SECSuccess) {
        PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n");
        rv = SECFailure;
    }
    PR_Cleanup();

    return rv;
}