Enc Dec MAC Using Key Wrap CertReq PKCS10 CSR

NSS Sample Code 6: Encryption/Decryption and MAC and output Public as a PKCS 11 CSR.

Generates encryption/mac keys and outputs public key as pkcs11 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 <prthread.h>
#include <plgetopt.h>
#include <prerror.h>
#include <prinit.h>
#include <prlog.h>
#include <prtypes.h>
#include <plstr.h>

/* NSS headers */
#include <keyhi.h>
#include <pk11priv.h>

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

/* Constants */
#define BLOCKSIZE 32
#define MODBLOCKSIZE 128
#define DEFAULT_KEY_BITS 1024

/* Header file Constants */
#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-----"
#define NS_CERT_ENC_HEADER "-----BEGIN CERTIFICATE FOR ENCRYPTION-----"
#define NS_CERT_ENC_TRAILER "-----END CERTIFICATE FOR ENCRYPTION-----"
#define NS_CERT_VFY_HEADER "-----BEGIN CERTIFICATE FOR SIGNATURE VERIFICATION-----"
#define NS_CERT_VFY_TRAILER "-----END CERTIFICATE FOR SIGNATURE VERIFICATION-----"
#define NS_SIG_HEADER "-----BEGIN SIGNATURE-----"
#define NS_SIG_TRAILER "-----END SIGNATURE-----"
#define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----"
#define NS_CERT_TRAILER "-----END CERTIFICATE-----"

/* sample 6 commands */
typedef enum {
GENERATE_CSR,
ADD_CERT_TO_DB,
SAVE_CERT_TO_HEADER,
ENCRYPT,
DECRYPT,
SIGN,
VERIFY,
UNKNOWN
} CommandType;

typedef enum {
SYMKEY = 0,
MACKEY = 1,
IV = 2,
MAC = 3,
PAD = 4,
PUBKEY = 5,
LAB = 6,
CERTENC= 7,
CERTVFY= 8,
SIG = 9
} HeaderType;


/*
* Print usage message and exit
*/
static void
Usage(const char *progName)
{
fprintf(stderr, "\nUsage: %s %s %s %s %s %s %s %s %s %s\n\n",
progName,
" -<G|A|H|E|DS|V> -d <dbdirpath> ",
"[-p <dbpwd> | -f <dbpwdfile>] [-z <noisefilename>] [-a <\"\">]",
"-s <subject> -r <csr> | ",
"-n <nickName> -t <trust> -c <cert> [ -r <csr> -u <issuerNickname> [-x <\"\">] -m <serialNumber> ] | ",
"-n <nickName> -b <headerfilename> | ",
"-b <headerfilename> -i <ipfilename> -e <encryptfilename> | ",
"-b <headerfilename> -i <ipfilename> | ",
"-b <headerfilename> -i <ipfilename> | ",
"-b <headerfilename> -e <encryptfilename> -o <opfilename> \n");
fprintf(stderr, "commands:\n\n");
fprintf(stderr, "%s %s\n --for generating cert request (for CA also)\n\n",
progName, "-G -s <subject> -r <csr>");
fprintf(stderr, "%s %s\n --to input and store cert (for CA also)\n\n",
progName, "-A -n <nickName> -t <trust> -c <cert> [ -r <csr> -u <issuerNickname> [-x <\"\">] -m <serialNumber> ]");
fprintf(stderr, "%s %s\n --to put cert in header\n\n",
progName, "-H -n <nickname> -b <headerfilename> [-v <\"\">]");
fprintf(stderr, "%s %s\n --to find public key from cert in header and encrypt\n\n",
progName, "-E -b <headerfilename> -i <ipfilename> -e <encryptfilename> ");
fprintf(stderr, "%s %s\n --decrypt using corresponding private key \n\n",
progName, "-D -b <headerfilename> -e <encryptfilename> -o <opfilename>");
fprintf(stderr, "%s %s\n --Sign using private key \n\n",
progName, "-S -b <headerfilename> -i <infilename> ");
fprintf(stderr, "%s %s\n --Verify using public key \n\n",
progName, "-V -b <headerfilename> -i <ipfilename> ");
fprintf(stderr, "options:\n\n");
fprintf(stderr, "%-30s - db directory path\n\n",
"-d <dbdirpath>");
fprintf(stderr, "%-30s - db password [optional]\n\n",
"-p <dbpwd>");
fprintf(stderr, "%-30s - db password file [optional]\n\n",
"-f <dbpwdfile>");
fprintf(stderr, "%-30s - noise file name [optional]\n\n",
"-z <noisefilename>");
fprintf(stderr, "%-30s - input file name\n\n",
"-i <ipfilename>");
fprintf(stderr, "%-30s - header file name\n\n",
"-b <headerfilename>");
fprintf(stderr, "%-30s - encrypt file name\n\n",
"-e <encryptfilename>");
fprintf(stderr, "%-30s - output file name\n\n",
"-o <opfilename>");
fprintf(stderr, "%-30s - certificate serial number\n\n",
"-m <serialNumber>");
fprintf(stderr, "%-30s - certificate nickname\n\n",
"-n <nickname>");
fprintf(stderr, "%-30s - certificate trust\n\n",
"-t <trustargs>");
fprintf(stderr, "%-30s - certificate issuer nickname\n\n",
"-u <issuerNickname>");
fprintf(stderr, "%-30s - certificate signing request \n\n",
"-r <csr>");
fprintf(stderr, "%-30s - generate a self-signed cert [optional]\n\n",
"-x");
fprintf(stderr, "%-30s - to enable ascii [optional]\n\n",
"-a");
fprintf(stderr, "%-30s - to save certificate to header file as sig verification [optional]\n\n",
"-v");
exit(-1);
}

