nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / pk12util / pk12util.c
blobe001dbfbbdfda4f4ab91484b97c0a9f497b097bd
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nspr.h"
38 #include "secutil.h"
39 #include "pk11func.h"
40 #include "pkcs12.h"
41 #include "p12plcy.h"
42 #include "pk12util.h"
43 #include "nss.h"
44 #include "secport.h"
45 #include "secpkcs5.h"
46 #include "certdb.h"
48 #define PKCS12_IN_BUFFER_SIZE 200
50 static char *progName;
51 PRBool pk12_debugging = PR_FALSE;
52 PRBool dumpRawFile;
54 PRIntn pk12uErrno = 0;
56 static void
57 Usage(char *progName)
59 #define FPS PR_fprintf(PR_STDERR,
60 FPS "Usage: %s -i importfile [-d certdir] [-P dbprefix] [-h tokenname] [-v]\n",
61 progName);
62 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
64 FPS "Usage: %s -l listfile [-d certdir] [-P dbprefix] [-h tokenname] [-r]\n",
65 progName);
66 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
68 FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix] [-v]\n",
69 progName);
70 FPS "\t\t [-c key_cipher] [-C cert_cipher] [-k key_leng]\n");
71 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
73 exit(PK12UERR_USAGE);
76 static PRBool
77 p12u_OpenFile(p12uContext *p12cxt, PRBool fileRead)
79 if(!p12cxt || !p12cxt->filename) {
80 return PR_FALSE;
83 if(fileRead) {
84 p12cxt->file = PR_Open(p12cxt->filename,
85 PR_RDONLY, 0400);
86 } else {
87 p12cxt->file = PR_Open(p12cxt->filename,
88 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
89 0600);
92 if(!p12cxt->file) {
93 p12cxt->error = PR_TRUE;
94 return PR_FALSE;
97 return PR_TRUE;
100 static void
101 p12u_DestroyContext(p12uContext **ppCtx, PRBool removeFile)
103 if(!ppCtx || !(*ppCtx)) {
104 return;
107 if((*ppCtx)->file != NULL) {
108 PR_Close((*ppCtx)->file);
111 if((*ppCtx)->filename != NULL) {
112 if(removeFile) {
113 PR_Delete((*ppCtx)->filename);
115 PR_Free((*ppCtx)->filename);
118 PR_Free(*ppCtx);
119 *ppCtx = NULL;
122 static p12uContext *
123 p12u_InitContext(PRBool fileImport, char *filename)
125 p12uContext *p12cxt;
126 PRBool fileExist;
128 fileExist = fileImport;
130 p12cxt = PORT_ZNew(p12uContext);
131 if(!p12cxt) {
132 return NULL;
135 p12cxt->error = PR_FALSE;
136 p12cxt->errorValue = 0;
137 p12cxt->filename = strdup(filename);
139 if(!p12u_OpenFile(p12cxt, fileImport)) {
140 p12u_DestroyContext(&p12cxt, PR_FALSE);
141 return NULL;
144 return p12cxt;
147 SECItem *
148 P12U_NicknameCollisionCallback(SECItem *old_nick, PRBool *cancel, void *wincx)
150 char *nick = NULL;
151 SECItem *ret_nick = NULL;
152 CERTCertificate* cert = (CERTCertificate*)wincx;
154 if (!cancel || !cert) {
155 pk12uErrno = PK12UERR_USER_CANCELLED;
156 return NULL;
159 if (!old_nick)
160 fprintf(stdout, "pk12util: no nickname for cert in PKCS12 file.\n");
162 #if 0
163 /* XXX not handled yet */
164 *cancel = PR_TRUE;
165 return NULL;
167 #else
169 nick = CERT_MakeCANickname(cert);
170 if (!nick) {
171 return NULL;
174 if(old_nick && old_nick->data && old_nick->len &&
175 PORT_Strlen(nick) == old_nick->len &&
176 !PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) {
177 PORT_Free(nick);
178 PORT_SetError(SEC_ERROR_IO);
179 return NULL;
182 fprintf(stdout, "pk12util: using nickname: %s\n", nick);
183 ret_nick = PORT_ZNew(SECItem);
184 if(ret_nick == NULL) {
185 PORT_Free(nick);
186 return NULL;
189 ret_nick->data = (unsigned char *)nick;
190 ret_nick->len = PORT_Strlen(nick);
192 return ret_nick;
193 #endif
196 static SECStatus
197 p12u_SwapUnicodeBytes(SECItem *uniItem)
199 unsigned int i;
200 unsigned char a;
201 if((uniItem == NULL) || (uniItem->len % 2)) {
202 return SECFailure;
204 for(i = 0; i < uniItem->len; i += 2) {
205 a = uniItem->data[i];
206 uniItem->data[i] = uniItem->data[i+1];
207 uniItem->data[i+1] = a;
209 return SECSuccess;
212 static PRBool
213 p12u_ucs2_ascii_conversion_function(PRBool toUnicode,
214 unsigned char *inBuf,
215 unsigned int inBufLen,
216 unsigned char *outBuf,
217 unsigned int maxOutBufLen,
218 unsigned int *outBufLen,
219 PRBool swapBytes)
221 SECItem it = { 0 };
222 SECItem *dup = NULL;
223 PRBool ret;
225 #ifdef DEBUG_CONVERSION
226 if (pk12_debugging) {
227 int i;
228 printf("Converted from:\n");
229 for (i=0; i<inBufLen; i++) {
230 printf("%2x ", inBuf[i]);
231 /*if (i%60 == 0) printf("\n");*/
233 printf("\n");
235 #endif
236 it.data = inBuf;
237 it.len = inBufLen;
238 dup = SECITEM_DupItem(&it);
239 /* If converting Unicode to ASCII, swap bytes before conversion
240 * as neccessary.
242 if (!toUnicode && swapBytes) {
243 if (p12u_SwapUnicodeBytes(dup) != SECSuccess) {
244 SECITEM_ZfreeItem(dup, PR_TRUE);
245 return PR_FALSE;
248 /* Perform the conversion. */
249 ret = PORT_UCS2_UTF8Conversion(toUnicode, dup->data, dup->len,
250 outBuf, maxOutBufLen, outBufLen);
251 if (dup)
252 SECITEM_ZfreeItem(dup, PR_TRUE);
254 #ifdef DEBUG_CONVERSION
255 if (pk12_debugging) {
256 int i;
257 printf("Converted to:\n");
258 for (i=0; i<*outBufLen; i++) {
259 printf("%2x ", outBuf[i]);
260 /*if (i%60 == 0) printf("\n");*/
262 printf("\n");
264 #endif
265 return ret;
268 SECStatus
269 P12U_UnicodeConversion(PRArenaPool *arena, SECItem *dest, SECItem *src,
270 PRBool toUnicode, PRBool swapBytes)
272 unsigned int allocLen;
273 if(!dest || !src) {
274 return SECFailure;
276 allocLen = ((toUnicode) ? (src->len << 2) : src->len);
277 if(arena) {
278 dest->data = PORT_ArenaZAlloc(arena, allocLen);
279 } else {
280 dest->data = PORT_ZAlloc(allocLen);
282 if(PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len,
283 dest->data, allocLen, &dest->len,
284 swapBytes) == PR_FALSE) {
285 if(!arena) {
286 PORT_Free(dest->data);
288 dest->data = NULL;
289 return SECFailure;
291 return SECSuccess;
297 SECItem *
298 P12U_GetP12FilePassword(PRBool confirmPw, secuPWData *p12FilePw)
300 char *p0 = NULL;
301 SECItem *pwItem = NULL;
303 if (p12FilePw == NULL || p12FilePw->source == PW_NONE) {
304 char *p1 = NULL;
305 int rc;
306 for (;;) {
307 p0 = SECU_GetPasswordString(NULL,
308 "Enter password for PKCS12 file: ");
309 if (!confirmPw || p0 == NULL)
310 break;
311 p1 = SECU_GetPasswordString(NULL, "Re-enter password: ");
312 if (p1 == NULL) {
313 PORT_ZFree(p0, PL_strlen(p0));
314 p0 = NULL;
315 break;
317 rc = PL_strcmp(p0, p1);
318 PORT_ZFree(p1, PL_strlen(p1));
319 if (rc == 0)
320 break;
321 PORT_ZFree(p0, PL_strlen(p0));
323 } else if (p12FilePw->source == PW_FROMFILE) {
324 p0 = SECU_FilePasswd(NULL, PR_FALSE, p12FilePw->data);
325 } else { /* Plaintext */
326 p0 = PORT_Strdup(p12FilePw->data);
329 if (p0 == NULL) {
330 return NULL;
332 pwItem = SECITEM_AllocItem(NULL, NULL, PL_strlen(p0) + 1);
333 memcpy(pwItem->data, p0, pwItem->len);
335 PORT_ZFree(p0, PL_strlen(p0));
337 return pwItem;
340 SECStatus
341 P12U_InitSlot(PK11SlotInfo *slot, secuPWData *slotPw)
343 SECStatus rv;
345 /* New databases, initialize keydb password. */
346 if (PK11_NeedUserInit(slot)) {
347 rv = SECU_ChangePW(slot,
348 (slotPw->source == PW_PLAINTEXT) ? slotPw->data : 0,
349 (slotPw->source == PW_FROMFILE) ? slotPw->data : 0);
350 if (rv != SECSuccess) {
351 SECU_PrintError(progName, "Failed to initialize slot \"%s\"",
352 PK11_GetSlotName(slot));
353 return SECFailure;
357 if (PK11_Authenticate(slot, PR_TRUE, slotPw) != SECSuccess) {
358 SECU_PrintError(progName,
359 "Failed to authenticate to PKCS11 slot");
360 PORT_SetError(SEC_ERROR_USER_CANCELLED);
361 pk12uErrno = PK12UERR_USER_CANCELLED;
362 return SECFailure;
365 return SECSuccess;
368 /* This routine takes care of getting the PKCS12 file password, then reading and
369 * verifying the file. It returns the decoder context and a filled in password.
370 * (The password is needed by P12U_ImportPKCS12Object() to import the private
371 * key.)
373 SEC_PKCS12DecoderContext *
374 p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
375 secuPWData *slotPw, secuPWData *p12FilePw)
377 SEC_PKCS12DecoderContext *p12dcx = NULL;
378 p12uContext *p12cxt = NULL;
379 SECItem *pwitem = NULL;
380 SECItem p12file = { 0 };
381 SECStatus rv = SECFailure;
382 PRBool swapUnicode = PR_FALSE;
383 PRBool trypw;
384 int error;
386 #ifdef IS_LITTLE_ENDIAN
387 swapUnicode = PR_TRUE;
388 #endif
390 p12cxt = p12u_InitContext(PR_TRUE, in_file);
391 if(!p12cxt) {
392 SECU_PrintError(progName,"File Open failed: %s", in_file);
393 pk12uErrno = PK12UERR_INIT_FILE;
394 return NULL;
397 /* get the password */
398 pwitem = P12U_GetP12FilePassword(PR_FALSE, p12FilePw);
399 if (!pwitem) {
400 pk12uErrno = PK12UERR_USER_CANCELLED;
401 goto done;
404 if(P12U_UnicodeConversion(NULL, uniPwp, pwitem, PR_TRUE,
405 swapUnicode) != SECSuccess) {
406 SECU_PrintError(progName,"Unicode conversion failed");
407 pk12uErrno = PK12UERR_UNICODECONV;
408 goto done;
410 rv = SECU_FileToItem(&p12file, p12cxt->file);
411 if (rv != SECSuccess) {
412 SECU_PrintError(progName,"Failed to read from import file");
413 goto done;
416 do {
417 trypw = PR_FALSE; /* normally we do this once */
418 rv = SECFailure;
419 /* init the decoder context */
420 p12dcx = SEC_PKCS12DecoderStart(uniPwp, slot, slotPw,
421 NULL, NULL, NULL, NULL, NULL);
422 if(!p12dcx) {
423 SECU_PrintError(progName,"PKCS12 decoder start failed");
424 pk12uErrno = PK12UERR_PK12DECODESTART;
425 break;
428 /* decode the item */
429 rv = SEC_PKCS12DecoderUpdate(p12dcx, p12file.data, p12file.len);
431 if(rv != SECSuccess) {
432 error = PR_GetError();
433 if(error == SEC_ERROR_DECRYPTION_DISALLOWED) {
434 PR_SetError(error, 0);
435 break;
437 SECU_PrintError(progName,"PKCS12 decoding failed");
438 pk12uErrno = PK12UERR_DECODE;
441 /* does the blob authenticate properly? */
442 rv = SEC_PKCS12DecoderVerify(p12dcx);
443 if (rv != SECSuccess) {
444 if(uniPwp->len == 2) {
445 /* this is a null PW, try once more with a zero-length PW
446 instead of a null string */
447 SEC_PKCS12DecoderFinish(p12dcx);
448 uniPwp->len = 0;
449 trypw = PR_TRUE;
451 else {
452 SECU_PrintError(progName,"PKCS12 decode not verified");
453 pk12uErrno = PK12UERR_DECODEVERIFY;
454 break;
457 } while (trypw == PR_TRUE);
458 /* rv has been set at this point */
461 done:
462 if (rv != SECSuccess) {
463 if (p12dcx != NULL) {
464 SEC_PKCS12DecoderFinish(p12dcx);
465 p12dcx = NULL;
467 if (uniPwp->data) {
468 SECITEM_ZfreeItem(uniPwp, PR_FALSE);
469 uniPwp->data = NULL;
472 PR_Close(p12cxt->file);
473 p12cxt->file = NULL;
474 /* PK11_FreeSlot(slot); */
475 p12u_DestroyContext(&p12cxt, PR_FALSE);
477 if (pwitem) {
478 SECITEM_ZfreeItem(pwitem, PR_TRUE);
480 return p12dcx;
484 * given a filename for pkcs12 file, imports certs and keys
486 * Change: altitude
487 * I've changed this function so that it takes the keydb and pkcs12 file
488 * passwords from files. The "pwdKeyDB" and "pwdP12File"
489 * variables have been added for this purpose.
491 PRIntn
492 P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot,
493 secuPWData *slotPw, secuPWData *p12FilePw)
495 SEC_PKCS12DecoderContext *p12dcx = NULL;
496 SECItem uniPwitem = { 0 };
497 SECStatus rv = SECFailure;
499 rv = P12U_InitSlot(slot, slotPw);
500 if (rv != SECSuccess) {
501 SECU_PrintError(progName, "Failed to authenticate to \"%s\"",
502 PK11_GetSlotName(slot));
503 pk12uErrno = PK12UERR_PK11GETSLOT;
504 return rv;
507 rv = SECFailure;
508 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
510 if(p12dcx == NULL) {
511 goto loser;
514 /* make sure the bags are okey dokey -- nicknames correct, etc. */
515 rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback);
516 if (rv != SECSuccess) {
517 if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
518 pk12uErrno = PK12UERR_CERTALREADYEXISTS;
519 } else {
520 pk12uErrno = PK12UERR_DECODEVALIBAGS;
522 SECU_PrintError(progName,"PKCS12 decode validate bags failed");
523 goto loser;
526 /* stuff 'em in */
527 rv = SEC_PKCS12DecoderImportBags(p12dcx);
528 if (rv != SECSuccess) {
529 SECU_PrintError(progName,"PKCS12 decode import bags failed");
530 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
531 goto loser;
534 fprintf(stdout, "%s: PKCS12 IMPORT SUCCESSFUL\n", progName);
535 rv = SECSuccess;
537 loser:
538 if (p12dcx) {
539 SEC_PKCS12DecoderFinish(p12dcx);
542 if (uniPwitem.data) {
543 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
546 return rv;
549 static void
550 p12u_DoPKCS12ExportErrors()
552 int error_value;
554 error_value = PORT_GetError();
555 if ((error_value == SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY) ||
556 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME) ||
557 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_WRITE)) {
558 fprintf(stderr, SECU_ErrorStringRaw((int16)error_value));
559 } else if(error_value == SEC_ERROR_USER_CANCELLED) {
561 } else {
562 fprintf(stderr, SECU_ErrorStringRaw(SEC_ERROR_EXPORTING_CERTIFICATES));
566 static void
567 p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len)
569 p12uContext *p12cxt = arg;
570 int writeLen;
572 if(!p12cxt || (p12cxt->error == PR_TRUE)) {
573 return;
576 if(p12cxt->file == NULL) {
577 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
578 p12cxt->error = PR_TRUE;
579 return;
582 writeLen = PR_Write(p12cxt->file, (unsigned char *)buf, (int32)len);
584 if(writeLen != (int)len) {
585 PR_Close(p12cxt->file);
586 PR_Free(p12cxt->filename);
587 p12cxt->filename = NULL;
588 p12cxt->file = NULL;
589 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
590 p12cxt->error = PR_TRUE;
595 void
596 P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
597 SECOidTag cipher, SECOidTag certCipher,
598 secuPWData *slotPw, secuPWData *p12FilePw)
600 SEC_PKCS12ExportContext *p12ecx = NULL;
601 SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL;
602 SECItem *pwitem = NULL;
603 p12uContext *p12cxt = NULL;
604 CERTCertList* certlist = NULL;
605 CERTCertListNode* node = NULL;
606 PK11SlotInfo* slot = NULL;
608 if (P12U_InitSlot(inSlot, slotPw) != SECSuccess) {
609 SECU_PrintError(progName,"Failed to authenticate to \"%s\"",
610 PK11_GetSlotName(inSlot));
611 pk12uErrno = PK12UERR_PK11GETSLOT;
612 goto loser;
614 certlist = PK11_FindCertsFromNickname(nn, slotPw);
615 if(!certlist) {
616 SECU_PrintError(progName,"find user certs from nickname failed");
617 pk12uErrno = PK12UERR_FINDCERTBYNN;
618 return;
621 if ((SECSuccess != CERT_FilterCertListForUserCerts(certlist)) ||
622 CERT_LIST_EMPTY(certlist)) {
623 PR_fprintf(PR_STDERR, "%s: no user certs from given nickname\n",
624 progName);
625 pk12uErrno = PK12UERR_FINDCERTBYNN;
626 goto loser;
629 /* Password to use for PKCS12 file. */
630 pwitem = P12U_GetP12FilePassword(PR_TRUE, p12FilePw);
631 if(!pwitem) {
632 goto loser;
635 p12cxt = p12u_InitContext(PR_FALSE, outfile);
636 if(!p12cxt) {
637 SECU_PrintError(progName,"Initialization failed: %s", outfile);
638 pk12uErrno = PK12UERR_INIT_FILE;
639 goto loser;
642 if (certlist) {
643 CERTCertificate* cert = NULL;
644 node = CERT_LIST_HEAD(certlist);
645 if (node) {
646 cert = node->cert;
648 if (cert) {
649 slot = cert->slot; /* use the slot from the first matching
650 certificate to create the context . This is for keygen */
653 if (!slot) {
654 SECU_PrintError(progName,"cert does not have a slot");
655 pk12uErrno = PK12UERR_FINDCERTBYNN;
656 goto loser;
658 p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL, slot, slotPw);
659 if(!p12ecx) {
660 SECU_PrintError(progName,"export context creation failed");
661 pk12uErrno = PK12UERR_EXPORTCXCREATE;
662 goto loser;
665 if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, SEC_OID_SHA1)
666 != SECSuccess) {
667 SECU_PrintError(progName,"PKCS12 add password integrity failed");
668 pk12uErrno = PK12UERR_PK12ADDPWDINTEG;
669 goto loser;
672 for (node = CERT_LIST_HEAD(certlist);!CERT_LIST_END(node,certlist);node=CERT_LIST_NEXT(node))
674 CERTCertificate* cert = node->cert;
675 if (!cert->slot) {
676 SECU_PrintError(progName,"cert does not have a slot");
677 pk12uErrno = PK12UERR_FINDCERTBYNN;
678 goto loser;
681 keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
682 if(certCipher == SEC_OID_UNKNOWN) {
683 certSafe = keySafe;
684 } else {
685 certSafe =
686 SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certCipher);
689 if(!certSafe || !keySafe) {
690 SECU_PrintError(progName,"key or cert safe creation failed");
691 pk12uErrno = PK12UERR_CERTKEYSAFE;
692 goto loser;
695 if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert,
696 CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem, cipher)
697 != SECSuccess) {
698 SECU_PrintError(progName,"add cert and key failed");
699 pk12uErrno = PK12UERR_ADDCERTKEY;
700 goto loser;
704 CERT_DestroyCertList(certlist);
705 certlist = NULL;
707 if(SEC_PKCS12Encode(p12ecx, p12u_WriteToExportFile, p12cxt)
708 != SECSuccess) {
709 SECU_PrintError(progName,"PKCS12 encode failed");
710 pk12uErrno = PK12UERR_ENCODE;
711 goto loser;
714 p12u_DestroyContext(&p12cxt, PR_FALSE);
715 SECITEM_ZfreeItem(pwitem, PR_TRUE);
716 fprintf(stdout, "%s: PKCS12 EXPORT SUCCESSFUL\n", progName);
717 SEC_PKCS12DestroyExportContext(p12ecx);
719 return;
721 loser:
722 SEC_PKCS12DestroyExportContext(p12ecx);
724 if (certlist) {
725 CERT_DestroyCertList(certlist);
726 certlist = NULL;
729 p12u_DestroyContext(&p12cxt, PR_TRUE);
730 if(pwitem) {
731 SECITEM_ZfreeItem(pwitem, PR_TRUE);
733 p12u_DoPKCS12ExportErrors();
734 return;
738 PRIntn
739 P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot,
740 secuPWData *slotPw, secuPWData *p12FilePw)
742 SEC_PKCS12DecoderContext *p12dcx = NULL;
743 SECItem uniPwitem = { 0 };
744 SECStatus rv = SECFailure;
745 const SEC_PKCS12DecoderItem *dip;
747 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw,
748 p12FilePw);
749 /* did the blob authenticate properly? */
750 if(p12dcx == NULL) {
751 SECU_PrintError(progName,"PKCS12 decode not verified");
752 pk12uErrno = PK12UERR_DECODEVERIFY;
753 goto loser;
755 rv = SEC_PKCS12DecoderIterateInit(p12dcx);
756 if(rv != SECSuccess) {
757 SECU_PrintError(progName,"PKCS12 decode iterate bags failed");
758 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
759 rv = SECFailure;
760 } else {
761 int fileCounter = 0;
762 while (SEC_PKCS12DecoderIterateNext(p12dcx, &dip) == SECSuccess) {
763 switch (dip->type) {
764 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
765 printf("Certificate");
766 if (dumpRawFile) {
767 PRFileDesc * fd;
768 char fileName[20];
769 sprintf(fileName, "file%04d.der", ++fileCounter);
770 fd = PR_Open(fileName,
771 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
772 0600);
773 if (!fd) {
774 SECU_PrintError(progName,
775 "Cannot create output file");
776 } else {
777 PR_Write(fd, dip->der->data, dip->der->len);
778 PR_Close(fd);
780 } else
781 if (SECU_PrintSignedData(stdout, dip->der,
782 (dip->hasKey) ? "(has private key)" : "",
783 0, SECU_PrintCertificate) != 0) {
784 SECU_PrintError(progName,"PKCS12 print cert bag failed");
786 if (dip->friendlyName != NULL) {
787 printf(" Friendly Name: %s\n\n",
788 dip->friendlyName->data);
790 if (dip->shroudAlg) {
791 SECU_PrintAlgorithmID(stdout, dip->shroudAlg,
792 "Encryption algorithm",1);
794 break;
795 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
796 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
797 printf("Key");
798 if (dip->type == SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID)
799 printf("(shrouded)");
800 printf(":\n");
801 if (dip->friendlyName != NULL) {
802 printf(" Friendly Name: %s\n\n",
803 dip->friendlyName->data);
805 if (dip->shroudAlg) {
806 SECU_PrintAlgorithmID(stdout, dip->shroudAlg,
807 "Encryption algorithm",1);
809 break;
810 default:
811 printf("unknown bag type(%d): %s\n\n", dip->type,
812 SECOID_FindOIDTagDescription(dip->type));
813 break;
816 rv = SECSuccess;
819 loser:
821 if (p12dcx) {
822 SEC_PKCS12DecoderFinish(p12dcx);
825 if (uniPwitem.data) {
826 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
829 return rv;
833 * use the oid table description to map a user input string to a particular
834 * oid.
836 SECOidTag
837 PKCS12U_MapCipherFromString(char *cipherString, int keyLen)
839 SECOidTag tag;
840 SECOidData *oid;
841 SECOidTag cipher;
843 /* future enhancement: accept dotted oid spec? */
845 /* future enhancement: provide 'friendlier' typed in names for
846 * pbe mechanisms.
849 /* look for the oid tag by Description */
850 cipher = SEC_OID_UNKNOWN;
851 for (tag=1; (oid=SECOID_FindOIDByTag(tag)) != NULL ; tag++) {
852 /* only interested in oids that we actually understand */
853 if (oid->mechanism == CKM_INVALID_MECHANISM) {
854 continue;
856 if (PORT_Strcasecmp(oid->desc, cipherString) != 0) {
857 continue;
859 /* we found a match... get the PBE version of this
860 * cipher... */
861 if (!SEC_PKCS5IsAlgorithmPBEAlgTag(tag)) {
862 cipher = SEC_PKCS5GetPBEAlgorithm(tag, keyLen);
863 /* no eqivalent PKCS5/PKCS12 cipher, use the raw
864 * encryption tag we got and pass it directly in,
865 * pkcs12 will use the pkcsv5 mechanism */
866 if (cipher == SEC_OID_PKCS5_PBES2) {
867 cipher = tag;
868 } else if (cipher == SEC_OID_PKCS5_PBMAC1) {
869 /* make sure we have not macing ciphers here */
870 cipher = SEC_OID_UNKNOWN;
872 } else {
873 cipher = tag;
875 break;
877 return cipher;
880 static void
881 p12u_EnableAllCiphers()
883 SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
884 SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
885 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
886 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
887 SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
888 SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
889 SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
892 static PRUintn
893 P12U_Init(char *dir, char *dbprefix, PRBool listonly)
895 SECStatus rv;
896 PK11_SetPasswordFunc(SECU_GetModulePassword);
898 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
899 if (listonly && NSS_NoDB_Init("") == SECSuccess) {
900 rv = SECSuccess;
902 else {
903 rv = NSS_Initialize(dir,dbprefix,dbprefix,"secmod.db",0);
905 if (rv != SECSuccess) {
906 SECU_PrintPRandOSError(progName);
907 exit(-1);
910 /* setup unicode callback functions */
911 PORT_SetUCS2_ASCIIConversionFunction(p12u_ucs2_ascii_conversion_function);
912 /* use the defaults for UCS4-UTF8 and UCS2-UTF8 */
914 p12u_EnableAllCiphers();
916 return 0;
919 enum {
920 opt_CertDir = 0,
921 opt_TokenName,
922 opt_Import,
923 opt_SlotPWFile,
924 opt_SlotPW,
925 opt_List,
926 opt_Nickname,
927 opt_Export,
928 opt_Raw,
929 opt_P12FilePWFile,
930 opt_P12FilePW,
931 opt_DBPrefix,
932 opt_Debug,
933 opt_Cipher,
934 opt_CertCipher,
935 opt_KeyLength,
936 opt_CertKeyLength
939 static secuCommandFlag pk12util_options[] =
941 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
942 { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE },
943 { /* opt_Import */ 'i', PR_TRUE, 0, PR_FALSE },
944 { /* opt_SlotPWFile */ 'k', PR_TRUE, 0, PR_FALSE },
945 { /* opt_SlotPW */ 'K', PR_TRUE, 0, PR_FALSE },
946 { /* opt_List */ 'l', PR_TRUE, 0, PR_FALSE },
947 { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE },
948 { /* opt_Export */ 'o', PR_TRUE, 0, PR_FALSE },
949 { /* opt_Raw */ 'r', PR_FALSE, 0, PR_FALSE },
950 { /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE },
951 { /* opt_P12FilePW */ 'W', PR_TRUE, 0, PR_FALSE },
952 { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE },
953 { /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE },
954 { /* opt_Cipher */ 'c', PR_TRUE, 0, PR_FALSE },
955 { /* opt_CertCipher */ 'C', PR_TRUE, 0, PR_FALSE },
956 { /* opt_KeyLength */ 'k', PR_TRUE, 0, PR_FALSE },
957 { /* opt_CertKeyLength */ 'K', PR_TRUE, 0, PR_FALSE }
961 main(int argc, char **argv)
963 secuPWData slotPw = { PW_NONE, NULL };
964 secuPWData p12FilePw = { PW_NONE, NULL };
965 PK11SlotInfo *slot;
966 char *slotname = NULL;
967 char *import_file = NULL;
968 char *export_file = NULL;
969 char *dbprefix = "";
970 SECStatus rv;
971 SECOidTag cipher =
972 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
973 SECOidTag certCipher;
974 int keyLen = 0;
975 int certKeyLen = 0;
977 secuCommand pk12util;
978 pk12util.numCommands = 0;
979 pk12util.commands = 0;
980 pk12util.numOptions = sizeof(pk12util_options) / sizeof(secuCommandFlag);
981 pk12util.options = pk12util_options;
983 progName = strrchr(argv[0], '/');
984 progName = progName ? progName+1 : argv[0];
986 rv = SECU_ParseCommandLine(argc, argv, progName, &pk12util);
988 if (rv != SECSuccess)
989 Usage(progName);
991 pk12_debugging = pk12util.options[opt_Debug].activated;
993 if ((pk12util.options[opt_Import].activated +
994 pk12util.options[opt_Export].activated +
995 pk12util.options[opt_List].activated) != 1) {
996 Usage(progName);
999 if (pk12util.options[opt_Export].activated &&
1000 !pk12util.options[opt_Nickname].activated) {
1001 Usage(progName);
1004 slotname = SECU_GetOptionArg(&pk12util, opt_TokenName);
1006 import_file = (pk12util.options[opt_List].activated) ?
1007 SECU_GetOptionArg(&pk12util, opt_List) :
1008 SECU_GetOptionArg(&pk12util, opt_Import);
1009 export_file = SECU_GetOptionArg(&pk12util, opt_Export);
1011 if (pk12util.options[opt_P12FilePWFile].activated) {
1012 p12FilePw.source = PW_FROMFILE;
1013 p12FilePw.data = PL_strdup(pk12util.options[opt_P12FilePWFile].arg);
1016 if (pk12util.options[opt_P12FilePW].activated) {
1017 p12FilePw.source = PW_PLAINTEXT;
1018 p12FilePw.data = PL_strdup(pk12util.options[opt_P12FilePW].arg);
1021 if (pk12util.options[opt_SlotPWFile].activated) {
1022 slotPw.source = PW_FROMFILE;
1023 slotPw.data = PL_strdup(pk12util.options[opt_SlotPWFile].arg);
1026 if (pk12util.options[opt_SlotPW].activated) {
1027 slotPw.source = PW_PLAINTEXT;
1028 slotPw.data = PL_strdup(pk12util.options[opt_SlotPW].arg);
1031 if (pk12util.options[opt_CertDir].activated) {
1032 SECU_ConfigDirectory(pk12util.options[opt_CertDir].arg);
1034 if (pk12util.options[opt_DBPrefix].activated) {
1035 dbprefix = pk12util.options[opt_DBPrefix].arg;
1037 if (pk12util.options[opt_Raw].activated) {
1038 dumpRawFile = PR_TRUE;
1040 if (pk12util.options[opt_KeyLength].activated) {
1041 keyLen = atoi(pk12util.options[opt_KeyLength].arg);
1043 if (pk12util.options[opt_CertKeyLength].activated) {
1044 certKeyLen = atoi(pk12util.options[opt_CertKeyLength].arg);
1047 P12U_Init(SECU_ConfigDirectory(NULL), dbprefix,
1048 pk12util.options[opt_List].activated);
1050 if (!slotname || PL_strcmp(slotname, "internal") == 0)
1051 slot = PK11_GetInternalKeySlot();
1052 else
1053 slot = PK11_FindSlotByName(slotname);
1055 if (!slot) {
1056 SECU_PrintError(progName,"Invalid slot \"%s\"", slotname);
1057 pk12uErrno = PK12UERR_PK11GETSLOT;
1058 goto done;
1061 if (pk12util.options[opt_Cipher].activated) {
1062 char *cipherString = pk12util.options[opt_Cipher].arg;
1064 cipher = PKCS12U_MapCipherFromString(cipherString, keyLen);
1065 /* We only want encryption PBE's. make sure we don't have
1066 * any MAC pbes */
1067 if (cipher == SEC_OID_UNKNOWN) {
1068 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
1069 SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString);
1070 pk12uErrno = PK12UERR_INVALIDALGORITHM;
1071 goto done;
1075 certCipher = PK11_IsFIPS() ? SEC_OID_UNKNOWN :
1076 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
1077 if (pk12util.options[opt_CertCipher].activated) {
1078 char *cipherString = pk12util.options[opt_CertCipher].arg;
1080 if (PORT_Strcasecmp(cipherString, "none") == 0) {
1081 certCipher = SEC_OID_UNKNOWN;
1082 } else {
1083 certCipher = PKCS12U_MapCipherFromString(cipherString, certKeyLen);
1084 /* If the user requested a cipher and we didn't find it, then
1085 * don't just silently not encrypt. */
1086 if (cipher == SEC_OID_UNKNOWN) {
1087 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
1088 SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString);
1089 pk12uErrno = PK12UERR_INVALIDALGORITHM;
1090 goto done;
1096 if (pk12util.options[opt_Import].activated) {
1097 P12U_ImportPKCS12Object(import_file, slot, &slotPw,
1098 &p12FilePw);
1100 } else if (pk12util.options[opt_Export].activated) {
1101 P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg,
1102 export_file, slot, cipher, certCipher,
1103 &slotPw, &p12FilePw);
1105 } else if (pk12util.options[opt_List].activated) {
1106 P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw);
1108 } else {
1109 Usage(progName);
1110 pk12uErrno = PK12UERR_USAGE;
1113 done:
1114 if (slotPw.data != NULL)
1115 PORT_ZFree(slotPw.data, PL_strlen(slotPw.data));
1116 if (p12FilePw.data != NULL)
1117 PORT_ZFree(p12FilePw.data, PL_strlen(p12FilePw.data));
1118 if (slot) PK11_FreeSlot(slot);
1119 if (NSS_Shutdown() != SECSuccess) {
1120 pk12uErrno = 1;
1122 return pk12uErrno;