mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-11 10:20:19 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
1106 lines
30 KiB
C
1106 lines
30 KiB
C
/* 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;
|
|
|
|
PRIntn pk12uErrno = 0;
|
|
|
|
static void
|
|
Usage(char *progName)
|
|
{
|
|
#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 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);
|
|
if (dup)
|
|
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 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 {
|
|
SECU_PrintError(progName,"PKCS12 decode not verified");
|
|
pk12uErrno = PK12UERR_DECODEVERIFY;
|
|
break;
|
|
}
|
|
}
|
|
} while (trypw == PR_TRUE);
|
|
/* 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 };
|
|
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;
|
|
}
|
|
|
|
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 */
|
|
rv = SEC_PKCS12DecoderImportBags(p12dcx);
|
|
if (rv != SECSuccess) {
|
|
SECU_PrintError(progName,"PKCS12 decode import bags 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 = NULL;
|
|
node = CERT_LIST_HEAD(certlist);
|
|
if (node) {
|
|
cert = node->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_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;
|
|
|
|
#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(progName);
|
|
|
|
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(progName);
|
|
}
|
|
|
|
if (pk12util.options[opt_Export].activated &&
|
|
!pk12util.options[opt_Nickname].activated) {
|
|
Usage(progName);
|
|
}
|
|
|
|
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 (cipher == 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(progName);
|
|
pk12uErrno = PK12UERR_USAGE;
|
|
}
|
|
|
|
done:
|
|
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;
|
|
}
|