/*
* Validate the options used for Generate CSR command
*/
static void
ValidateGenerateCSRCommand(const char *progName,
const char *dbdir,
CERTName *subject,
const char *subjectStr,
const char *certReqFileName)
{
PRBool validationFailed = PR_FALSE;
if (!subject) {
PR_fprintf(PR_STDERR, "%s -G -d %s -s: improperly formatted name: \"%s\"\n",
progName, dbdir, subjectStr);
validationFailed = PR_TRUE;
}
if (!certReqFileName) {
PR_fprintf(PR_STDERR, "%s -G -d %s -s %s -r: certificate request file name not found\n",
progName, dbdir, subjectStr);
validationFailed = PR_TRUE;
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-G -d <dbdirpath> -s <subject> -r <csr> \n");
exit(-1);
}
}

/*
* Validate the options used for Add Cert to DB command
*/
static void
ValidateAddCertToDBCommand(const char *progName,
const char *dbdir,
const char *nickNameStr,
const char *trustStr,
const char *certFileName,
const char *certReqFileName,
const char *issuerNameStr,
const char *serialNumberStr,
PRBool selfsign)
{
PRBool validationFailed = PR_FALSE;
if (!nickNameStr) {
PR_fprintf(PR_STDERR, "%s -A -d %s -n : nick name is missing\n",
progName, dbdir);
validationFailed = PR_TRUE;
}
if (!trustStr) {
PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t: trust flag is missing\n",
progName, dbdir, nickNameStr);
validationFailed = PR_TRUE;
}
if (!certFileName) {
PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c: certificate file name not found\n",
progName, dbdir, nickNameStr, trustStr, serialNumberStr, certReqFileName);
validationFailed = PR_TRUE;
}
if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) {
if (!certReqFileName) {
PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r: certificate file or certificate request file is not found\n",
progName, dbdir, nickNameStr, trustStr, certFileName);
validationFailed = PR_TRUE;
}
if (!selfsign && !issuerNameStr) {
PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u : issuer name is missing\n",
progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName);
validationFailed = PR_TRUE;
}
if (!serialNumberStr) {
PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u %s -m : serial number is missing\n",
progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName, issuerNameStr);
validationFailed = PR_TRUE;
}
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
" -A -d <dbdirpath> -n <nickName> -t <trust> -c <cert> \n");
fprintf(stderr, " OR\n");
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-A -d <dbdirpath> -n <nickName> -t <trust> -c <cert> -r <csr> -u <issuerNickname> -m <serialNumber> [-x <\"\">] \n");
exit(-1);
}
}

/*
* Validate the options used for Save Cert To Header command
*/
static void
ValidateSaveCertToHeaderCommand(const char *progName,
const char *dbdir,
const char *nickNameStr,
const char *headerFileName)
{
PRBool validationFailed = PR_FALSE;
if (!nickNameStr) {
PR_fprintf(PR_STDERR, "%s -S -d %s -n : nick name is missing\n",
progName, dbdir);
validationFailed = PR_TRUE;
}
if (!headerFileName) {
PR_fprintf(PR_STDERR, "%s -S -d %s -n %s -b : header file name is not found\n",
progName, dbdir, nickNameStr);
validationFailed = PR_TRUE;
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-S -d <dbdirpath> -n <nickname> -b <headerfilename> [-v <\"\">]\n");
exit(-1);
}
}

/*
* Validate the options used for Encrypt command
*/
static void
ValidateEncryptCommand(const char *progName,
const char *dbdir,
const char *nickNameStr,
const char *headerFileName,
const char *inFileName,
const char *encryptedFileName)
{
PRBool validationFailed = PR_FALSE;
if (!nickNameStr) {
PR_fprintf(PR_STDERR, "%s -E -d %s -n : nick name is missing\n",
progName, dbdir);
validationFailed = PR_TRUE;
}
if (!headerFileName) {
PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b : header file name is not found\n",
progName, dbdir, nickNameStr);
validationFailed = PR_TRUE;
}
if (!inFileName) {
PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i : input file name is not found\n",
progName, dbdir, nickNameStr, headerFileName);
validationFailed = PR_TRUE;
}
if (!encryptedFileName) {
PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i %s -e : encrypt file name is not found\n",
progName, dbdir, nickNameStr, headerFileName, inFileName);
validationFailed = PR_TRUE;
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-E -d <dbdirpath> -b <headerfilename> -i <ipfilename> -e <encryptfilename> -n <nickname> \n");
exit(-1);
}
}

/*
* Validate the options used for Sign command
*/
static void
ValidateSignCommand(const char *progName,
const char *dbdir,
const char *nickNameStr,
const char *headerFileName,
const char *inFileName)
{
PRBool validationFailed = PR_FALSE;
if (!nickNameStr) {
PR_fprintf(PR_STDERR, "%s -I -d %s -n : nick name is missing\n",
progName, dbdir);
validationFailed = PR_TRUE;
}
if (!headerFileName) {
PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b : header file name is not found\n",
progName, dbdir, nickNameStr);
validationFailed = PR_TRUE;
}
if (!inFileName) {
PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b %s -i : input file name is not found\n",
progName, dbdir, nickNameStr, headerFileName);
validationFailed = PR_TRUE;
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-I -d <dbdirpath> -b <headerfilename> -i <ipfilename> -n <nickname> \n");
exit(-1);
}
}

/*
* Validate the options used for verify command
*/
static void
ValidateVerifyCommand(const char *progName,
const char *dbdir,
const char *headerFileName,
const char *inFileName)
{
PRBool validationFailed = PR_FALSE;
if (!headerFileName) {
PR_fprintf(PR_STDERR, "%s -V -d %s -b : header file name is not found\n",
progName, dbdir);
validationFailed = PR_TRUE;
}
if (!inFileName) {
PR_fprintf(PR_STDERR, "%s -I -d %s -b %s -i : input file name is not found\n",
progName, dbdir, headerFileName);
validationFailed = PR_TRUE;
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-I -d <dbdirpath> -b <headerfilename> -i <ipfilename> \n");
exit(-1);
}
}

