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