blob: 5884713e3c6179a64d78b235b381e4208686a55c [file] [log] [blame]
/* 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/. */
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include "nspr.h"
#include "secutil.h"
#include "pk11func.h"
#include "pkcs12.h"
#include "p12plcy.h"
#include "pk12util.h"
#include "nss.h"
#include "secport.h"
#include "secpkcs5.h"
#include "certdb.h"
#define PKCS12_IN_BUFFER_SIZE 200
static char *progName;
PRBool pk12_debugging = PR_FALSE;
PRBool dumpRawFile;
static PRBool pk12uForceUnicode;
PRIntn pk12uErrno = 0;
static void
Usage()
{
#define FPS PR_fprintf(PR_STDERR,
FPS "Usage: %s -i importfile [-d certdir] [-P dbprefix] [-h tokenname]\n",
progName);
FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
FPS "\t\t [-v]\n");
FPS "Usage: %s -l listfile [-d certdir] [-P dbprefix] [-h tokenname]\n",
progName);
FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
FPS "\t\t [-v]\n");
FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix]\n",
progName);
FPS "\t\t [-c key_cipher] [-C cert_cipher]\n"
"\t\t [-m | --key_len keyLen] [--cert_key_len certKeyLen] [-v]\n");
FPS "\t\t [-k slotpwfile | -K slotpw]\n"
"\t\t [-w p12filepwfile | -W p12filepw]\n");
exit(PK12UERR_USAGE);
}
static PRBool
p12u_OpenFile(p12uContext *p12cxt, PRBool fileRead)
{
if (!p12cxt || !p12cxt->filename) {
return PR_FALSE;
}
if (fileRead) {
p12cxt->file = PR_Open(p12cxt->filename,
PR_RDONLY, 0400);
} else {
p12cxt->file = PR_Open(p12cxt->filename,
PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
0600);
}
if (!p12cxt->file) {
p12cxt->error = PR_TRUE;
return PR_FALSE;
}
return PR_TRUE;
}
static void
p12u_DestroyContext(p12uContext **ppCtx, PRBool removeFile)
{
if (!ppCtx || !(*ppCtx)) {
return;
}
if ((*ppCtx)->file != NULL) {
PR_Close((*ppCtx)->file);
}
if ((*ppCtx)->filename != NULL) {
if (removeFile) {
PR_Delete((*ppCtx)->filename);
}
PL_strfree((*ppCtx)->filename);
(*ppCtx)->filename = NULL;
}
PR_Free(*ppCtx);
*ppCtx = NULL;
}
static p12uContext *
p12u_InitContext(PRBool fileImport, char *filename)
{
p12uContext *p12cxt;
p12cxt = PORT_ZNew(p12uContext);
if (!p12cxt) {
return NULL;
}
p12cxt->error = PR_FALSE;
p12cxt->errorValue = 0;
p12cxt->filename = PL_strdup(filename);
if (!p12u_OpenFile(p12cxt, fileImport)) {
p12u_DestroyContext(&p12cxt, PR_FALSE);
return NULL;
}
return p12cxt;
}
SECItem *
P12U_NicknameCollisionCallback(SECItem *old_nick, PRBool *cancel, void *wincx)
{
char *nick = NULL;
SECItem *ret_nick = NULL;
CERTCertificate *cert = (CERTCertificate *)wincx;
if (!cancel || !cert) {
pk12uErrno = PK12UERR_USER_CANCELLED;
return NULL;
}
if (!old_nick)
fprintf(stdout, "pk12util: no nickname for cert in PKCS12 file.\n");
#if 0
/* XXX not handled yet */
*cancel = PR_TRUE;
return NULL;
#else
nick = CERT_MakeCANickname(cert);
if (!nick) {
return NULL;
}
if (old_nick && old_nick->data && old_nick->len &&
PORT_Strlen(nick) == old_nick->len &&
!PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) {
PORT_Free(nick);
PORT_SetError(SEC_ERROR_IO);
return NULL;
}
fprintf(stdout, "pk12util: using nickname: %s\n", nick);
ret_nick = PORT_ZNew(SECItem);
if (ret_nick == NULL) {
PORT_Free(nick);
return NULL;
}
ret_nick->data = (unsigned char *)nick;
ret_nick->len = PORT_Strlen(nick);
return ret_nick;
#endif
}
static SECStatus
p12u_SwapUnicodeBytes(SECItem *uniItem)
{
unsigned int i;
unsigned char a;
if ((uniItem == NULL) || (uniItem->len % 2)) {
return SECFailure;
}
for (i = 0; i < uniItem->len; i += 2) {
a = uniItem->data[i];
uniItem->data[i] = uniItem->data[i + 1];
uniItem->data[i + 1] = a;
}
return SECSuccess;
}
static PRBool
p12u_ucs2_ascii_conversion_function(PRBool toUnicode,
unsigned char *inBuf,
unsigned int inBufLen,
unsigned char *outBuf,
unsigned int maxOutBufLen,
unsigned int *outBufLen,
PRBool swapBytes)
{
SECItem it = { 0 };
SECItem *dup = NULL;
PRBool ret;
#ifdef DEBUG_CONVERSION
if (pk12_debugging) {
int i;
printf("Converted from:\n");
for (i = 0; i < inBufLen; i++) {
printf("%2x ", inBuf[i]);
/*if (i%60 == 0) printf("\n");*/
}
printf("\n");
}
#endif
it.data = inBuf;
it.len = inBufLen;
dup = SECITEM_DupItem(&it);
if (!dup) {
return PR_FALSE;
}
/* If converting Unicode to ASCII, swap bytes before conversion
* as neccessary.
*/
if (!toUnicode && swapBytes) {
if (p12u_SwapUnicodeBytes(dup) != SECSuccess) {
SECITEM_ZfreeItem(dup, PR_TRUE);
return PR_FALSE;
}
}
/* Perform the conversion. */
ret = PORT_UCS2_UTF8Conversion(toUnicode, dup->data, dup->len,
outBuf, maxOutBufLen, outBufLen);
SECITEM_ZfreeItem(dup, PR_TRUE);
#ifdef DEBUG_CONVERSION
if (pk12_debugging) {
int i;
printf("Converted to:\n");
for (i = 0; i < *outBufLen; i++) {
printf("%2x ", outBuf[i]);
/*if (i%60 == 0) printf("\n");*/
}
printf("\n");
}
#endif
return ret;
}
SECStatus
P12U_UnicodeConversion(PLArenaPool *arena, SECItem *dest, SECItem *src,
PRBool toUnicode, PRBool swapBytes)
{
unsigned int allocLen;
if (!dest || !src) {
return SECFailure;
}
allocLen = ((toUnicode) ? (src->len << 2) : src->len);
if (arena) {
dest->data = PORT_ArenaZAlloc(arena, allocLen);
} else {
dest->data = PORT_ZAlloc(allocLen);
}
if (PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len,
dest->data, allocLen, &dest->len,
swapBytes) == PR_FALSE) {
if (!arena) {
PORT_Free(dest->data);
}
dest->data = NULL;
return SECFailure;
}
return SECSuccess;
}
/*
*
*/
SECItem *
P12U_GetP12FilePassword(PRBool confirmPw, secuPWData *p12FilePw)
{
char *p0 = NULL;
SECItem *pwItem = NULL;
if (p12FilePw == NULL || p12FilePw->source == PW_NONE) {
char *p1 = NULL;
int rc;
for (;;) {
p0 = SECU_GetPasswordString(NULL,
"Enter password for PKCS12 file: ");
if (!confirmPw || p0 == NULL)
break;
p1 = SECU_GetPasswordString(NULL, "Re-enter password: ");
if (p1 == NULL) {
PORT_ZFree(p0, PL_strlen(p0));
p0 = NULL;
break;
}
rc = PL_strcmp(p0, p1);
PORT_ZFree(p1, PL_strlen(p1));
if (rc == 0)
break;
PORT_ZFree(p0, PL_strlen(p0));
}
} else if (p12FilePw->source == PW_FROMFILE) {
p0 = SECU_FilePasswd(NULL, PR_FALSE, p12FilePw->data);
} else { /* Plaintext */
p0 = PORT_Strdup(p12FilePw->data);
}
if (p0 == NULL) {
return NULL;
}
pwItem = SECITEM_AllocItem(NULL, NULL, PL_strlen(p0) + 1);
memcpy(pwItem->data, p0, pwItem->len);
PORT_ZFree(p0, PL_strlen(p0));
return pwItem;
}
SECStatus
P12U_InitSlot(PK11SlotInfo *slot, secuPWData *slotPw)
{
SECStatus rv;
/* New databases, initialize keydb password. */
if (PK11_NeedUserInit(slot)) {
rv = SECU_ChangePW(slot,
(slotPw->source == PW_PLAINTEXT) ? slotPw->data : 0,
(slotPw->source == PW_FROMFILE) ? slotPw->data : 0);
if (rv != SECSuccess) {
SECU_PrintError(progName, "Failed to initialize slot \"%s\"",
PK11_GetSlotName(slot));
return SECFailure;
}
}
if (PK11_Authenticate(slot, PR_TRUE, slotPw) != SECSuccess) {
SECU_PrintError(progName,
"Failed to authenticate to PKCS11 slot");
PORT_SetError(SEC_ERROR_USER_CANCELLED);
pk12uErrno = PK12UERR_USER_CANCELLED;
return SECFailure;
}
return SECSuccess;
}
/* This routine takes care of getting the PKCS12 file password, then reading and
* verifying the file. It returns the decoder context and a filled in password.
* (The password is needed by P12U_ImportPKCS12Object() to import the private
* key.)
*/
SEC_PKCS12DecoderContext *
p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
secuPWData *slotPw, secuPWData *p12FilePw)
{
SEC_PKCS12DecoderContext *p12dcx = NULL;
p12uContext *p12cxt = NULL;
SECItem *pwitem = NULL;
SECItem p12file = { 0 };
SECStatus rv = SECFailure;
PRBool swapUnicode = PR_FALSE;
PRBool forceUnicode = pk12uForceUnicode;
PRBool trypw;
int error;
#ifdef IS_LITTLE_ENDIAN
swapUnicode = PR_TRUE;
#endif
p12cxt = p12u_InitContext(PR_TRUE, in_file);
if (!p12cxt) {
SECU_PrintError(progName, "File Open failed: %s", in_file);
pk12uErrno = PK12UERR_INIT_FILE;
return NULL;
}
/* get the password */
pwitem = P12U_GetP12FilePassword(PR_FALSE, p12FilePw);
if (!pwitem) {
pk12uErrno = PK12UERR_USER_CANCELLED;
goto done;
}
if (P12U_UnicodeConversion(NULL, uniPwp, pwitem, PR_TRUE,
swapUnicode) != SECSuccess) {
SECU_PrintError(progName, "Unicode conversion failed");
pk12uErrno = PK12UERR_UNICODECONV;
goto done;
}
rv = SECU_FileToItem(&p12file, p12cxt->file);
if (rv != SECSuccess) {
SECU_PrintError(progName, "Failed to read from import file");
goto done;
}
do {
trypw = PR_FALSE; /* normally we do this once */
rv = SECFailure;
/* init the decoder context */
p12dcx = SEC_PKCS12DecoderStart(uniPwp, slot, slotPw,
NULL, NULL, NULL, NULL, NULL);
if (!p12dcx) {
SECU_PrintError(progName, "PKCS12 decoder start failed");
pk12uErrno = PK12UERR_PK12DECODESTART;
break;
}
/* decode the item */
rv = SEC_PKCS12DecoderUpdate(p12dcx, p12file.data, p12file.len);
if (rv != SECSuccess) {
error = PR_GetError();
if (error == SEC_ERROR_DECRYPTION_DISALLOWED) {
PR_SetError(error, 0);
break;
}
SECU_PrintError(progName, "PKCS12 decoding failed");
pk12uErrno = PK12UERR_DECODE;
}
/* does the blob authenticate properly? */
rv = SEC_PKCS12DecoderVerify(p12dcx);
if (rv != SECSuccess) {
if (uniPwp->len == 2) {
/* this is a null PW, try once more with a zero-length PW
instead of a null string */
SEC_PKCS12DecoderFinish(p12dcx);
uniPwp->len = 0;
trypw = PR_TRUE;
} else if (forceUnicode == pk12uForceUnicode) {
/* try again with a different password encoding */
forceUnicode = !pk12uForceUnicode;
rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE,
forceUnicode);
if (rv != SECSuccess) {
SECU_PrintError(progName, "PKCS12 decoding failed to set option");
pk12uErrno = PK12UERR_DECODEVERIFY;
break;
}
SEC_PKCS12DecoderFinish(p12dcx);
trypw = PR_TRUE;
} else {
SECU_PrintError(progName, "PKCS12 decode not verified");
pk12uErrno = PK12UERR_DECODEVERIFY;
break;
}
}
} while (trypw == PR_TRUE);
/* revert the option setting */
if (forceUnicode != pk12uForceUnicode) {
rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode);
if (rv != SECSuccess) {
SECU_PrintError(progName, "PKCS12 decoding failed to set option");
pk12uErrno = PK12UERR_DECODEVERIFY;
}
}
/* rv has been set at this point */
done:
if (rv != SECSuccess) {
if (p12dcx != NULL) {
SEC_PKCS12DecoderFinish(p12dcx);
p12dcx = NULL;
}
if (uniPwp->data) {
SECITEM_ZfreeItem(uniPwp, PR_FALSE);
uniPwp->data = NULL;
}
}
PR_Close(p12cxt->file);
p12cxt->file = NULL;
/* PK11_FreeSlot(slot); */
p12u_DestroyContext(&p12cxt, PR_FALSE);
if (pwitem) {
SECITEM_ZfreeItem(pwitem, PR_TRUE);
}
SECITEM_ZfreeItem(&p12file, PR_FALSE);
return p12dcx;
}
/*
* given a filename for pkcs12 file, imports certs and keys
*
* Change: altitude
* I've changed this function so that it takes the keydb and pkcs12 file
* passwords from files. The "pwdKeyDB" and "pwdP12File"
* variables have been added for this purpose.
*/
PRIntn
P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot,
secuPWData *slotPw, secuPWData *p12FilePw)
{
SEC_PKCS12DecoderContext *p12dcx = NULL;
SECItem uniPwitem = { 0 };
PRBool forceUnicode = pk12uForceUnicode;
PRBool trypw;
SECStatus rv = SECFailure;
rv = P12U_InitSlot(slot, slotPw);
if (rv != SECSuccess) {
SECU_PrintError(progName, "Failed to authenticate to \"%s\"",
PK11_GetSlotName(slot));
pk12uErrno = PK12UERR_PK11GETSLOT;
return rv;
}
do {
trypw = PR_FALSE; /* normally we do this once */
rv = SECFailure;
p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
if (p12dcx == NULL) {
goto loser;
}
/* make sure the bags are okey dokey -- nicknames correct, etc. */
rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback);
if (rv != SECSuccess) {
if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
pk12uErrno = PK12UERR_CERTALREADYEXISTS;
} else {
pk12uErrno = PK12UERR_DECODEVALIBAGS;
}
SECU_PrintError(progName, "PKCS12 decode validate bags failed");
goto loser;
}
/* stuff 'em in */
if (forceUnicode != pk12uForceUnicode) {
rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE,
forceUnicode);
if (rv != SECSuccess) {
SECU_PrintError(progName, "PKCS12 decode set option failed");
pk12uErrno = PK12UERR_DECODEIMPTBAGS;
goto loser;
}
}
rv = SEC_PKCS12DecoderImportBags(p12dcx);
if (rv != SECSuccess) {
if (PR_GetError() == SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY &&
forceUnicode == pk12uForceUnicode) {
/* try again with a different password encoding */
forceUnicode = !pk12uForceUnicode;
SEC_PKCS12DecoderFinish(p12dcx);
SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
trypw = PR_TRUE;
} else {
SECU_PrintError(progName, "PKCS12 decode import bags failed");
pk12uErrno = PK12UERR_DECODEIMPTBAGS;
goto loser;
}
}
} while (trypw);
/* revert the option setting */
if (forceUnicode != pk12uForceUnicode) {
rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode);
if (rv != SECSuccess) {
SECU_PrintError(progName, "PKCS12 decode set option failed");
pk12uErrno = PK12UERR_DECODEIMPTBAGS;
goto loser;
}
}
fprintf(stdout, "%s: PKCS12 IMPORT SUCCESSFUL\n", progName);
rv = SECSuccess;
loser:
if (p12dcx) {
SEC_PKCS12DecoderFinish(p12dcx);
}
if (uniPwitem.data) {
SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
}
return rv;
}
static void
p12u_DoPKCS12ExportErrors()
{
PRErrorCode error_value;
error_value = PORT_GetError();
if ((error_value == SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY) ||
(error_value == SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME) ||
(error_value == SEC_ERROR_PKCS12_UNABLE_TO_WRITE)) {
fputs(SECU_Strerror(error_value), stderr);
} else if (error_value == SEC_ERROR_USER_CANCELLED) {
;
} else {
fputs(SECU_Strerror(SEC_ERROR_EXPORTING_CERTIFICATES), stderr);
}
}
static void
p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len)
{
p12uContext *p12cxt = arg;
int writeLen;
if (!p12cxt || (p12cxt->error == PR_TRUE)) {
return;
}
if (p12cxt->file == NULL) {
p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
p12cxt->error = PR_TRUE;
return;
}
writeLen = PR_Write(p12cxt->file, (unsigned char *)buf, (PRInt32)len);
if (writeLen != (int)len) {
PR_Close(p12cxt->file);
PL_strfree(p12cxt->filename);
p12cxt->filename = NULL;
p12cxt->file = NULL;
p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
p12cxt->error = PR_TRUE;
}
}
void
P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
SECOidTag cipher, SECOidTag certCipher,
secuPWData *slotPw, secuPWData *p12FilePw)
{
SEC_PKCS12ExportContext *p12ecx = NULL;
SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL;
SECItem *pwitem = NULL;
p12uContext *p12cxt = NULL;
CERTCertList *certlist = NULL;
CERTCertListNode *node = NULL;
PK11SlotInfo *slot = NULL;
if (P12U_InitSlot(inSlot, slotPw) != SECSuccess) {
SECU_PrintError(progName, "Failed to authenticate to \"%s\"",
PK11_GetSlotName(inSlot));
pk12uErrno = PK12UERR_PK11GETSLOT;
goto loser;
}
certlist = PK11_FindCertsFromNickname(nn, slotPw);
if (!certlist) {
SECU_PrintError(progName, "find user certs from nickname failed");
pk12uErrno = PK12UERR_FINDCERTBYNN;
return;
}
if ((SECSuccess != CERT_FilterCertListForUserCerts(certlist)) ||
CERT_LIST_EMPTY(certlist)) {
PR_fprintf(PR_STDERR, "%s: no user certs from given nickname\n",
progName);
pk12uErrno = PK12UERR_FINDCERTBYNN;
goto loser;
}
/* Password to use for PKCS12 file. */
pwitem = P12U_GetP12FilePassword(PR_TRUE, p12FilePw);
if (!pwitem) {
goto loser;
}
p12cxt = p12u_InitContext(PR_FALSE, outfile);
if (!p12cxt) {
SECU_PrintError(progName, "Initialization failed: %s", outfile);
pk12uErrno = PK12UERR_INIT_FILE;
goto loser;
}
if (certlist) {
CERTCertificate *cert = CERT_LIST_HEAD(certlist)->cert;
if (cert) {
slot = cert->slot; /* use the slot from the first matching
certificate to create the context . This is for keygen */
}
}
if (!slot) {
SECU_PrintError(progName, "cert does not have a slot");
pk12uErrno = PK12UERR_FINDCERTBYNN;
goto loser;
}
p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL, slot, slotPw);
if (!p12ecx) {
SECU_PrintError(progName, "export context creation failed");
pk12uErrno = PK12UERR_EXPORTCXCREATE;
goto loser;
}
if (SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, SEC_OID_SHA1) !=
SECSuccess) {
SECU_PrintError(progName, "PKCS12 add password integrity failed");
pk12uErrno = PK12UERR_PK12ADDPWDINTEG;
goto loser;
}
for (node = CERT_LIST_HEAD(certlist);
!CERT_LIST_END(node, certlist);
node = CERT_LIST_NEXT(node)) {
CERTCertificate *cert = node->cert;
if (!cert->slot) {
SECU_PrintError(progName, "cert does not have a slot");
pk12uErrno = PK12UERR_FINDCERTBYNN;
goto loser;
}
keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
if (certCipher == SEC_OID_UNKNOWN) {
certSafe = keySafe;
} else {
certSafe =
SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certCipher);
}
if (!certSafe || !keySafe) {
SECU_PrintError(progName, "key or cert safe creation failed");
pk12uErrno = PK12UERR_CERTKEYSAFE;
goto loser;
}
if (SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert,
CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem, cipher) !=
SECSuccess) {
SECU_PrintError(progName, "add cert and key failed");
pk12uErrno = PK12UERR_ADDCERTKEY;
goto loser;
}
}
CERT_DestroyCertList(certlist);
certlist = NULL;
if (SEC_PKCS12Encode(p12ecx, p12u_WriteToExportFile, p12cxt) !=
SECSuccess) {
SECU_PrintError(progName, "PKCS12 encode failed");
pk12uErrno = PK12UERR_ENCODE;
goto loser;
}
p12u_DestroyContext(&p12cxt, PR_FALSE);
SECITEM_ZfreeItem(pwitem, PR_TRUE);
fprintf(stdout, "%s: PKCS12 EXPORT SUCCESSFUL\n", progName);
SEC_PKCS12DestroyExportContext(p12ecx);
return;
loser:
SEC_PKCS12DestroyExportContext(p12ecx);
if (certlist) {
CERT_DestroyCertList(certlist);
certlist = NULL;
}
p12u_DestroyContext(&p12cxt, PR_TRUE);
if (pwitem) {
SECITEM_ZfreeItem(pwitem, PR_TRUE);
}
p12u_DoPKCS12ExportErrors();
return;
}
PRIntn
P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot,
secuPWData *slotPw, secuPWData *p12FilePw)
{
SEC_PKCS12DecoderContext *p12dcx = NULL;
SECItem uniPwitem = { 0 };
SECStatus rv = SECFailure;
const SEC_PKCS12DecoderItem *dip;
p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
/* did the blob authenticate properly? */
if (p12dcx == NULL) {
SECU_PrintError(progName, "PKCS12 decode not verified");
pk12uErrno = PK12UERR_DECODEVERIFY;
goto loser;
}
rv = SEC_PKCS12DecoderIterateInit(p12dcx);
if (rv != SECSuccess) {
SECU_PrintError(progName, "PKCS12 decode iterate bags failed");
pk12uErrno = PK12UERR_DECODEIMPTBAGS;
rv = SECFailure;
} else {
int fileCounter = 0;
while (SEC_PKCS12DecoderIterateNext(p12dcx, &dip) == SECSuccess) {
switch (dip->type) {
case SEC_OID_PKCS12_V1_CERT_BAG_ID:
printf("Certificate");
if (dumpRawFile) {
PRFileDesc *fd;
char fileName[20];
sprintf(fileName, "file%04d.der", ++fileCounter);
fd = PR_Open(fileName,
PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
0600);
if (!fd) {
SECU_PrintError(progName,
"Cannot create output file");
} else {
PR_Write(fd, dip->der->data, dip->der->len);
PR_Close(fd);
}
} else if (SECU_PrintSignedData(stdout, dip->der,
(dip->hasKey) ? "(has private key)"
: "",
0, (SECU_PPFunc)SECU_PrintCertificate) !=
0) {
SECU_PrintError(progName, "PKCS12 print cert bag failed");
}
if (dip->friendlyName != NULL) {
printf(" Friendly Name: %s\n\n",
dip->friendlyName->data);
}
if (dip->shroudAlg) {
SECU_PrintAlgorithmID(stdout, dip->shroudAlg,
"Encryption algorithm", 1);
}
break;
case SEC_OID_PKCS12_V1_KEY_BAG_ID:
case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
printf("Key");
if (dip->type == SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID)
printf("(shrouded)");
printf(":\n");
if (dip->friendlyName != NULL) {
printf(" Friendly Name: %s\n\n",
dip->friendlyName->data);
}
if (dip->shroudAlg) {
SECU_PrintAlgorithmID(stdout, dip->shroudAlg,
"Encryption algorithm", 1);
}
break;
default:
printf("unknown bag type(%d): %s\n\n", dip->type,
SECOID_FindOIDTagDescription(dip->type));
break;
}
}
rv = SECSuccess;
}
loser:
if (p12dcx) {
SEC_PKCS12DecoderFinish(p12dcx);
}
if (uniPwitem.data) {
SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
}
return rv;
}
/*
* use the oid table description to map a user input string to a particular
* oid.
*/
SECOidTag
PKCS12U_MapCipherFromString(char *cipherString, int keyLen)
{
SECOidTag tag;
SECOidData *oid;
SECOidTag cipher;
/* future enhancement: accept dotted oid spec? */
/* future enhancement: provide 'friendlier' typed in names for
* pbe mechanisms.
*/
/* look for the oid tag by Description */
cipher = SEC_OID_UNKNOWN;
for (tag = 1; (oid = SECOID_FindOIDByTag(tag)) != NULL; tag++) {
/* only interested in oids that we actually understand */
if (oid->mechanism == CKM_INVALID_MECHANISM) {
continue;
}
if (PORT_Strcasecmp(oid->desc, cipherString) != 0) {
continue;
}
/* we found a match... get the PBE version of this
* cipher... */
if (!SEC_PKCS5IsAlgorithmPBEAlgTag(tag)) {
cipher = SEC_PKCS5GetPBEAlgorithm(tag, keyLen);
/* no eqivalent PKCS5/PKCS12 cipher, use the raw
* encryption tag we got and pass it directly in,
* pkcs12 will use the pkcsv5 mechanism */
if (cipher == SEC_OID_PKCS5_PBES2) {
cipher = tag;
} else if (cipher == SEC_OID_PKCS5_PBMAC1) {
/* make sure we have not macing ciphers here */
cipher = SEC_OID_UNKNOWN;
}
} else {
cipher = tag;
}
break;
}
return cipher;
}
static void
p12u_EnableAllCiphers()
{
SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
SEC_PKCS12EnableCipher(PKCS12_AES_CBC_128, 1);
SEC_PKCS12EnableCipher(PKCS12_AES_CBC_192, 1);
SEC_PKCS12EnableCipher(PKCS12_AES_CBC_256, 1);
SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
}
static PRUintn
P12U_Init(char *dir, char *dbprefix, PRBool listonly)
{
SECStatus rv;
PK11_SetPasswordFunc(SECU_GetModulePassword);
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
if (listonly && NSS_NoDB_Init("") == SECSuccess) {
rv = SECSuccess;
} else {
rv = NSS_Initialize(dir, dbprefix, dbprefix, "secmod.db", 0);
}
if (rv != SECSuccess) {
SECU_PrintPRandOSError(progName);
exit(-1);
}
/* setup unicode callback functions */
PORT_SetUCS2_ASCIIConversionFunction(p12u_ucs2_ascii_conversion_function);
/* use the defaults for UCS4-UTF8 and UCS2-UTF8 */
p12u_EnableAllCiphers();
return 0;
}
enum {
opt_CertDir = 0,
opt_TokenName,
opt_Import,
opt_SlotPWFile,
opt_SlotPW,
opt_List,
opt_Nickname,
opt_Export,
opt_Raw,
opt_P12FilePWFile,
opt_P12FilePW,
opt_DBPrefix,
opt_Debug,
opt_Cipher,
opt_CertCipher,
opt_KeyLength,
opt_CertKeyLength
};
static secuCommandFlag pk12util_options[] =
{
{ /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
{ /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE },
{ /* opt_Import */ 'i', PR_TRUE, 0, PR_FALSE },
{ /* opt_SlotPWFile */ 'k', PR_TRUE, 0, PR_FALSE },
{ /* opt_SlotPW */ 'K', PR_TRUE, 0, PR_FALSE },
{ /* opt_List */ 'l', PR_TRUE, 0, PR_FALSE },
{ /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE },
{ /* opt_Export */ 'o', PR_TRUE, 0, PR_FALSE },
{ /* opt_Raw */ 'r', PR_FALSE, 0, PR_FALSE },
{ /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE },
{ /* opt_P12FilePW */ 'W', PR_TRUE, 0, PR_FALSE },
{ /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE },
{ /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE },
{ /* opt_Cipher */ 'c', PR_TRUE, 0, PR_FALSE },
{ /* opt_CertCipher */ 'C', PR_TRUE, 0, PR_FALSE },
{ /* opt_KeyLength */ 'm', PR_TRUE, 0, PR_FALSE, "key_len" },
{ /* opt_CertKeyLength */ 0, PR_TRUE, 0, PR_FALSE, "cert_key_len" }
};
int
main(int argc, char **argv)
{
secuPWData slotPw = { PW_NONE, NULL };
secuPWData p12FilePw = { PW_NONE, NULL };
PK11SlotInfo *slot;
char *slotname = NULL;
char *import_file = NULL;
char *export_file = NULL;
char *dbprefix = "";
SECStatus rv;
SECOidTag cipher =
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
SECOidTag certCipher;
int keyLen = 0;
int certKeyLen = 0;
secuCommand pk12util;
PRInt32 forceUnicode;
#ifdef _CRTDBG_MAP_ALLOC
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
pk12util.numCommands = 0;
pk12util.commands = 0;
pk12util.numOptions = sizeof(pk12util_options) / sizeof(secuCommandFlag);
pk12util.options = pk12util_options;
progName = strrchr(argv[0], '/');
progName = progName ? progName + 1 : argv[0];
rv = SECU_ParseCommandLine(argc, argv, progName, &pk12util);
if (rv != SECSuccess)
Usage();
pk12_debugging = pk12util.options[opt_Debug].activated;
if ((pk12util.options[opt_Import].activated +
pk12util.options[opt_Export].activated +
pk12util.options[opt_List].activated) != 1) {
Usage();
}
if (pk12util.options[opt_Export].activated &&
!pk12util.options[opt_Nickname].activated) {
Usage();
}
rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
if (rv != SECSuccess) {
SECU_PrintError(progName,
"Failed to get NSS_PKCS12_DECODE_FORCE_UNICODE option");
Usage();
}
pk12uForceUnicode = forceUnicode;
slotname = SECU_GetOptionArg(&pk12util, opt_TokenName);
import_file = (pk12util.options[opt_List].activated) ? SECU_GetOptionArg(&pk12util, opt_List)
: SECU_GetOptionArg(&pk12util, opt_Import);
export_file = SECU_GetOptionArg(&pk12util, opt_Export);
if (pk12util.options[opt_P12FilePWFile].activated) {
p12FilePw.source = PW_FROMFILE;
p12FilePw.data = PORT_Strdup(pk12util.options[opt_P12FilePWFile].arg);
}
if (pk12util.options[opt_P12FilePW].activated) {
p12FilePw.source = PW_PLAINTEXT;
p12FilePw.data = PORT_Strdup(pk12util.options[opt_P12FilePW].arg);
}
if (pk12util.options[opt_SlotPWFile].activated) {
slotPw.source = PW_FROMFILE;
slotPw.data = PORT_Strdup(pk12util.options[opt_SlotPWFile].arg);
}
if (pk12util.options[opt_SlotPW].activated) {
slotPw.source = PW_PLAINTEXT;
slotPw.data = PORT_Strdup(pk12util.options[opt_SlotPW].arg);
}
if (pk12util.options[opt_CertDir].activated) {
SECU_ConfigDirectory(pk12util.options[opt_CertDir].arg);
}
if (pk12util.options[opt_DBPrefix].activated) {
dbprefix = pk12util.options[opt_DBPrefix].arg;
}
if (pk12util.options[opt_Raw].activated) {
dumpRawFile = PR_TRUE;
}
if (pk12util.options[opt_KeyLength].activated) {
keyLen = atoi(pk12util.options[opt_KeyLength].arg);
}
if (pk12util.options[opt_CertKeyLength].activated) {
certKeyLen = atoi(pk12util.options[opt_CertKeyLength].arg);
}
P12U_Init(SECU_ConfigDirectory(NULL), dbprefix,
pk12util.options[opt_List].activated);
if (!slotname || PL_strcmp(slotname, "internal") == 0)
slot = PK11_GetInternalKeySlot();
else
slot = PK11_FindSlotByName(slotname);
if (!slot) {
SECU_PrintError(progName, "Invalid slot \"%s\"", slotname);
pk12uErrno = PK12UERR_PK11GETSLOT;
goto done;
}
if (pk12util.options[opt_Cipher].activated) {
char *cipherString = pk12util.options[opt_Cipher].arg;
cipher = PKCS12U_MapCipherFromString(cipherString, keyLen);
/* We only want encryption PBE's. make sure we don't have
* any MAC pbes */
if (cipher == SEC_OID_UNKNOWN) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString);
pk12uErrno = PK12UERR_INVALIDALGORITHM;
goto done;
}
}
certCipher = PK11_IsFIPS() ? SEC_OID_UNKNOWN : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
if (pk12util.options[opt_CertCipher].activated) {
char *cipherString = pk12util.options[opt_CertCipher].arg;
if (PORT_Strcasecmp(cipherString, "none") == 0) {
certCipher = SEC_OID_UNKNOWN;
} else {
certCipher = PKCS12U_MapCipherFromString(cipherString, certKeyLen);
/* If the user requested a cipher and we didn't find it, then
* don't just silently not encrypt. */
if (certCipher == SEC_OID_UNKNOWN) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString);
pk12uErrno = PK12UERR_INVALIDALGORITHM;
goto done;
}
}
}
if (pk12util.options[opt_Import].activated) {
P12U_ImportPKCS12Object(import_file, slot, &slotPw, &p12FilePw);
} else if (pk12util.options[opt_Export].activated) {
P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg,
export_file, slot, cipher, certCipher,
&slotPw, &p12FilePw);
} else if (pk12util.options[opt_List].activated) {
P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw);
} else {
Usage();
pk12uErrno = PK12UERR_USAGE;
}
done:
if (import_file != NULL)
PORT_ZFree(import_file, PL_strlen(import_file));
if (export_file != NULL)
PORT_ZFree(export_file, PL_strlen(export_file));
if (slotPw.data != NULL)
PORT_ZFree(slotPw.data, PL_strlen(slotPw.data));
if (p12FilePw.data != NULL)
PORT_ZFree(p12FilePw.data, PL_strlen(p12FilePw.data));
if (slot)
PK11_FreeSlot(slot);
if (NSS_Shutdown() != SECSuccess) {
pk12uErrno = 1;
}
PL_ArenaFinish();
PR_Cleanup();
return pk12uErrno;
}