/*
* Validate the options used for Decrypt command
*/
static void
ValidateDecryptCommand(const char *progName,
const char *dbdir,
const char *headerFileName,
const char *encryptedFileName,
const char *outFileName)
{
PRBool validationFailed = PR_FALSE;
if (!headerFileName) {
PR_fprintf(PR_STDERR, "%s -D -d %s -b : header file name is not found\n",
progName, dbdir);
validationFailed = PR_TRUE;
}
if (!encryptedFileName) {
PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e : encrypt file name is not found\n",
progName, dbdir, headerFileName);
validationFailed = PR_TRUE;
}
if (!outFileName) {
PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e %s -o : output file name is not found\n",
progName, dbdir, headerFileName, encryptedFileName);
validationFailed = PR_TRUE;
}
if (validationFailed) {
fprintf(stderr, "\nUsage: %s %s \n\n", progName,
"-D -d <dbdirpath> -b <headerfilename> -e <encryptfilename> -o <opfilename>\n");
exit(-1);
}
}

/*
* Sign the contents of input file using private key and
* return result as SECItem
*/
SECStatus
SignData(const char *inFileName, SECKEYPrivateKey *pk, SECItem *res)
{
SECStatus rv = SECFailure;
unsigned int nb;
unsigned char ibuf[4096];
PRFileDesc *inFile = NULL;
SGNContext *sgn = NULL;

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

/* Sign using private key */

sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, pk);
if (!sgn) {
PR_fprintf(PR_STDERR, "unable to create context for signing\n");
rv = SECFailure;
goto cleanup;
}

rv = SGN_Begin(sgn);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "problem while SGN_Begin\n");
goto cleanup;
}
while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) {
rv = SGN_Update(sgn, ibuf, nb);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "problem while SGN_Update\n");
goto cleanup;
}
}
rv = SGN_End(sgn, res);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "problem while SGN_End\n");
goto cleanup;
}
cleanup:
if (inFile) {
PR_Close(inFile);
}
if (sgn) {
SGN_DestroyContext(sgn, PR_TRUE);
}
return rv;
}

/*
* Verify the signature using public key
*/
SECStatus
VerifyData(const char *inFileName, SECKEYPublicKey *pk,
SECItem *sigItem, secuPWData *pwdata)
{
unsigned int nb;
unsigned char ibuf[4096];
SECStatus rv = SECFailure;
VFYContext *vfy = NULL;
PRFileDesc *inFile = NULL;

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

vfy = VFY_CreateContext(pk,
sigItem,
SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,
pwdata);
if (!vfy) {
PR_fprintf(PR_STDERR, "unable to create context for verifying signature\n");
rv = SECFailure;
goto cleanup;
}
rv = VFY_Begin(vfy);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "problem while VFY_Begin\n");
goto cleanup;
}
while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) {
rv = VFY_Update(vfy, ibuf, nb);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "problem while VFY_Update\n");
goto cleanup;
}
}
rv = VFY_End(vfy);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "problem while VFY_End\n");
goto cleanup;
}

cleanup:
if (inFile) {
PR_Close(inFile);
}
if (vfy) {
VFY_DestroyContext(vfy, PR_TRUE);
}
return rv;
}

/*
* Write Cryptographic parameters to header file
*/
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 CERTENC:
header = NS_CERT_ENC_HEADER;
trailer = NS_CERT_ENC_TRAILER;
break;
case CERTVFY:
header = NS_CERT_VFY_HEADER;
trailer = NS_CERT_VFY_TRAILER;
break;
case SIG:
header = NS_SIG_HEADER;
trailer = NS_SIG_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);
PrintAsHex(outFile, buf, len);
PR_fprintf(outFile, "%s\n\n", trailer);
return SECSuccess;
}

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

outbuf.type = siBuffer;
file = PR_Open(fileName, PR_RDONLY, 0);
if (!file) {
PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName);
rv = SECFailure;
goto cleanup;
}
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;
case CERTENC:
header = NS_CERT_ENC_HEADER;
trailer = NS_CERT_ENC_TRAILER;
break;
case CERTVFY:
header = NS_CERT_VFY_HEADER;
trailer = NS_CERT_VFY_TRAILER;
break;
case SIG:
header = NS_SIG_HEADER;
trailer = NS_SIG_TRAILER;
break;
default:
rv = SECFailure;
goto cleanup;
}

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 */
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);
rv = SECFailure;
goto cleanup;
}
} else {
/* headers didn't exist */
char *trail = NULL;
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);
rv = SECFailure;
goto cleanup;
}
}
}
HexToBuf(body, item, isHexData);
cleanup:
if (file) {
PR_Close(file);
}
return rv;
}

/*
* Generate the private key
*/
SECKEYPrivateKey *
GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
int publicExponent, const char *noise,
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;
}

/*
* Get the certificate request from CSR
*/
static CERTCertificateRequest *
GetCertRequest(char *inFileName, PRBool ascii)
{
CERTSignedData signedData;
SECItem reqDER;
CERTCertificateRequest *certReq = NULL;
SECStatus rv = SECSuccess;
PRArenaPool *arena = NULL;

reqDER.data = 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 */);
if (reqDER.data) {
SECITEM_FreeItem(&reqDER, PR_FALSE);
}

cleanup:
if (rv) {
PR_fprintf(PR_STDERR, "bad certificate request\n");
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
certReq = NULL;
}
return certReq;
}

