Import 3.0 beta 3 tarball
[mozilla-nss.git] / security / nss / cmd / pk12util / pk12util.c
blob147f8b30532cb286d48c1b7591b975231217baa3
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 "certdb.h"
47 #define PKCS12_IN_BUFFER_SIZE 200
49 static char *progName;
50 PRBool pk12_debugging = PR_FALSE;
51 PRBool dumpRawFile;
53 PRIntn pk12uErrno = 0;
55 static void
56 Usage(char *progName)
58 #define FPS PR_fprintf(PR_STDERR,
59 FPS "Usage: %s -i importfile [-d certdir] [-P dbprefix] [-h tokenname] [-v]\n",
60 progName);
61 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
63 FPS "Usage: %s -l listfile [-d certdir] [-P dbprefix] [-h tokenname] [-r]\n",
64 progName);
65 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
67 FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix] [-v]\n",
68 progName);
69 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n");
71 exit(PK12UERR_USAGE);
74 static PRBool
75 p12u_OpenFile(p12uContext *p12cxt, PRBool fileRead)
77 if(!p12cxt || !p12cxt->filename) {
78 return PR_FALSE;
81 if(fileRead) {
82 p12cxt->file = PR_Open(p12cxt->filename,
83 PR_RDONLY, 0400);
84 } else {
85 p12cxt->file = PR_Open(p12cxt->filename,
86 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
87 0600);
90 if(!p12cxt->file) {
91 p12cxt->error = PR_TRUE;
92 return PR_FALSE;
95 return PR_TRUE;
98 static void
99 p12u_DestroyContext(p12uContext **ppCtx, PRBool removeFile)
101 if(!ppCtx || !(*ppCtx)) {
102 return;
105 if((*ppCtx)->file != NULL) {
106 PR_Close((*ppCtx)->file);
109 if((*ppCtx)->filename != NULL) {
110 if(removeFile) {
111 PR_Delete((*ppCtx)->filename);
113 PR_Free((*ppCtx)->filename);
116 PR_Free(*ppCtx);
117 *ppCtx = NULL;
120 static p12uContext *
121 p12u_InitContext(PRBool fileImport, char *filename)
123 p12uContext *p12cxt;
124 PRBool fileExist;
126 fileExist = fileImport;
128 p12cxt = PORT_ZNew(p12uContext);
129 if(!p12cxt) {
130 return NULL;
133 p12cxt->error = PR_FALSE;
134 p12cxt->errorValue = 0;
135 p12cxt->filename = strdup(filename);
137 if(!p12u_OpenFile(p12cxt, fileImport)) {
138 p12u_DestroyContext(&p12cxt, PR_FALSE);
139 return NULL;
142 return p12cxt;
145 SECItem *
146 P12U_NicknameCollisionCallback(SECItem *old_nick, PRBool *cancel, void *wincx)
148 char *nick = NULL;
149 SECItem *ret_nick = NULL;
150 CERTCertificate* cert = (CERTCertificate*)wincx;
152 if (!cancel || !cert) {
153 pk12uErrno = PK12UERR_USER_CANCELLED;
154 return NULL;
157 if (!old_nick)
158 fprintf(stdout, "pk12util: no nickname for cert in PKCS12 file.\n");
160 #if 0
161 /* XXX not handled yet */
162 *cancel = PR_TRUE;
163 return NULL;
165 #else
167 nick = CERT_MakeCANickname(cert);
168 if (!nick) {
169 return NULL;
172 if(old_nick && old_nick->data && old_nick->len &&
173 PORT_Strlen(nick) == old_nick->len &&
174 !PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) {
175 PORT_Free(nick);
176 PORT_SetError(SEC_ERROR_IO);
177 return NULL;
180 fprintf(stdout, "pk12util: using nickname: %s\n", nick);
181 ret_nick = PORT_ZNew(SECItem);
182 if(ret_nick == NULL) {
183 PORT_Free(nick);
184 return NULL;
187 ret_nick->data = (unsigned char *)nick;
188 ret_nick->len = PORT_Strlen(nick);
190 return ret_nick;
191 #endif
194 static SECStatus
195 p12u_SwapUnicodeBytes(SECItem *uniItem)
197 unsigned int i;
198 unsigned char a;
199 if((uniItem == NULL) || (uniItem->len % 2)) {
200 return SECFailure;
202 for(i = 0; i < uniItem->len; i += 2) {
203 a = uniItem->data[i];
204 uniItem->data[i] = uniItem->data[i+1];
205 uniItem->data[i+1] = a;
207 return SECSuccess;
210 static PRBool
211 p12u_ucs2_ascii_conversion_function(PRBool toUnicode,
212 unsigned char *inBuf,
213 unsigned int inBufLen,
214 unsigned char *outBuf,
215 unsigned int maxOutBufLen,
216 unsigned int *outBufLen,
217 PRBool swapBytes)
219 SECItem it = { 0 };
220 SECItem *dup = NULL;
221 PRBool ret;
223 #ifdef DEBUG_CONVERSION
224 if (pk12_debugging) {
225 int i;
226 printf("Converted from:\n");
227 for (i=0; i<inBufLen; i++) {
228 printf("%2x ", inBuf[i]);
229 /*if (i%60 == 0) printf("\n");*/
231 printf("\n");
233 #endif
234 it.data = inBuf;
235 it.len = inBufLen;
236 dup = SECITEM_DupItem(&it);
237 /* If converting Unicode to ASCII, swap bytes before conversion
238 * as neccessary.
240 if (!toUnicode && swapBytes) {
241 if (p12u_SwapUnicodeBytes(dup) != SECSuccess) {
242 SECITEM_ZfreeItem(dup, PR_TRUE);
243 return PR_FALSE;
246 /* Perform the conversion. */
247 ret = PORT_UCS2_UTF8Conversion(toUnicode, dup->data, dup->len,
248 outBuf, maxOutBufLen, outBufLen);
249 if (dup)
250 SECITEM_ZfreeItem(dup, PR_TRUE);
252 #ifdef DEBUG_CONVERSION
253 if (pk12_debugging) {
254 int i;
255 printf("Converted to:\n");
256 for (i=0; i<*outBufLen; i++) {
257 printf("%2x ", outBuf[i]);
258 /*if (i%60 == 0) printf("\n");*/
260 printf("\n");
262 #endif
263 return ret;
266 SECStatus
267 P12U_UnicodeConversion(PRArenaPool *arena, SECItem *dest, SECItem *src,
268 PRBool toUnicode, PRBool swapBytes)
270 unsigned int allocLen;
271 if(!dest || !src) {
272 return SECFailure;
274 allocLen = ((toUnicode) ? (src->len << 2) : src->len);
275 if(arena) {
276 dest->data = PORT_ArenaZAlloc(arena, allocLen);
277 } else {
278 dest->data = PORT_ZAlloc(allocLen);
280 if(PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len,
281 dest->data, allocLen, &dest->len,
282 swapBytes) == PR_FALSE) {
283 if(!arena) {
284 PORT_Free(dest->data);
286 dest->data = NULL;
287 return SECFailure;
289 return SECSuccess;
295 SECItem *
296 P12U_GetP12FilePassword(PRBool confirmPw, secuPWData *p12FilePw)
298 char *p0 = NULL;
299 SECItem *pwItem = NULL;
301 if (p12FilePw == NULL || p12FilePw->source == PW_NONE) {
302 char *p1 = NULL;
303 int rc;
304 for (;;) {
305 p0 = SECU_GetPasswordString(NULL,
306 "Enter password for PKCS12 file: ");
307 if (!confirmPw || p0 == NULL)
308 break;
309 p1 = SECU_GetPasswordString(NULL, "Re-enter password: ");
310 if (p1 == NULL) {
311 PORT_ZFree(p0, PL_strlen(p0));
312 p0 = NULL;
313 break;
315 rc = PL_strcmp(p0, p1);
316 PORT_ZFree(p1, PL_strlen(p1));
317 if (rc == 0)
318 break;
319 PORT_ZFree(p0, PL_strlen(p0));
321 } else if (p12FilePw->source == PW_FROMFILE) {
322 p0 = SECU_FilePasswd(NULL, PR_FALSE, p12FilePw->data);
323 } else { /* Plaintext */
324 p0 = PORT_Strdup(p12FilePw->data);
327 if (p0 == NULL) {
328 return NULL;
330 pwItem = SECITEM_AllocItem(NULL, NULL, PL_strlen(p0) + 1);
331 memcpy(pwItem->data, p0, pwItem->len);
333 PORT_ZFree(p0, PL_strlen(p0));
335 return pwItem;
338 SECStatus
339 P12U_InitSlot(PK11SlotInfo *slot, secuPWData *slotPw)
341 SECStatus rv;
343 /* New databases, initialize keydb password. */
344 if (PK11_NeedUserInit(slot)) {
345 rv = SECU_ChangePW(slot,
346 (slotPw->source == PW_PLAINTEXT) ? slotPw->data : 0,
347 (slotPw->source == PW_FROMFILE) ? slotPw->data : 0);
348 if (rv != SECSuccess) {
349 SECU_PrintError(progName, "Failed to initialize slot \"%s\"",
350 PK11_GetSlotName(slot));
351 return SECFailure;
355 if (PK11_Authenticate(slot, PR_TRUE, slotPw) != SECSuccess) {
356 SECU_PrintError(progName,
357 "Failed to authenticate to PKCS11 slot");
358 PORT_SetError(SEC_ERROR_USER_CANCELLED);
359 pk12uErrno = PK12UERR_USER_CANCELLED;
360 return SECFailure;
363 return SECSuccess;
366 /* This routine takes care of getting the PKCS12 file password, then reading and
367 * verifying the file. It returns the decoder context and a filled in password.
368 * (The password is needed by P12U_ImportPKCS12Object() to import the private
369 * key.)
371 SEC_PKCS12DecoderContext *
372 p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot,
373 secuPWData *slotPw, secuPWData *p12FilePw)
375 SEC_PKCS12DecoderContext *p12dcx = NULL;
376 p12uContext *p12cxt = NULL;
377 SECItem *pwitem = NULL;
378 SECItem p12file = { 0 };
379 SECStatus rv = SECFailure;
380 PRBool swapUnicode = PR_FALSE;
381 PRBool trypw;
382 int error;
384 #ifdef IS_LITTLE_ENDIAN
385 swapUnicode = PR_TRUE;
386 #endif
388 p12cxt = p12u_InitContext(PR_TRUE, in_file);
389 if(!p12cxt) {
390 SECU_PrintError(progName,"File Open failed: %s", in_file);
391 pk12uErrno = PK12UERR_INIT_FILE;
392 return NULL;
395 /* get the password */
396 pwitem = P12U_GetP12FilePassword(PR_FALSE, p12FilePw);
397 if (!pwitem) {
398 pk12uErrno = PK12UERR_USER_CANCELLED;
399 goto done;
402 if(P12U_UnicodeConversion(NULL, uniPwp, pwitem, PR_TRUE,
403 swapUnicode) != SECSuccess) {
404 SECU_PrintError(progName,"Unicode conversion failed");
405 pk12uErrno = PK12UERR_UNICODECONV;
406 goto done;
408 rv = SECU_FileToItem(&p12file, p12cxt->file);
409 if (rv != SECSuccess) {
410 SECU_PrintError(progName,"Failed to read from import file");
411 goto done;
414 do {
415 trypw = PR_FALSE; /* normally we do this once */
416 rv = SECFailure;
417 /* init the decoder context */
418 p12dcx = SEC_PKCS12DecoderStart(uniPwp, slot, slotPw,
419 NULL, NULL, NULL, NULL, NULL);
420 if(!p12dcx) {
421 SECU_PrintError(progName,"PKCS12 decoder start failed");
422 pk12uErrno = PK12UERR_PK12DECODESTART;
423 break;
426 /* decode the item */
427 rv = SEC_PKCS12DecoderUpdate(p12dcx, p12file.data, p12file.len);
429 if(rv != SECSuccess) {
430 error = PR_GetError();
431 if(error == SEC_ERROR_DECRYPTION_DISALLOWED) {
432 PR_SetError(error, 0);
433 break;
435 SECU_PrintError(progName,"PKCS12 decoding failed");
436 pk12uErrno = PK12UERR_DECODE;
439 /* does the blob authenticate properly? */
440 rv = SEC_PKCS12DecoderVerify(p12dcx);
441 if (rv != SECSuccess) {
442 if(uniPwp->len == 2) {
443 /* this is a null PW, try once more with a zero-length PW
444 instead of a null string */
445 SEC_PKCS12DecoderFinish(p12dcx);
446 uniPwp->len = 0;
447 trypw = PR_TRUE;
449 else {
450 SECU_PrintError(progName,"PKCS12 decode not verified");
451 pk12uErrno = PK12UERR_DECODEVERIFY;
452 break;
455 } while (trypw == PR_TRUE);
456 /* rv has been set at this point */
459 done:
460 if (rv != SECSuccess) {
461 if (p12dcx != NULL) {
462 SEC_PKCS12DecoderFinish(p12dcx);
463 p12dcx = NULL;
465 if (uniPwp->data) {
466 SECITEM_ZfreeItem(uniPwp, PR_FALSE);
467 uniPwp->data = NULL;
470 PR_Close(p12cxt->file);
471 p12cxt->file = NULL;
472 /* PK11_FreeSlot(slot); */
473 p12u_DestroyContext(&p12cxt, PR_FALSE);
475 if (pwitem) {
476 SECITEM_ZfreeItem(pwitem, PR_TRUE);
478 return p12dcx;
482 * given a filename for pkcs12 file, imports certs and keys
484 * Change: altitude
485 * I've changed this function so that it takes the keydb and pkcs12 file
486 * passwords from files. The "pwdKeyDB" and "pwdP12File"
487 * variables have been added for this purpose.
489 PRIntn
490 P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot,
491 secuPWData *slotPw, secuPWData *p12FilePw)
493 SEC_PKCS12DecoderContext *p12dcx = NULL;
494 SECItem uniPwitem = { 0 };
495 SECStatus rv = SECFailure;
497 rv = P12U_InitSlot(slot, slotPw);
498 if (rv != SECSuccess) {
499 SECU_PrintError(progName, "Failed to authenticate to \"%s\"",
500 PK11_GetSlotName(slot));
501 pk12uErrno = PK12UERR_PK11GETSLOT;
502 return rv;
505 rv = SECFailure;
506 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw);
508 if(p12dcx == NULL) {
509 goto loser;
512 /* make sure the bags are okey dokey -- nicknames correct, etc. */
513 rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback);
514 if (rv != SECSuccess) {
515 if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) {
516 pk12uErrno = PK12UERR_CERTALREADYEXISTS;
517 } else {
518 pk12uErrno = PK12UERR_DECODEVALIBAGS;
520 SECU_PrintError(progName,"PKCS12 decode validate bags failed");
521 goto loser;
524 /* stuff 'em in */
525 rv = SEC_PKCS12DecoderImportBags(p12dcx);
526 if (rv != SECSuccess) {
527 SECU_PrintError(progName,"PKCS12 decode import bags failed");
528 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
529 goto loser;
532 fprintf(stdout, "%s: PKCS12 IMPORT SUCCESSFUL\n", progName);
533 rv = SECSuccess;
535 loser:
536 if (p12dcx) {
537 SEC_PKCS12DecoderFinish(p12dcx);
540 if (uniPwitem.data) {
541 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
544 return rv;
547 static void
548 p12u_DoPKCS12ExportErrors()
550 int error_value;
552 error_value = PORT_GetError();
553 if ((error_value == SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY) ||
554 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME) ||
555 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_WRITE)) {
556 fprintf(stderr, SECU_ErrorStringRaw((int16)error_value));
557 } else if(error_value == SEC_ERROR_USER_CANCELLED) {
559 } else {
560 fprintf(stderr, SECU_ErrorStringRaw(SEC_ERROR_EXPORTING_CERTIFICATES));
564 static void
565 p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len)
567 p12uContext *p12cxt = arg;
568 int writeLen;
570 if(!p12cxt || (p12cxt->error == PR_TRUE)) {
571 return;
574 if(p12cxt->file == NULL) {
575 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
576 p12cxt->error = PR_TRUE;
577 return;
580 writeLen = PR_Write(p12cxt->file, (unsigned char *)buf, (int32)len);
582 if(writeLen != (int)len) {
583 PR_Close(p12cxt->file);
584 PR_Free(p12cxt->filename);
585 p12cxt->filename = NULL;
586 p12cxt->file = NULL;
587 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
588 p12cxt->error = PR_TRUE;
593 void
594 P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
595 secuPWData *slotPw, secuPWData *p12FilePw)
597 SEC_PKCS12ExportContext *p12ecx = NULL;
598 SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL;
599 SECItem *pwitem = NULL;
600 p12uContext *p12cxt = NULL;
601 CERTCertList* certlist = NULL;
602 CERTCertListNode* node = NULL;
603 PK11SlotInfo* slot = NULL;
605 if (P12U_InitSlot(inSlot, slotPw) != SECSuccess) {
606 SECU_PrintError(progName,"Failed to authenticate to \"%s\"",
607 PK11_GetSlotName(inSlot));
608 pk12uErrno = PK12UERR_PK11GETSLOT;
609 goto loser;
611 certlist = PK11_FindCertsFromNickname(nn, slotPw);
612 if(!certlist) {
613 SECU_PrintError(progName,"find user certs from nickname failed");
614 pk12uErrno = PK12UERR_FINDCERTBYNN;
615 return;
618 if ((SECSuccess != CERT_FilterCertListForUserCerts(certlist)) ||
619 CERT_LIST_EMPTY(certlist)) {
620 PR_fprintf(PR_STDERR, "%s: no user certs from given nickname\n",
621 progName);
622 pk12uErrno = PK12UERR_FINDCERTBYNN;
623 goto loser;
626 /* Password to use for PKCS12 file. */
627 pwitem = P12U_GetP12FilePassword(PR_TRUE, p12FilePw);
628 if(!pwitem) {
629 goto loser;
632 p12cxt = p12u_InitContext(PR_FALSE, outfile);
633 if(!p12cxt) {
634 SECU_PrintError(progName,"Initialization failed: %s", outfile);
635 pk12uErrno = PK12UERR_INIT_FILE;
636 goto loser;
639 if (certlist) {
640 CERTCertificate* cert = NULL;
641 node = CERT_LIST_HEAD(certlist);
642 if (node) {
643 cert = node->cert;
645 if (cert) {
646 slot = cert->slot; /* use the slot from the first matching
647 certificate to create the context . This is for keygen */
650 if (!slot) {
651 SECU_PrintError(progName,"cert does not have a slot");
652 pk12uErrno = PK12UERR_FINDCERTBYNN;
653 goto loser;
655 p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL, slot, slotPw);
656 if(!p12ecx) {
657 SECU_PrintError(progName,"export context creation failed");
658 pk12uErrno = PK12UERR_EXPORTCXCREATE;
659 goto loser;
662 if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, SEC_OID_SHA1)
663 != SECSuccess) {
664 SECU_PrintError(progName,"PKCS12 add password integrity failed");
665 pk12uErrno = PK12UERR_PK12ADDPWDINTEG;
666 goto loser;
669 for (node = CERT_LIST_HEAD(certlist);!CERT_LIST_END(node,certlist);node=CERT_LIST_NEXT(node))
671 CERTCertificate* cert = node->cert;
672 if (!cert->slot) {
673 SECU_PrintError(progName,"cert does not have a slot");
674 pk12uErrno = PK12UERR_FINDCERTBYNN;
675 goto loser;
678 keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
679 if(/*!SEC_PKCS12IsEncryptionAllowed() || */ PK11_IsFIPS()) {
680 certSafe = keySafe;
681 } else {
682 certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem,
683 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
686 if(!certSafe || !keySafe) {
687 SECU_PrintError(progName,"key or cert safe creation failed");
688 pk12uErrno = PK12UERR_CERTKEYSAFE;
689 goto loser;
692 if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert,
693 CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem,
694 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC)
695 != SECSuccess) {
696 SECU_PrintError(progName,"add cert and key failed");
697 pk12uErrno = PK12UERR_ADDCERTKEY;
698 goto loser;
702 CERT_DestroyCertList(certlist);
703 certlist = NULL;
705 if(SEC_PKCS12Encode(p12ecx, p12u_WriteToExportFile, p12cxt)
706 != SECSuccess) {
707 SECU_PrintError(progName,"PKCS12 encode failed");
708 pk12uErrno = PK12UERR_ENCODE;
709 goto loser;
712 p12u_DestroyContext(&p12cxt, PR_FALSE);
713 SECITEM_ZfreeItem(pwitem, PR_TRUE);
714 fprintf(stdout, "%s: PKCS12 EXPORT SUCCESSFUL\n", progName);
715 SEC_PKCS12DestroyExportContext(p12ecx);
717 return;
719 loser:
720 SEC_PKCS12DestroyExportContext(p12ecx);
722 if (certlist) {
723 CERT_DestroyCertList(certlist);
724 certlist = NULL;
727 p12u_DestroyContext(&p12cxt, PR_TRUE);
728 if(pwitem) {
729 SECITEM_ZfreeItem(pwitem, PR_TRUE);
731 p12u_DoPKCS12ExportErrors();
732 return;
736 PRIntn
737 P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot,
738 secuPWData *slotPw, secuPWData *p12FilePw)
740 SEC_PKCS12DecoderContext *p12dcx = NULL;
741 SECItem uniPwitem = { 0 };
742 SECStatus rv = SECFailure;
743 const SEC_PKCS12DecoderItem *dip;
745 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw,
746 p12FilePw);
747 /* did the blob authenticate properly? */
748 if(p12dcx == NULL) {
749 SECU_PrintError(progName,"PKCS12 decode not verified");
750 pk12uErrno = PK12UERR_DECODEVERIFY;
751 goto loser;
753 rv = SEC_PKCS12DecoderIterateInit(p12dcx);
754 if(rv != SECSuccess) {
755 SECU_PrintError(progName,"PKCS12 decode iterate bags failed");
756 pk12uErrno = PK12UERR_DECODEIMPTBAGS;
757 rv = SECFailure;
758 } else {
759 int fileCounter = 0;
760 while (SEC_PKCS12DecoderIterateNext(p12dcx, &dip) == SECSuccess) {
761 switch (dip->type) {
762 case SEC_OID_PKCS12_V1_CERT_BAG_ID:
763 printf("Certificate");
764 if (dumpRawFile) {
765 PRFileDesc * fd;
766 char fileName[20];
767 sprintf(fileName, "file%04d.der", ++fileCounter);
768 fd = PR_Open(fileName,
769 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE,
770 0600);
771 if (!fd) {
772 SECU_PrintError(progName,
773 "Cannot create output file");
774 } else {
775 PR_Write(fd, dip->der->data, dip->der->len);
776 PR_Close(fd);
778 } else
779 if (SECU_PrintSignedData(stdout, dip->der,
780 (dip->hasKey) ? "(has private key)" : "",
781 0, SECU_PrintCertificate) != 0) {
782 SECU_PrintError(progName,"PKCS12 print cert bag failed");
784 if (dip->friendlyName != NULL) {
785 printf(" Friendly Name: %s\n\n",
786 dip->friendlyName->data);
788 break;
789 case SEC_OID_PKCS12_V1_KEY_BAG_ID:
790 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
791 printf("Key");
792 if (dip->type == SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID)
793 printf("(shrouded)");
794 printf(":\n");
795 if (dip->friendlyName != NULL) {
796 printf(" Friendly Name: %s\n\n",
797 dip->friendlyName->data);
799 break;
800 default:
801 printf("unknown bag type(%d): %s\n\n", dip->type,
802 SECOID_FindOIDTagDescription(dip->type));
803 break;
806 rv = SECSuccess;
809 loser:
811 if (p12dcx) {
812 SEC_PKCS12DecoderFinish(p12dcx);
815 if (uniPwitem.data) {
816 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
819 return rv;
822 static void
823 p12u_EnableAllCiphers()
825 SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
826 SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
827 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
828 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
829 SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
830 SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
831 SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
834 static PRUintn
835 P12U_Init(char *dir, char *dbprefix, PRBool listonly)
837 SECStatus rv;
838 PK11_SetPasswordFunc(SECU_GetModulePassword);
840 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
841 if (listonly && NSS_NoDB_Init("") == SECSuccess) {
842 rv = SECSuccess;
844 else {
845 rv = NSS_Initialize(dir,dbprefix,dbprefix,"secmod.db",0);
847 if (rv != SECSuccess) {
848 SECU_PrintPRandOSError(progName);
849 exit(-1);
852 /* setup unicode callback functions */
853 PORT_SetUCS2_ASCIIConversionFunction(p12u_ucs2_ascii_conversion_function);
854 /* use the defaults for UCS4-UTF8 and UCS2-UTF8 */
856 p12u_EnableAllCiphers();
858 return 0;
861 enum {
862 opt_CertDir = 0,
863 opt_TokenName,
864 opt_Import,
865 opt_SlotPWFile,
866 opt_SlotPW,
867 opt_List,
868 opt_Nickname,
869 opt_Export,
870 opt_Raw,
871 opt_P12FilePWFile,
872 opt_P12FilePW,
873 opt_DBPrefix,
874 opt_Debug
877 static secuCommandFlag pk12util_options[] =
879 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
880 { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE },
881 { /* opt_Import */ 'i', PR_TRUE, 0, PR_FALSE },
882 { /* opt_SlotPWFile */ 'k', PR_TRUE, 0, PR_FALSE },
883 { /* opt_SlotPW */ 'K', PR_TRUE, 0, PR_FALSE },
884 { /* opt_List */ 'l', PR_TRUE, 0, PR_FALSE },
885 { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE },
886 { /* opt_Export */ 'o', PR_TRUE, 0, PR_FALSE },
887 { /* opt_Raw */ 'r', PR_FALSE, 0, PR_FALSE },
888 { /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE },
889 { /* opt_P12FilePW */ 'W', PR_TRUE, 0, PR_FALSE },
890 { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE },
891 { /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE }
895 main(int argc, char **argv)
897 secuPWData slotPw = { PW_NONE, NULL };
898 secuPWData p12FilePw = { PW_NONE, NULL };
899 PK11SlotInfo *slot;
900 char *slotname = NULL;
901 char *import_file = NULL;
902 char *export_file = NULL;
903 char *dbprefix = "";
904 SECStatus rv;
906 secuCommand pk12util;
907 pk12util.numCommands = 0;
908 pk12util.commands = 0;
909 pk12util.numOptions = sizeof(pk12util_options) / sizeof(secuCommandFlag);
910 pk12util.options = pk12util_options;
912 progName = strrchr(argv[0], '/');
913 progName = progName ? progName+1 : argv[0];
915 rv = SECU_ParseCommandLine(argc, argv, progName, &pk12util);
917 if (rv != SECSuccess)
918 Usage(progName);
920 pk12_debugging = pk12util.options[opt_Debug].activated;
922 if ((pk12util.options[opt_Import].activated +
923 pk12util.options[opt_Export].activated +
924 pk12util.options[opt_List].activated) != 1) {
925 Usage(progName);
928 if (pk12util.options[opt_Export].activated &&
929 !pk12util.options[opt_Nickname].activated) {
930 Usage(progName);
933 slotname = SECU_GetOptionArg(&pk12util, opt_TokenName);
935 import_file = (pk12util.options[opt_List].activated) ?
936 SECU_GetOptionArg(&pk12util, opt_List) :
937 SECU_GetOptionArg(&pk12util, opt_Import);
938 export_file = SECU_GetOptionArg(&pk12util, opt_Export);
940 if (pk12util.options[opt_P12FilePWFile].activated) {
941 p12FilePw.source = PW_FROMFILE;
942 p12FilePw.data = PL_strdup(pk12util.options[opt_P12FilePWFile].arg);
945 if (pk12util.options[opt_P12FilePW].activated) {
946 p12FilePw.source = PW_PLAINTEXT;
947 p12FilePw.data = PL_strdup(pk12util.options[opt_P12FilePW].arg);
950 if (pk12util.options[opt_SlotPWFile].activated) {
951 slotPw.source = PW_FROMFILE;
952 slotPw.data = PL_strdup(pk12util.options[opt_SlotPWFile].arg);
955 if (pk12util.options[opt_SlotPW].activated) {
956 slotPw.source = PW_PLAINTEXT;
957 slotPw.data = PL_strdup(pk12util.options[opt_SlotPW].arg);
960 if (pk12util.options[opt_CertDir].activated) {
961 SECU_ConfigDirectory(pk12util.options[opt_CertDir].arg);
963 if (pk12util.options[opt_DBPrefix].activated) {
964 dbprefix = pk12util.options[opt_DBPrefix].arg;
966 if (pk12util.options[opt_Raw].activated) {
967 dumpRawFile = PR_TRUE;
969 P12U_Init(SECU_ConfigDirectory(NULL), dbprefix,
970 pk12util.options[opt_List].activated);
972 if (!slotname || PL_strcmp(slotname, "internal") == 0)
973 slot = PK11_GetInternalKeySlot();
974 else
975 slot = PK11_FindSlotByName(slotname);
977 if (!slot) {
978 SECU_PrintError(progName,"Invalid slot \"%s\"", slotname);
979 pk12uErrno = PK12UERR_PK11GETSLOT;
980 goto done;
983 if (pk12util.options[opt_Import].activated) {
984 P12U_ImportPKCS12Object(import_file, slot, &slotPw,
985 &p12FilePw);
987 } else if (pk12util.options[opt_Export].activated) {
988 P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg,
989 export_file, slot, &slotPw, &p12FilePw);
991 } else if (pk12util.options[opt_List].activated) {
992 P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw);
994 } else {
995 Usage(progName);
996 pk12uErrno = PK12UERR_USAGE;
999 done:
1000 if (slotPw.data != NULL)
1001 PORT_ZFree(slotPw.data, PL_strlen(slotPw.data));
1002 if (p12FilePw.data != NULL)
1003 PORT_ZFree(p12FilePw.data, PL_strlen(p12FilePw.data));
1004 if (slot) PK11_FreeSlot(slot);
1005 if (NSS_Shutdown() != SECSuccess) {
1006 pk12uErrno = 1;
1008 return pk12uErrno;