/*
* Sign Cert
*/
static SECItem *
SignCert(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool selfsign, SECOidTag hashAlgTag,
SECKEYPrivateKey *privKey, char *issuerNickName, void *pwarg)
{
SECItem der;
SECStatus rv;
SECOidTag algID;
void *dummy;
PRArenaPool *arena = NULL;
SECItem *result = NULL;
SECKEYPrivateKey *caPrivateKey = NULL;

if (!selfsign) {
CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg);
if ((CERTCertificate *)NULL == issuer) {
PR_fprintf(PR_STDERR, "unable to find issuer with nickname %s\n",
issuerNickName);
goto cleanup;
}
privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg);
CERT_DestroyCertificate(issuer);
if (caPrivateKey == NULL) {
PR_fprintf(PR_STDERR, "unable to retrieve key %s\n",
issuerNickName);
goto cleanup;
}
}
arena = cert->arena;
algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag);
if (algID == SEC_OID_UNKNOWN) {
PR_fprintf(PR_STDERR, "Unknown key or hash type for issuer.\n");
goto cleanup;
}
rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Could not set signature algorithm id.\n%s\n",
PORT_ErrorToString(rv));
goto cleanup;
}

/* we only deal with cert v3 here */
*(cert->version.data) = 2;
cert->version.len = 1;

der.len = 0;
der.data = NULL;
dummy = SEC_ASN1EncodeItem (arena, &der, cert,
SEC_ASN1_GET(CERT_CertificateTemplate));
if (!dummy) {
PR_fprintf(PR_STDERR, "Could not encode certificate.\n");
goto cleanup;
}

result = (SECItem *) PORT_ArenaZAlloc (arena, sizeof (SECItem));
if (result == NULL) {
PR_fprintf(PR_STDERR, "Could not allocate item for certificate data.\n");
goto cleanup;
}

rv = SEC_DerSignData(arena, result, der.data, der.len, privKey, algID);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Could not sign encoded certificate data : %s\n",
PORT_ErrorToString(rv));
/* result allocated out of the arena, it will be freed
* when the arena is freed */
result = NULL;
goto cleanup;
}
cert->derCert = *result;
cleanup:
if (caPrivateKey) {
SECKEY_DestroyPrivateKey(caPrivateKey);
}
return result;
}

/*
* MakeV1Cert
*/
static CERTCertificate *
MakeV1Cert(CERTCertDBHandle *handle,
CERTCertificateRequest *req,
char * issuerNickName,
PRBool selfsign,
unsigned int serialNumber,
int warpmonths,
int validityMonths)
{
PRExplodedTime printableTime;
PRTime now;
PRTime after;
CERTValidity *validity = NULL;
CERTCertificate *issuerCert = NULL;
CERTCertificate *cert = NULL;

if ( !selfsign ) {
issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName);
if (!issuerCert) {
PR_fprintf(PR_STDERR, "could not find certificate named %s\n",
issuerNickName);
goto cleanup;
}
}

now = PR_Now();
PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
if ( warpmonths ) {
printableTime.tm_month += warpmonths;
now = PR_ImplodeTime (&printableTime);
PR_ExplodeTime (now, PR_GMTParameters, &printableTime);
}
printableTime.tm_month += validityMonths;
after = PR_ImplodeTime (&printableTime);

/* note that the time is now in micro-second unit */
validity = CERT_CreateValidity (now, after);
if (validity) {
cert = CERT_CreateCertificate(serialNumber,
(selfsign ? &req->subject : &issuerCert->subject),
validity, req);

CERT_DestroyValidity(validity);
}
cleanup:
if ( issuerCert ) {
CERT_DestroyCertificate (issuerCert);
}
return cert;
}

/*
* Add a certificate to the nss database
*/
SECStatus
AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle,
const char *name, char *trusts, char *inFileName,
PRBool ascii, PRBool emailcert, void *pwdata)
{
SECItem certDER;
SECStatus rv;
CERTCertTrust *trust = NULL;
CERTCertificate *cert = NULL;

certDER.data = NULL;

/* Read in the entire file specified with the -i argument */
rv = ReadDERFromFile(&certDER, inFileName, ascii);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "unable to read input file %s : %s\n",
inFileName, PORT_ErrorToString(rv));
goto cleanup;
}

/* Read in an ASCII cert and return a CERTCertificate */
cert = CERT_DecodeCertFromPackage((char *)certDER.data, certDER.len);
if (!cert) {
PR_fprintf(PR_STDERR, "could not obtain certificate from file\n");
rv = SECFailure;
goto cleanup;
}

/* Create a cert trust */
trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
if (!trust) {
PR_fprintf(PR_STDERR, "unable to allocate cert trust\n");
rv = SECFailure;
goto cleanup;
}

rv = CERT_DecodeTrustString(trust, trusts);
if (rv) {
PR_fprintf(PR_STDERR, "unable to decode trust string\n");
rv = SECFailure;
goto cleanup;
}

rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE);
if (rv != SECSuccess) {
/* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have
* been coded to take a password arg. */
if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
rv = PK11_Authenticate(slot, PR_TRUE, pwdata);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n",
PK11_GetTokenName(slot), PORT_ErrorToString(rv));
rv = SECFailure;
goto cleanup;
}
rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE,
name, PR_FALSE);
}
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR,
"could not add certificate to token or database : %s\n",
PORT_ErrorToString(rv));
rv = SECFailure;
goto cleanup;
}
}
rv = CERT_ChangeCertTrust(handle, cert, trust);
if (rv != SECSuccess) {
if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
rv = PK11_Authenticate(slot, PR_TRUE, pwdata);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n",
PK11_GetTokenName(slot), PORT_ErrorToString(rv));
rv = SECFailure;
goto cleanup;
}
rv = CERT_ChangeCertTrust(handle, cert, trust);
}
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "could not change trust on certificate : %s\n",
PORT_ErrorToString(rv));
rv = SECFailure;
goto cleanup;
}
}

if (emailcert) {
CERT_SaveSMimeProfile(cert, NULL, pwdata);
}

cleanup:
if (cert) {
CERT_DestroyCertificate (cert);
}
if (trust) {
PORT_Free(trust);
}
if (certDER.data) {
PORT_Free(certDER.data);
}
return rv;
}

/*
* Create a certificate
*/
static SECStatus
CreateCert(
CERTCertDBHandle *handle,
PK11SlotInfo *slot,
char * issuerNickName,
char *inFileName,
char *outFileName,
SECKEYPrivateKey **selfsignprivkey,
void *pwarg,
SECOidTag hashAlgTag,
unsigned int serialNumber,
int warpmonths,
int validityMonths,
const char *dnsNames,
PRBool ascii,
PRBool selfsign)
{
void *extHandle;
SECItem reqDER;
CERTCertExtension **CRexts;
SECStatus rv = SECSuccess;
CERTCertificate *subjectCert = NULL;
CERTCertificateRequest *certReq = NULL;
PRFileDesc *outFile = NULL;
SECItem *certDER = NULL;

reqDER.data = NULL;
outFile = PR_Open(outFileName,
PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00660);

/* Create a cert request object from the input cert request der */
certReq = GetCertRequest(inFileName, ascii);
if (certReq == NULL) {
rv = SECFailure;
goto cleanup;
}
subjectCert = MakeV1Cert(handle, certReq, issuerNickName, selfsign,
serialNumber, warpmonths, validityMonths);
if (subjectCert == NULL) {
rv = SECFailure;
goto cleanup;
}

extHandle = CERT_StartCertExtensions (subjectCert);
if (extHandle == NULL) {
rv = SECFailure;
goto cleanup;
}

if (certReq->attributes != NULL &&
certReq->attributes[0] != NULL &&
certReq->attributes[0]->attrType.data != NULL &&
certReq->attributes[0]->attrType.len > 0 &&
SECOID_FindOIDTag(&certReq->attributes[0]->attrType)
== SEC_OID_PKCS9_EXTENSION_REQUEST) {
rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv));
goto cleanup;
}
rv = CERT_MergeExtensions(extHandle, CRexts);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv));
goto cleanup;
}
}

CERT_FinishExtensions(extHandle);

/* self-signing a cert request, find the private key */
if (*selfsignprivkey == NULL) {
*selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg);
if (!*selfsignprivkey) {
PR_fprintf(PR_STDERR, "Failed to locate private key.\n");
rv = SECFailure;
goto cleanup;
}
}

certDER = SignCert(handle, subjectCert, selfsign, hashAlgTag,
*selfsignprivkey, issuerNickName,pwarg);
if (certDER) {
if (ascii) {
PR_fprintf(outFile, "%s\n%s\n%s\n", NS_CERT_HEADER,
BTOA_DataToAscii(certDER->data, certDER->len),
NS_CERT_TRAILER);
} else {
PR_Write(outFile, certDER->data, certDER->len);
}
}
if (rv != SECSuccess) {
PRErrorCode perr = PR_GetError();
PR_fprintf(PR_STDERR, "unable to create cert %s\n",
perr);
}
cleanup:
if (outFile) {
PR_Close(outFile);
}
if (*selfsignprivkey) {
SECKEY_DestroyPrivateKey(*selfsignprivkey);
}
if (certReq) {
CERT_DestroyCertificateRequest(certReq);
}
if (subjectCert) {
CERT_DestroyCertificate(subjectCert);
}
return rv;
}

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

/* If the certificate request file already exists, delete it */
if (PR_Access(certReqFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) {
PR_Delete(certReqFileName);
}
/* 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);
} 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 (outFile) {
PR_Close(outFile);
}
if (privk) {
SECKEY_DestroyPrivateKey(privk);
}
if (pubk) {
SECKEY_DestroyPublicKey(pubk);
}
return rv;
}

/*
* Create certificate request with subject
*/
SECStatus CreateCertRequest(PK11SlotInfo *slot,
secuPWData *pwdata,
CERTName *subject,
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:
return rv;
}

/*
* Creates the certificate using CSR and adds the certificate to DB
*/
SECStatus AddCertificateToDB(PK11SlotInfo *slot,
secuPWData *pwdata,
char *certReqFileName,
char *certFileName,
char *issuerNameStr,
CERTCertDBHandle *certHandle,
const char *nickNameStr,
char *trustStr,
unsigned int serialNumber,
PRBool selfsign,
PRBool ascii)
{
SECStatus rv;
SECKEYPrivateKey *privkey = NULL;
SECKEYPublicKey *pubkey = NULL;
SECOidTag hashAlgTag = SEC_OID_UNKNOWN;

if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) {
rv = CreateCert(certHandle, slot, issuerNameStr,
certReqFileName, certFileName, &privkey, &pwdata, hashAlgTag,
serialNumber, 0, 3, NULL, ascii, selfsign);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Failed to create Certificate\n");
goto cleanup;
}
}
rv = AddCert(slot, certHandle, nickNameStr,
trustStr, certFileName, ascii, 0, &pwdata);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Failed to add Certificate\n");
}
cleanup:
return rv;
}

/*
* Finds the certificate using nickname and saves it to the header file
*/
SECStatus AddCertificateToHeader(PK11SlotInfo *slot,
secuPWData *pwdata,
const char *headerFileName,
CERTCertDBHandle *certHandle,
const char *nickNameStr,
PRBool sigVerify)

{
SECStatus rv = SECSuccess;
PRFileDesc *headerFile = NULL;
CERTCertificate *cert = NULL;
HeaderType hType = CERTENC;

/* If the intermediate header file already exists, delete it */
if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) {
PR_Delete(headerFileName);
}
headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660);
if (!headerFile) {
PR_fprintf(PR_STDERR,
"unable to open \"%s\" for writing (%ld, %ld).\n",
headerFileName, PR_GetError(), PR_GetOSError());
rv = SECFailure;
goto cleanup;
}
cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr);
if (!cert) {
PR_fprintf(PR_STDERR, "could not obtain certificate from file\n");
rv = SECFailure;
goto cleanup;
}
if (sigVerify) {
hType = CERTVFY;
}
WriteToHeaderFile(cert->derCert.data, cert->derCert.len, hType, headerFile);
cleanup:
if (headerFile) {
PR_Close(headerFile);
}
if (cert) {
CERT_DestroyCertificate(cert);
}
return rv;
}

/*
* Finds the public key from the certificate saved in the header file
* and encrypts with it the contents of inFileName to encryptedFileName.
*/
SECStatus FindKeyAndEncrypt(PK11SlotInfo *slot,
secuPWData *pwdata,
const char *headerFileName,
const char *encryptedFileName,
const char *inFileName)
{
SECStatus rv;
PRFileDesc *headerFile = NULL;
PRFileDesc *encFile = NULL;
PRFileDesc *inFile = NULL;
CERTCertificate *cert = NULL;
SECItem data;
unsigned char ptext[MODBLOCKSIZE];
unsigned char encBuf[MODBLOCKSIZE];
unsigned int ptextLen;
int index;
unsigned int nWritten;
unsigned int pad[1];
SECItem padItem;
unsigned int paddingLength = 0;
SECKEYPublicKey *pubkey = NULL;

/* If the intermediate encrypted file already exists, delete it*/
if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) {
PR_Delete(encryptedFileName);
}

/* Read certificate from header file */
rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Could not read certificate from header file\n");
goto cleanup;
}
/* Read in an ASCII cert and return a CERTCertificate */
cert = CERT_DecodeCertFromPackage((char *)data.data, data.len);
if (!cert) {
PR_fprintf(PR_STDERR, "could not obtain certificate from file\n");
rv = SECFailure;
goto cleanup;
}
/* Extract the public key from certificate */
pubkey = CERT_ExtractPublicKey(cert);
if (!pubkey) {
PR_fprintf(PR_STDERR, "could not get key from certificate\n");
rv = SECFailure;
goto cleanup;
}

/* Open the encrypted file for writing */
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);
rv = SECFailure;
goto cleanup;
}

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

/* Open the header file to write padding */
headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660);
if (!headerFile) {
PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n",
headerFileName);
rv = SECFailure;
goto cleanup;
}

/* Read input file */
while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) {
if (ptextLen != MODBLOCKSIZE) {
paddingLength = MODBLOCKSIZE - ptextLen;
for ( index=0; index < paddingLength; index++) {
ptext[ptextLen+index] = (unsigned char)paddingLength;
}
ptextLen = MODBLOCKSIZE;
}
rv = PK11_PubEncryptRaw(pubkey, encBuf, ptext, ptextLen, NULL);
nWritten = PR_Write(encFile, encBuf, ptextLen);
}

/* Write the padding to header file */
pad[0] = paddingLength;
padItem.type = siBuffer;
padItem.data = (unsigned char *)pad;
padItem.len = sizeof(pad[0]);
WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile);

cleanup:
if (headerFile) {
PR_Close(headerFile);
}
if (encFile) {
PR_Close(encFile);
}
if (inFile) {
PR_Close(inFile);
}
if (pubkey) {
SECKEY_DestroyPublicKey(pubkey);
}
if (cert) {
CERT_DestroyCertificate(cert);
}
return rv;
}

/*
* Finds the private key from db and signs the contents
* of inFileName and writes to signatureFileName
*/
SECStatus FindKeyAndSign(PK11SlotInfo *slot,
CERTCertDBHandle* certHandle,
secuPWData *pwdata,
const char *nickNameStr,
const char *headerFileName,
const char *inFileName)
{
SECStatus rv;
PRFileDesc *headerFile = NULL;
PRFileDesc *inFile = NULL;
CERTCertificate *cert = NULL;
unsigned int signatureLen = 0;
SECKEYPrivateKey *privkey = NULL;
SECItem sigItem;
SECOidTag hashOIDTag;

/* Open the header file to write padding */
headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660);
if (!headerFile) {
PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n",
headerFileName);
rv = SECFailure;
goto cleanup;
}

/* Get the certificate by nick name and write to header file */
cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr);
if (!cert) {
PR_fprintf(PR_STDERR, "could not obtain certificate by name - %s\n", nickNameStr);
rv = SECFailure;
goto cleanup;
}
WriteToHeaderFile(cert->derCert.data, cert->derCert.len, CERTVFY, headerFile);


/* Find private key from certificate */
privkey = PK11_FindKeyByAnyCert(cert, NULL);
if (privkey == NULL) {
fprintf(stderr, "Couldn't find private key for cert\n");
rv = SECFailure;
goto cleanup;
}

/* Sign the contents of the input file */
rv = SignData(inFileName, privkey, &sigItem);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "could not sign the contents from file - %s \n", inFileName);
goto cleanup;
}

/* write signature to header file */
WriteToHeaderFile(sigItem.data, sigItem.len, SIG, headerFile);

cleanup:
if (headerFile) {
PR_Close(headerFile);
}
if (privkey) {
SECKEY_DestroyPrivateKey(privkey);
}
if (cert) {
CERT_DestroyCertificate(cert);
}
return rv;
}

/*
* Finds the public key from certificate and verifies signature
*/
SECStatus FindKeyAndVerify(PK11SlotInfo *slot,
CERTCertDBHandle* certHandle,
secuPWData *pwdata,
const char *headerFileName,
const char *inFileName)
{
SECStatus rv = SECFailure;
PRFileDesc *headerFile = NULL;
PRFileDesc *inFile = NULL;
CERTCertificate *cert = NULL;
SECKEYPublicKey *pubkey = NULL;
SECItem sigItem;
SECItem certData;

/* 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);
rv = SECFailure;
goto cleanup;
}

/* Open the header file to read the certificate and signature */
headerFile = PR_Open(headerFileName, PR_RDONLY, 0);
if (!headerFile) {
PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n",
headerFileName);
rv = SECFailure;
goto cleanup;
}

/* Read certificate from header file */
rv = ReadFromHeaderFile(headerFileName, CERTVFY, &certData, PR_TRUE);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Could not read certificate from header file\n");
goto cleanup;
}

/* Read in an ASCII cert and return a CERTCertificate */
cert = CERT_DecodeCertFromPackage((char *)certData.data, certData.len);
if (!cert) {
PR_fprintf(PR_STDERR, "could not obtain certificate from file\n");
rv = SECFailure;
goto cleanup;
}

/* Extract the public key from certificate */
pubkey = CERT_ExtractPublicKey(cert);
if (!pubkey) {
PR_fprintf(PR_STDERR, "Could not get key from certificate\n");
rv = SECFailure;
goto cleanup;
}

/* Read signature from header file */
rv = ReadFromHeaderFile(headerFileName, SIG, &sigItem, PR_TRUE);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Could not read signature from header file\n");
goto cleanup;
}

/* Verify with the public key */
rv = VerifyData(inFileName, pubkey, &sigItem, pwdata);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Couldn't verify the signature for file - %s\n", inFileName);
goto cleanup;
}

cleanup:
if (headerFile) {
PR_Close(headerFile);
}
if (pubkey) {
SECKEY_DestroyPublicKey(pubkey);
}
if (cert) {
CERT_DestroyCertificate(cert);
}
return rv;
}

/*
* Finds the private key corresponding to the certificate saved in the header file
* and decrypts with it the contents of encryptedFileName to outFileName.
*/
SECStatus FindKeyAndDecrypt(PK11SlotInfo *slot,
secuPWData *pwdata,
const char *headerFileName,
const char *encryptedFileName,
const char *outFileName)
{
SECStatus rv;
PRFileDesc *encFile = NULL;
PRFileDesc *outFile = NULL;
SECKEYPrivateKey *pvtkey = NULL;
unsigned int inFileLength = 0;
unsigned int paddingLength = 0;
unsigned int count = 0;
unsigned int temp = 0;
unsigned char ctext[MODBLOCKSIZE];
unsigned char decBuf[MODBLOCKSIZE];
unsigned int ctextLen;
unsigned int decBufLen;
SECItem padItem;
SECItem data;
SECItem signature;
CERTCertificate *cert = NULL;

/* Read certificate from header file */
rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Could not read certificate from header file\n");
goto cleanup;
}

/* Read padding from 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;
}
paddingLength = (unsigned int)padItem.data[0];
inFileLength = FileSize(encryptedFileName);

/* Read in an ASCII cert and return a CERTCertificate */
cert = CERT_DecodeCertFromPackage((char *)data.data, data.len);
if (!cert) {
PR_fprintf(PR_STDERR, "could not obtain certificate from file\n");
rv = SECFailure;
goto cleanup;
}

/* Find private key from certificate */
pvtkey = PK11_FindKeyByAnyCert(cert, NULL);
if (pvtkey == NULL) {
fprintf(stderr, "Couldn't find private key for cert\n");
rv = SECFailure;
goto cleanup;
}

/* Open the out file to write */
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);
rv = SECFailure;
goto cleanup;
}
/* Open the encrypted file for reading */
encFile = PR_Open(encryptedFileName, PR_RDONLY, 0);
if (!encFile) {
PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n",
encryptedFileName);
rv = SECFailure;
goto cleanup;
}
/* Read the encrypt file, decrypt and write to out file */
while ((ctextLen = PR_Read(encFile, ctext, sizeof(ctext))) > 0) {
count += ctextLen;
rv = PK11_PubDecryptRaw(pvtkey, decBuf, &decBufLen, sizeof(decBuf), ctext, ctextLen);
if (rv != SECSuccess) {
fprintf(stderr, "Couldn't decrypt\n");
goto cleanup;
}
if (decBufLen == 0) {
break;
}
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;
}
}
cleanup:
if (encFile) {
PR_Close(encFile);
}
if (outFile) {
PR_Close(outFile);
}
if (pvtkey) {
SECKEY_DestroyPrivateKey(pvtkey);
}
if (cert) {
CERT_DestroyCertificate(cert);
}
return rv;
}

/* Map option letter to command */
static CommandType option2Command(char c)
{
switch (c) {
case 'G': return GENERATE_CSR;
case 'A': return ADD_CERT_TO_DB;
case 'H': return SAVE_CERT_TO_HEADER;
case 'E': return ENCRYPT;
case 'D': return DECRYPT;
case 'S': return SIGN;
case 'V': return VERIFY;
default: return UNKNOWN;
}
}

/*
* This example illustrates basic encryption/decryption and MACing
* Generates the RSA key pair as token object and outputs public key as cert request.
* Reads cert request file and stores certificate in DB.
* Input, store and trust CA certificate.
* Write certificate to intermediate header file
* Extract public key from certificate, encrypts the input file and write to external file.
* Finds the matching private key, decrypts and write to external file
*
* How this sample is different from sample 5 ?
*
* 1. As in sample 5, output is a PKCS#10 CSR
* 2. Input and store a cert in cert DB and also used to input, store and trust CA cert.
* 3. Like sample 5, but puts cert in header
* 4. Like sample 5, but finds key matching cert in header
*/
int
main(int argc, char **argv)
{
SECStatus rv;
PLOptState *optstate;
PLOptStatus status;
PRBool initialized = PR_FALSE;

CommandType cmd = UNKNOWN;
const char *dbdir = NULL;
secuPWData pwdata = { PW_NONE, 0 };

char *subjectStr = NULL;
CERTName *subject = 0;

unsigned int serialNumber = 0;
char *serialNumberStr = NULL;
char *trustStr = NULL;
CERTCertDBHandle *certHandle;
const char *nickNameStr = NULL;
char *issuerNameStr = NULL;
PRBool selfsign = PR_FALSE;
PRBool ascii = PR_FALSE;
PRBool sigVerify = PR_FALSE;

const char *headerFileName = NULL;
const char *encryptedFileName = NULL;
const char *inFileName = NULL;
const char *outFileName = NULL;
char *certReqFileName = NULL;
char *certFileName = NULL;
const char *noiseFileName = NULL;
PK11SlotInfo *slot = NULL;

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

/* Parse command line arguments */
optstate = PL_CreateOptState(argc, argv, "GAHEDSVad:i:o:f:p:z:s:r:n:x:m:t:c:u:e:b:v:");
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case 'a':
ascii = PR_TRUE;
break;
case 'G': /* Generate a CSR */
case 'A': /* Add cert to database */
case 'H': /* Save cert to the header file */
case 'E': /* Encrypt with public key from cert in header file */
case 'S': /* Sign with private key */
case 'D': /* Decrypt with the matching private key */
case 'V': /* Verify with the matching public key */
cmd = option2Command(optstate->option);
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 'b':
headerFileName = strdup(optstate->value);
break;
case 'e':
encryptedFileName = strdup(optstate->value);
break;
case 'o':
outFileName = strdup(optstate->value);
break;
case 'z':
noiseFileName = strdup(optstate->value);
break;
case 's':
subjectStr = strdup(optstate->value);
subject = CERT_AsciiToName(subjectStr);
break;
case 'r':
certReqFileName = strdup(optstate->value);
break;
case 'c':
certFileName = strdup(optstate->value);
break;
case 'u':
issuerNameStr = strdup(optstate->value);
break;
case 'n':
nickNameStr = strdup(optstate->value);
break;
case 'x':
selfsign = PR_TRUE;
break;
case 'm':
serialNumberStr = strdup(optstate->value);
serialNumber = atoi(serialNumberStr);
break;
case 't':
trustStr = strdup(optstate->value);
break;
case 'v':
sigVerify = PR_TRUE;
break;
default:
Usage(progName);
break;
}
}
PL_DestroyOptState(optstate);

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

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

PK11_SetPasswordFunc(GetModulePassword);
slot = PK11_GetInternalKeySlot();
if (PK11_NeedLogin(slot)) {
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 GENERATE_CSR:
ValidateGenerateCSRCommand(progName, dbdir, subject, subjectStr,
certReqFileName);
/* Generate a CSR */
rv = CreateCertRequest(slot, &pwdata, subject,
certReqFileName, ascii);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Create Certificate Request: Failed\n");
goto cleanup;
}
break;
case ADD_CERT_TO_DB:
ValidateAddCertToDBCommand(progName, dbdir, nickNameStr, trustStr,
certFileName, certReqFileName,
issuerNameStr, serialNumberStr, selfsign);
/* Add cert to database */
rv = AddCertificateToDB(slot, &pwdata, certReqFileName, certFileName,
issuerNameStr, certHandle, nickNameStr,
trustStr, serialNumber, selfsign, ascii);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Add Certificate to DB: Failed\n");
goto cleanup;
}
break;
case SAVE_CERT_TO_HEADER:
ValidateSaveCertToHeaderCommand(progName, dbdir, nickNameStr, headerFileName);
/* Save cert to the header file */
rv = AddCertificateToHeader(slot, &pwdata, headerFileName, certHandle, nickNameStr, sigVerify);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Saving Certificate to header: Failed\n");
goto cleanup;
}
break;
case ENCRYPT:
ValidateEncryptCommand(progName, dbdir, nickNameStr, headerFileName, inFileName, encryptedFileName);
/* Encrypt with public key from cert in header file */
rv = FindKeyAndEncrypt(slot, &pwdata, headerFileName, encryptedFileName, inFileName);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Find public key and Encrypt : Failed\n");
goto cleanup;
}
break;
case SIGN:
ValidateSignCommand(progName, dbdir, nickNameStr, headerFileName, inFileName);
/* Sign with private key */
rv = FindKeyAndSign(slot, certHandle, &pwdata, nickNameStr, headerFileName, inFileName);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Find private key and sign : Failed\n");
goto cleanup;
}
break;
case DECRYPT:
ValidateDecryptCommand(progName, dbdir, headerFileName, encryptedFileName, outFileName);
/* Decrypt with the matching private key */
rv = FindKeyAndDecrypt(slot, &pwdata, headerFileName, encryptedFileName, outFileName);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Find private key and Decrypt : Failed\n");
}
break;
case VERIFY:
ValidateVerifyCommand(progName, dbdir, headerFileName, inFileName);
/* Verify with the matching public key */
rv = FindKeyAndVerify(slot, certHandle, &pwdata, headerFileName, inFileName);
if (rv != SECSuccess) {
PR_fprintf(PR_STDERR, "Find public key and verify signature : Failed\n");
goto cleanup;
}
}
cleanup:
if (slot) {
PK11_FreeSlot(slot);
}
if (initialized) {
SECStatus rvShutdown = NSS_Shutdown();
if (rvShutdown != SECSuccess) {
PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown() - %s",
PORT_ErrorToString(rvShutdown));
rv = SECFailure;
}
PR_Cleanup();
}
return rv;
}
</pre>