pk12util.c (38607B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifdef _CRTDBG_MAP_ALLOC 6 #include <stdlib.h> 7 #include <crtdbg.h> 8 #endif 9 10 #include "nspr.h" 11 #include "secutil.h" 12 #include "pk11func.h" 13 #include "pkcs12.h" 14 #include "p12plcy.h" 15 #include "pk12util.h" 16 #include "nss.h" 17 #include "secport.h" 18 #include "secpkcs5.h" 19 #include "sechash.h" 20 #include "certdb.h" 21 22 #define PKCS12_IN_BUFFER_SIZE 200 23 24 static char *progName; 25 PRBool pk12_debugging = PR_FALSE; 26 PRBool dumpRawFile; 27 static PRBool pk12uForceUnicode; 28 29 PRIntn pk12uErrno = 0; 30 31 static void 32 Usage() 33 { 34 #define FPS PR_fprintf(PR_STDERR, 35 FPS "Usage: %s -i importfile [-I] [-d certdir] [-P dbprefix] [-h tokenname]\n", 36 progName); 37 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n"); 38 FPS "\t\t [-v]\n"); 39 40 FPS "Usage: %s -l listfile [-I] [-d certdir] [-P dbprefix] [-h tokenname]\n", 41 progName); 42 FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n"); 43 FPS "\t\t [-v]\n"); 44 45 FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix]\n", 46 progName); 47 FPS "\t\t [-c key_cipher] [-C cert_cipher] [-M mac_alg]\n" 48 "\t\t [-m | --key_len keyLen] [--cert_key_len certKeyLen] [-v]\n"); 49 FPS "\t\t [-k slotpwfile | -K slotpw]\n" 50 "\t\t [-w p12filepwfile | -W p12filepw]\n"); 51 52 exit(PK12UERR_USAGE); 53 } 54 55 static PRBool 56 p12u_OpenFile(p12uContext *p12cxt, PRBool fileRead) 57 { 58 if (!p12cxt || !p12cxt->filename) { 59 return PR_FALSE; 60 } 61 62 if (fileRead) { 63 p12cxt->file = PR_Open(p12cxt->filename, 64 PR_RDONLY, 0400); 65 } else { 66 p12cxt->file = PR_Open(p12cxt->filename, 67 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 68 0600); 69 } 70 71 if (!p12cxt->file) { 72 p12cxt->error = PR_TRUE; 73 return PR_FALSE; 74 } 75 76 return PR_TRUE; 77 } 78 79 static void 80 p12u_DestroyContext(p12uContext **ppCtx, PRBool removeFile) 81 { 82 if (!ppCtx || !(*ppCtx)) { 83 return; 84 } 85 86 if ((*ppCtx)->file != NULL) { 87 PR_Close((*ppCtx)->file); 88 } 89 90 if ((*ppCtx)->filename != NULL) { 91 if (removeFile) { 92 PR_Delete((*ppCtx)->filename); 93 } 94 PL_strfree((*ppCtx)->filename); 95 (*ppCtx)->filename = NULL; 96 } 97 98 PR_Free(*ppCtx); 99 *ppCtx = NULL; 100 } 101 102 static p12uContext * 103 p12u_InitContext(PRBool fileImport, char *filename) 104 { 105 p12uContext *p12cxt; 106 107 p12cxt = PORT_ZNew(p12uContext); 108 if (!p12cxt) { 109 return NULL; 110 } 111 112 p12cxt->error = PR_FALSE; 113 p12cxt->errorValue = 0; 114 p12cxt->filename = PL_strdup(filename); 115 116 if (!p12u_OpenFile(p12cxt, fileImport)) { 117 p12u_DestroyContext(&p12cxt, PR_FALSE); 118 return NULL; 119 } 120 121 return p12cxt; 122 } 123 124 SECItem * 125 P12U_NicknameCollisionCallback(SECItem *old_nick, PRBool *cancel, void *wincx) 126 { 127 char *nick = NULL; 128 SECItem *ret_nick = NULL; 129 CERTCertificate *cert = (CERTCertificate *)wincx; 130 131 if (!cancel || !cert) { 132 pk12uErrno = PK12UERR_USER_CANCELLED; 133 return NULL; 134 } 135 136 if (!old_nick) 137 fprintf(stdout, "pk12util: no nickname for cert in PKCS12 file.\n"); 138 139 #if 0 140 /* XXX not handled yet */ 141 *cancel = PR_TRUE; 142 return NULL; 143 144 #else 145 146 nick = CERT_MakeCANickname(cert); 147 if (!nick) { 148 return NULL; 149 } 150 151 if (old_nick && old_nick->data && old_nick->len && 152 PORT_Strlen(nick) == old_nick->len && 153 !PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) { 154 PORT_Free(nick); 155 PORT_SetError(SEC_ERROR_IO); 156 return NULL; 157 } 158 159 fprintf(stdout, "pk12util: using nickname: %s\n", nick); 160 ret_nick = PORT_ZNew(SECItem); 161 if (ret_nick == NULL) { 162 PORT_Free(nick); 163 return NULL; 164 } 165 166 ret_nick->data = (unsigned char *)nick; 167 ret_nick->len = PORT_Strlen(nick); 168 169 return ret_nick; 170 #endif 171 } 172 173 static SECStatus 174 p12u_SwapUnicodeBytes(SECItem *uniItem) 175 { 176 unsigned int i; 177 unsigned char a; 178 if ((uniItem == NULL) || (uniItem->len % 2)) { 179 return SECFailure; 180 } 181 for (i = 0; i < uniItem->len; i += 2) { 182 a = uniItem->data[i]; 183 uniItem->data[i] = uniItem->data[i + 1]; 184 uniItem->data[i + 1] = a; 185 } 186 return SECSuccess; 187 } 188 189 static PRBool 190 p12u_ucs2_ascii_conversion_function(PRBool toUnicode, 191 unsigned char *inBuf, 192 unsigned int inBufLen, 193 unsigned char *outBuf, 194 unsigned int maxOutBufLen, 195 unsigned int *outBufLen, 196 PRBool swapBytes) 197 { 198 SECItem it = { 0 }; 199 SECItem *dup = NULL; 200 PRBool ret; 201 202 #ifdef DEBUG_CONVERSION 203 if (pk12_debugging) { 204 int i; 205 printf("Converted from:\n"); 206 for (i = 0; i < inBufLen; i++) { 207 printf("%2x ", inBuf[i]); 208 /*if (i%60 == 0) printf("\n");*/ 209 } 210 printf("\n"); 211 } 212 #endif 213 it.data = inBuf; 214 it.len = inBufLen; 215 dup = SECITEM_DupItem(&it); 216 if (!dup) { 217 return PR_FALSE; 218 } 219 /* If converting Unicode to ASCII, swap bytes before conversion 220 * as neccessary. 221 */ 222 if (!toUnicode && swapBytes) { 223 if (p12u_SwapUnicodeBytes(dup) != SECSuccess) { 224 SECITEM_ZfreeItem(dup, PR_TRUE); 225 return PR_FALSE; 226 } 227 } 228 /* Perform the conversion. */ 229 ret = PORT_UCS2_UTF8Conversion(toUnicode, dup->data, dup->len, 230 outBuf, maxOutBufLen, outBufLen); 231 SECITEM_ZfreeItem(dup, PR_TRUE); 232 233 #ifdef DEBUG_CONVERSION 234 if (pk12_debugging) { 235 int i; 236 printf("Converted to:\n"); 237 for (i = 0; i < *outBufLen; i++) { 238 printf("%2x ", outBuf[i]); 239 /*if (i%60 == 0) printf("\n");*/ 240 } 241 printf("\n"); 242 } 243 #endif 244 return ret; 245 } 246 247 SECStatus 248 P12U_UnicodeConversion(PLArenaPool *arena, SECItem *dest, SECItem *src, 249 PRBool toUnicode, PRBool swapBytes) 250 { 251 unsigned int allocLen; 252 if (!dest || !src) { 253 return SECFailure; 254 } 255 allocLen = ((toUnicode) ? (src->len << 2) : src->len); 256 if (arena) { 257 dest->data = PORT_ArenaZAlloc(arena, allocLen); 258 } else { 259 dest->data = PORT_ZAlloc(allocLen); 260 } 261 if (PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len, 262 dest->data, allocLen, &dest->len, 263 swapBytes) == PR_FALSE) { 264 if (!arena) { 265 PORT_Free(dest->data); 266 } 267 dest->data = NULL; 268 return SECFailure; 269 } 270 return SECSuccess; 271 } 272 273 /* 274 * 275 */ 276 SECItem * 277 P12U_GetP12FilePassword(PRBool confirmPw, secuPWData *p12FilePw) 278 { 279 char *p0 = NULL; 280 SECItem *pwItem = NULL; 281 282 if (p12FilePw == NULL || p12FilePw->source == PW_NONE) { 283 char *p1 = NULL; 284 int rc; 285 for (;;) { 286 p0 = SECU_GetPasswordString(NULL, 287 "Enter password for PKCS12 file: "); 288 if (!confirmPw || p0 == NULL) 289 break; 290 p1 = SECU_GetPasswordString(NULL, "Re-enter password: "); 291 if (p1 == NULL) { 292 PORT_ZFree(p0, PL_strlen(p0)); 293 p0 = NULL; 294 break; 295 } 296 rc = PL_strcmp(p0, p1); 297 PORT_ZFree(p1, PL_strlen(p1)); 298 if (rc == 0) 299 break; 300 PORT_ZFree(p0, PL_strlen(p0)); 301 } 302 } else if (p12FilePw->source == PW_FROMFILE) { 303 p0 = SECU_FilePasswd(NULL, PR_FALSE, p12FilePw->data); 304 } else { /* Plaintext */ 305 p0 = PORT_Strdup(p12FilePw->data); 306 } 307 308 if (p0 == NULL) { 309 return NULL; 310 } 311 pwItem = SECITEM_AllocItem(NULL, NULL, PL_strlen(p0) + 1); 312 memcpy(pwItem->data, p0, pwItem->len); 313 314 PORT_ZFree(p0, PL_strlen(p0)); 315 316 return pwItem; 317 } 318 319 SECStatus 320 P12U_InitSlot(PK11SlotInfo *slot, secuPWData *slotPw) 321 { 322 SECStatus rv; 323 324 /* New databases, initialize keydb password. */ 325 if (PK11_NeedUserInit(slot)) { 326 rv = SECU_ChangePW(slot, 327 (slotPw->source == PW_PLAINTEXT) ? slotPw->data : 0, 328 (slotPw->source == PW_FROMFILE) ? slotPw->data : 0); 329 if (rv != SECSuccess) { 330 SECU_PrintError(progName, "Failed to initialize slot \"%s\"", 331 PK11_GetSlotName(slot)); 332 return SECFailure; 333 } 334 } 335 336 if (PK11_Authenticate(slot, PR_TRUE, slotPw) != SECSuccess) { 337 SECU_PrintError(progName, 338 "Failed to authenticate to PKCS11 slot"); 339 PORT_SetError(SEC_ERROR_USER_CANCELLED); 340 pk12uErrno = PK12UERR_USER_CANCELLED; 341 return SECFailure; 342 } 343 344 return SECSuccess; 345 } 346 347 /* This routine takes care of getting the PKCS12 file password, then reading and 348 * verifying the file. It returns the decoder context and a filled in password. 349 * (The password is needed by P12U_ImportPKCS12Object() to import the private 350 * key.) 351 */ 352 SEC_PKCS12DecoderContext * 353 p12U_ReadPKCS12File(SECItem *uniPwp, char *in_file, PK11SlotInfo *slot, 354 secuPWData *slotPw, secuPWData *p12FilePw, 355 PRBool ignoreIntegrity) 356 { 357 SEC_PKCS12DecoderContext *p12dcx = NULL; 358 p12uContext *p12cxt = NULL; 359 SECItem *pwitem = NULL; 360 SECItem p12file = { 0 }; 361 SECStatus rv = SECFailure; 362 PRBool swapUnicode = PR_FALSE; 363 PRBool forceUnicode = pk12uForceUnicode; 364 PRBool trypw; 365 int error; 366 367 #ifdef IS_LITTLE_ENDIAN 368 swapUnicode = PR_TRUE; 369 #endif 370 371 p12cxt = p12u_InitContext(PR_TRUE, in_file); 372 if (!p12cxt) { 373 SECU_PrintError(progName, "File Open failed: %s", in_file); 374 pk12uErrno = PK12UERR_INIT_FILE; 375 return NULL; 376 } 377 378 /* get the password */ 379 pwitem = P12U_GetP12FilePassword(PR_FALSE, p12FilePw); 380 if (!pwitem) { 381 pk12uErrno = PK12UERR_USER_CANCELLED; 382 goto done; 383 } 384 385 if (P12U_UnicodeConversion(NULL, uniPwp, pwitem, PR_TRUE, 386 swapUnicode) != SECSuccess) { 387 SECU_PrintError(progName, "Unicode conversion failed"); 388 pk12uErrno = PK12UERR_UNICODECONV; 389 goto done; 390 } 391 rv = SECU_FileToItem(&p12file, p12cxt->file); 392 if (rv != SECSuccess) { 393 SECU_PrintError(progName, "Failed to read from import file"); 394 goto done; 395 } 396 397 do { 398 trypw = PR_FALSE; /* normally we do this once */ 399 rv = SECFailure; 400 /* init the decoder context */ 401 p12dcx = SEC_PKCS12DecoderStart(uniPwp, slot, slotPw, 402 NULL, NULL, NULL, NULL, NULL); 403 if (!p12dcx) { 404 SECU_PrintError(progName, "PKCS12 decoder start failed"); 405 pk12uErrno = PK12UERR_PK12DECODESTART; 406 break; 407 } 408 409 /* decode the item */ 410 rv = SEC_PKCS12DecoderUpdate(p12dcx, p12file.data, p12file.len); 411 412 if (rv != SECSuccess) { 413 error = PR_GetError(); 414 if (error == SEC_ERROR_DECRYPTION_DISALLOWED) { 415 PR_SetError(error, 0); 416 break; 417 } 418 SECU_PrintError(progName, "PKCS12 decoding failed"); 419 pk12uErrno = PK12UERR_DECODE; 420 } 421 422 /* does the blob authenticate properly? */ 423 rv = SEC_PKCS12DecoderVerify(p12dcx); 424 if (rv != SECSuccess) { 425 if (uniPwp->len == 2) { 426 /* this is a null PW, try once more with a zero-length PW 427 instead of a null string */ 428 SEC_PKCS12DecoderFinish(p12dcx); 429 uniPwp->len = 0; 430 trypw = PR_TRUE; 431 } else if (forceUnicode == pk12uForceUnicode) { 432 /* try again with a different password encoding */ 433 forceUnicode = !pk12uForceUnicode; 434 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, 435 forceUnicode); 436 if (rv != SECSuccess) { 437 SECU_PrintError(progName, "PKCS12 decoding failed to set option"); 438 pk12uErrno = PK12UERR_DECODEVERIFY; 439 break; 440 } 441 SEC_PKCS12DecoderFinish(p12dcx); 442 trypw = PR_TRUE; 443 } else { 444 SECU_PrintError(progName, "PKCS12 decode not verified"); 445 pk12uErrno = PK12UERR_DECODEVERIFY; 446 break; 447 } 448 } 449 } while (trypw == PR_TRUE); 450 451 /* revert the option setting */ 452 if (forceUnicode != pk12uForceUnicode) { 453 if (SECSuccess != NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode)) { 454 SECU_PrintError(progName, "PKCS12 decoding failed to set option"); 455 pk12uErrno = PK12UERR_DECODEVERIFY; 456 rv = SECFailure; 457 } 458 } 459 /* rv has been set at this point */ 460 461 done: 462 /* if we are ignoring Integrity and we failed because we couldn't 463 * verify the integrity code, go ahead and succeed */ 464 if (rv != SECSuccess && !(ignoreIntegrity && 465 (pk12uErrno == PK12UERR_DECODEVERIFY))) { 466 if (p12dcx != NULL) { 467 SEC_PKCS12DecoderFinish(p12dcx); 468 p12dcx = NULL; 469 } 470 if (uniPwp->data) { 471 SECITEM_ZfreeItem(uniPwp, PR_FALSE); 472 uniPwp->data = NULL; 473 } 474 } 475 PR_Close(p12cxt->file); 476 p12cxt->file = NULL; 477 /* PK11_FreeSlot(slot); */ 478 p12u_DestroyContext(&p12cxt, PR_FALSE); 479 480 if (pwitem) { 481 SECITEM_ZfreeItem(pwitem, PR_TRUE); 482 } 483 SECITEM_ZfreeItem(&p12file, PR_FALSE); 484 return p12dcx; 485 } 486 487 /* 488 * given a filename for pkcs12 file, imports certs and keys 489 * 490 * Change: altitude 491 * I've changed this function so that it takes the keydb and pkcs12 file 492 * passwords from files. The "pwdKeyDB" and "pwdP12File" 493 * variables have been added for this purpose. 494 */ 495 PRIntn 496 P12U_ImportPKCS12Object(char *in_file, PK11SlotInfo *slot, 497 secuPWData *slotPw, secuPWData *p12FilePw, 498 PRBool ignoreIntegrity) 499 { 500 SEC_PKCS12DecoderContext *p12dcx = NULL; 501 SECItem uniPwitem = { 0 }; 502 PRBool forceUnicode = pk12uForceUnicode; 503 PRBool trypw; 504 SECStatus rv = SECFailure; 505 506 rv = P12U_InitSlot(slot, slotPw); 507 if (rv != SECSuccess) { 508 SECU_PrintError(progName, "Failed to authenticate to \"%s\"", 509 PK11_GetSlotName(slot)); 510 pk12uErrno = PK12UERR_PK11GETSLOT; 511 return rv; 512 } 513 514 do { 515 trypw = PR_FALSE; /* normally we do this once */ 516 rv = SECFailure; 517 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, 518 p12FilePw, ignoreIntegrity); 519 520 if (p12dcx == NULL) { 521 goto loser; 522 } 523 524 /* make sure the bags are okey dokey -- nicknames correct, etc. */ 525 rv = SEC_PKCS12DecoderValidateBags(p12dcx, P12U_NicknameCollisionCallback); 526 if (rv != SECSuccess) { 527 if (PORT_GetError() == SEC_ERROR_PKCS12_DUPLICATE_DATA) { 528 pk12uErrno = PK12UERR_CERTALREADYEXISTS; 529 } else { 530 pk12uErrno = PK12UERR_DECODEVALIBAGS; 531 } 532 SECU_PrintError(progName, "PKCS12 decode validate bags failed"); 533 goto loser; 534 } 535 536 /* stuff 'em in */ 537 if (forceUnicode != pk12uForceUnicode) { 538 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, 539 forceUnicode); 540 if (rv != SECSuccess) { 541 SECU_PrintError(progName, "PKCS12 decode set option failed"); 542 pk12uErrno = PK12UERR_DECODEIMPTBAGS; 543 goto loser; 544 } 545 } 546 rv = SEC_PKCS12DecoderImportBags(p12dcx); 547 if (rv != SECSuccess) { 548 if (PR_GetError() == SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY && 549 forceUnicode == pk12uForceUnicode) { 550 /* try again with a different password encoding */ 551 forceUnicode = !pk12uForceUnicode; 552 SEC_PKCS12DecoderFinish(p12dcx); 553 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); 554 trypw = PR_TRUE; 555 } else { 556 SECU_PrintError(progName, "PKCS12 decode import bags failed"); 557 pk12uErrno = PK12UERR_DECODEIMPTBAGS; 558 goto loser; 559 } 560 } 561 } while (trypw); 562 563 /* revert the option setting */ 564 if (forceUnicode != pk12uForceUnicode) { 565 rv = NSS_OptionSet(__NSS_PKCS12_DECODE_FORCE_UNICODE, pk12uForceUnicode); 566 if (rv != SECSuccess) { 567 SECU_PrintError(progName, "PKCS12 decode set option failed"); 568 pk12uErrno = PK12UERR_DECODEIMPTBAGS; 569 goto loser; 570 } 571 } 572 573 fprintf(stdout, "%s: PKCS12 IMPORT SUCCESSFUL\n", progName); 574 rv = SECSuccess; 575 576 loser: 577 if (p12dcx) { 578 SEC_PKCS12DecoderFinish(p12dcx); 579 } 580 581 if (uniPwitem.data) { 582 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); 583 } 584 585 return rv; 586 } 587 588 static void 589 p12u_DoPKCS12ExportErrors() 590 { 591 PRErrorCode error_value; 592 593 error_value = PORT_GetError(); 594 if ((error_value == SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY) || 595 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME) || 596 (error_value == SEC_ERROR_PKCS12_UNABLE_TO_WRITE)) { 597 fputs(SECU_Strerror(error_value), stderr); 598 } else if (error_value == SEC_ERROR_USER_CANCELLED) { 599 ; 600 } else { 601 fputs(SECU_Strerror(SEC_ERROR_EXPORTING_CERTIFICATES), stderr); 602 } 603 } 604 605 static void 606 p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len) 607 { 608 p12uContext *p12cxt = arg; 609 int writeLen; 610 611 if (!p12cxt || (p12cxt->error == PR_TRUE)) { 612 return; 613 } 614 615 if (p12cxt->file == NULL) { 616 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE; 617 p12cxt->error = PR_TRUE; 618 return; 619 } 620 621 writeLen = PR_Write(p12cxt->file, (unsigned char *)buf, (PRInt32)len); 622 623 if (writeLen != (int)len) { 624 PR_Close(p12cxt->file); 625 PL_strfree(p12cxt->filename); 626 p12cxt->filename = NULL; 627 p12cxt->file = NULL; 628 p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE; 629 p12cxt->error = PR_TRUE; 630 } 631 } 632 633 void 634 P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot, 635 SECOidTag cipher, SECOidTag certCipher, SECOidTag hash, 636 secuPWData *slotPw, secuPWData *p12FilePw) 637 { 638 SEC_PKCS12ExportContext *p12ecx = NULL; 639 SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL; 640 SECItem *pwitem = NULL; 641 p12uContext *p12cxt = NULL; 642 CERTCertList *certlist = NULL; 643 CERTCertListNode *node = NULL; 644 PK11SlotInfo *slot = NULL; 645 646 if (P12U_InitSlot(inSlot, slotPw) != SECSuccess) { 647 SECU_PrintError(progName, "Failed to authenticate to \"%s\"", 648 PK11_GetSlotName(inSlot)); 649 pk12uErrno = PK12UERR_PK11GETSLOT; 650 goto loser; 651 } 652 certlist = PK11_FindCertsFromNickname(nn, slotPw); 653 if (!certlist) { 654 PORT_SetError(SEC_ERROR_UNKNOWN_CERT); 655 SECU_PrintError(progName, "find user certs from nickname failed"); 656 pk12uErrno = PK12UERR_FINDCERTBYNN; 657 return; 658 } 659 660 if ((SECSuccess != CERT_FilterCertListForUserCerts(certlist)) || 661 CERT_LIST_EMPTY(certlist)) { 662 PR_fprintf(PR_STDERR, "%s: no user certs from given nickname\n", 663 progName); 664 pk12uErrno = PK12UERR_FINDCERTBYNN; 665 goto loser; 666 } 667 668 /* Password to use for PKCS12 file. */ 669 pwitem = P12U_GetP12FilePassword(PR_TRUE, p12FilePw); 670 if (!pwitem) { 671 goto loser; 672 } 673 674 /* we are passing UTF8, drop the NULL in the normal password value. 675 * UCS2 conversion will add it back if necessary. This only affects 676 * password > Blocksize of the Hash function and pkcs5v2 pbe (if password 677 * <=Blocksize then the password is zero padded anyway, so an extra NULL 678 * at the end has not effect). This is allows us to work with openssl and 679 * gnutls. Older versions of NSS already fail to decrypt long passwords 680 * in this case, so we aren't breaking anyone with this code */ 681 if ((pwitem->len > 0) && (!pwitem->data[pwitem->len - 1])) { 682 pwitem->len--; 683 } 684 685 p12cxt = p12u_InitContext(PR_FALSE, outfile); 686 if (!p12cxt) { 687 SECU_PrintError(progName, "Initialization failed: %s", outfile); 688 pk12uErrno = PK12UERR_INIT_FILE; 689 goto loser; 690 } 691 692 if (certlist) { 693 CERTCertificate *cert = CERT_LIST_HEAD(certlist)->cert; 694 if (cert) { 695 slot = cert->slot; /* use the slot from the first matching 696 certificate to create the context . This is for keygen */ 697 } 698 } 699 if (!slot) { 700 SECU_PrintError(progName, "cert does not have a slot"); 701 pk12uErrno = PK12UERR_FINDCERTBYNN; 702 goto loser; 703 } 704 p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL, slot, slotPw); 705 if (!p12ecx) { 706 SECU_PrintError(progName, "export context creation failed"); 707 pk12uErrno = PK12UERR_EXPORTCXCREATE; 708 goto loser; 709 } 710 711 if (SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, hash) != 712 SECSuccess) { 713 SECU_PrintError(progName, "PKCS12 add password integrity failed"); 714 pk12uErrno = PK12UERR_PK12ADDPWDINTEG; 715 goto loser; 716 } 717 718 for (node = CERT_LIST_HEAD(certlist); 719 !CERT_LIST_END(node, certlist); 720 node = CERT_LIST_NEXT(node)) { 721 CERTCertificate *cert = node->cert; 722 if (!cert->slot) { 723 SECU_PrintError(progName, "cert does not have a slot"); 724 pk12uErrno = PK12UERR_FINDCERTBYNN; 725 goto loser; 726 } 727 728 keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); 729 if (certCipher == SEC_OID_UNKNOWN) { 730 certSafe = keySafe; 731 } else { 732 certSafe = 733 SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certCipher); 734 } 735 736 if (!certSafe || !keySafe) { 737 SECU_PrintError(progName, "key or cert safe creation failed"); 738 pk12uErrno = PK12UERR_CERTKEYSAFE; 739 goto loser; 740 } 741 742 if (SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, 743 CERT_GetDefaultCertDB(), keySafe, NULL, 744 PR_TRUE, pwitem, cipher) != SECSuccess) { 745 SECU_PrintError(progName, "add cert and key failed"); 746 pk12uErrno = PK12UERR_ADDCERTKEY; 747 goto loser; 748 } 749 } 750 751 CERT_DestroyCertList(certlist); 752 certlist = NULL; 753 754 if (SEC_PKCS12Encode(p12ecx, p12u_WriteToExportFile, p12cxt) != 755 SECSuccess) { 756 SECU_PrintError(progName, "PKCS12 encode failed"); 757 pk12uErrno = PK12UERR_ENCODE; 758 goto loser; 759 } 760 761 p12u_DestroyContext(&p12cxt, PR_FALSE); 762 SECITEM_ZfreeItem(pwitem, PR_TRUE); 763 fprintf(stdout, "%s: PKCS12 EXPORT SUCCESSFUL\n", progName); 764 SEC_PKCS12DestroyExportContext(p12ecx); 765 766 return; 767 768 loser: 769 SEC_PKCS12DestroyExportContext(p12ecx); 770 771 if (certlist) { 772 CERT_DestroyCertList(certlist); 773 certlist = NULL; 774 } 775 776 p12u_DestroyContext(&p12cxt, PR_TRUE); 777 if (pwitem) { 778 SECITEM_ZfreeItem(pwitem, PR_TRUE); 779 } 780 p12u_DoPKCS12ExportErrors(); 781 return; 782 } 783 784 PRIntn 785 P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot, 786 secuPWData *slotPw, secuPWData *p12FilePw, 787 PRBool ignoreIntegrity) 788 { 789 SEC_PKCS12DecoderContext *p12dcx = NULL; 790 SECItem uniPwitem = { 0 }; 791 SECStatus rv = SECFailure; 792 const SEC_PKCS12DecoderItem *dip; 793 794 p12dcx = p12U_ReadPKCS12File(&uniPwitem, in_file, slot, slotPw, p12FilePw, 795 ignoreIntegrity); 796 /* did the blob authenticate properly? */ 797 if (p12dcx == NULL) { 798 SECU_PrintError(progName, "PKCS12 decode not verified"); 799 pk12uErrno = PK12UERR_DECODEVERIFY; 800 goto loser; 801 } 802 rv = SEC_PKCS12DecoderIterateInit(p12dcx); 803 if (rv != SECSuccess) { 804 SECU_PrintError(progName, "PKCS12 decode iterate bags failed"); 805 pk12uErrno = PK12UERR_DECODEIMPTBAGS; 806 rv = SECFailure; 807 } else { 808 int fileCounter = 0; 809 while (SEC_PKCS12DecoderIterateNext(p12dcx, &dip) == SECSuccess) { 810 switch (dip->type) { 811 case SEC_OID_PKCS12_V1_CERT_BAG_ID: 812 printf("Certificate"); 813 if (dumpRawFile) { 814 PRFileDesc *fd; 815 char fileName[20]; 816 snprintf(fileName, sizeof(fileName), "file%04d.der", ++fileCounter); 817 fd = PR_Open(fileName, 818 PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 819 0600); 820 if (!fd) { 821 SECU_PrintError(progName, 822 "Cannot create output file"); 823 } else { 824 PR_Write(fd, dip->der->data, dip->der->len); 825 PR_Close(fd); 826 } 827 } else if (SECU_PrintSignedData(stdout, dip->der, 828 (dip->hasKey) ? "(has private key)" 829 : "", 830 0, SECU_PrintCertificate) != 831 0) { 832 SECU_PrintError(progName, "PKCS12 print cert bag failed"); 833 } 834 if (dip->friendlyName != NULL) { 835 printf(" Friendly Name: %s\n\n", 836 dip->friendlyName->data); 837 } 838 if (dip->shroudAlg) { 839 SECU_PrintAlgorithmID(stdout, dip->shroudAlg, 840 "Encryption algorithm", 1); 841 } 842 break; 843 case SEC_OID_PKCS12_V1_KEY_BAG_ID: 844 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: 845 printf("Key"); 846 if (dip->type == SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID) 847 printf("(shrouded)"); 848 printf(":\n"); 849 if (dip->friendlyName != NULL) { 850 printf(" Friendly Name: %s\n\n", 851 dip->friendlyName->data); 852 } 853 if (dip->shroudAlg) { 854 SECU_PrintAlgorithmID(stdout, dip->shroudAlg, 855 "Encryption algorithm", 1); 856 } 857 break; 858 default: 859 printf("unknown bag type(%d): %s\n\n", dip->type, 860 SECOID_FindOIDTagDescription(dip->type)); 861 break; 862 } 863 } 864 rv = SECSuccess; 865 } 866 867 loser: 868 869 if (p12dcx) { 870 SEC_PKCS12DecoderFinish(p12dcx); 871 } 872 873 if (uniPwitem.data) { 874 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); 875 } 876 877 return rv; 878 } 879 880 SECOidTag 881 PKCS12U_FindTagFromString(char *cipherString) 882 { 883 return SECOID_FindOIDTagFromDescripton(cipherString, (size_t)-1, PR_TRUE); 884 } 885 886 /* 887 * use the oid table description to map a user input string to a particular 888 * oid. 889 */ 890 SECOidTag 891 PKCS12U_MapCipherFromString(char *cipherString, int keyLen) 892 { 893 SECOidTag tag; 894 SECOidTag cipher; 895 896 /* future enhancement: provide 'friendlier' typed in names for 897 * pbe mechanisms. 898 */ 899 900 /* look for the oid tag by Description */ 901 tag = PKCS12U_FindTagFromString(cipherString); 902 if (tag == SEC_OID_UNKNOWN) { 903 return tag; 904 } 905 906 cipher = SEC_OID_UNKNOWN; 907 /* we found a match... get the PBE version of this 908 * cipher... */ 909 if (!SEC_PKCS5IsAlgorithmPBEAlgTag(tag)) { 910 cipher = SEC_PKCS5GetPBEAlgorithm(tag, keyLen); 911 /* no eqivalent PKCS5/PKCS12 cipher, use the raw 912 * encryption tag we got and pass it directly in, 913 * pkcs12 will use the pkcsv5 mechanism */ 914 if (cipher == SEC_OID_PKCS5_PBES2) { 915 cipher = tag; 916 } else if (cipher == SEC_OID_PKCS5_PBMAC1) { 917 /* make sure we have not macing ciphers here */ 918 cipher = SEC_OID_UNKNOWN; 919 } 920 } else { 921 cipher = tag; 922 } 923 return cipher; 924 } 925 926 SECOidTag 927 PKCS12U_MapHashFromString(char *hashString) 928 { 929 SECOidTag hashAlg; 930 931 /* look for the oid tag by Description */ 932 hashAlg = PKCS12U_FindTagFromString(hashString); 933 if (hashAlg == SEC_OID_UNKNOWN) { 934 return hashAlg; 935 } 936 /* make sure it's a hashing oid */ 937 if (HASH_GetHashTypeByOidTag(hashAlg) == HASH_AlgNULL) { 938 /* allow HMAC here. HMAC implies PKCS 5 v2 pba */ 939 SECOidTag baseHashAlg = HASH_GetHashOidTagByHMACOidTag(hashAlg); 940 if (baseHashAlg == SEC_OID_UNKNOWN) { 941 /* not an hmac either, reject the entry */ 942 return SEC_OID_UNKNOWN; 943 } 944 } 945 return hashAlg; 946 } 947 948 static PRUintn 949 P12U_Init(char *dir, char *dbprefix, PRBool listonly) 950 { 951 SECStatus rv; 952 PK11_SetPasswordFunc(SECU_GetModulePassword); 953 954 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 955 if (listonly && NSS_NoDB_Init("") == SECSuccess) { 956 rv = SECSuccess; 957 } else { 958 rv = NSS_Initialize(dir, dbprefix, dbprefix, "secmod.db", 0); 959 } 960 if (rv != SECSuccess) { 961 SECU_PrintPRandOSError(progName); 962 exit(-1); 963 } 964 965 /* setup unicode callback functions */ 966 PORT_SetUCS2_ASCIIConversionFunction(p12u_ucs2_ascii_conversion_function); 967 /* use the defaults for UCS4-UTF8 and UCS2-UTF8 */ 968 969 /* ciphers are already enabled by default, allow policy to work */ 970 /* p12u_EnableAllCiphers(); */ 971 972 return 0; 973 } 974 975 enum { 976 opt_CertDir = 0, 977 opt_TokenName, 978 opt_Import, 979 opt_SlotPWFile, 980 opt_SlotPW, 981 opt_List, 982 opt_Nickname, 983 opt_Export, 984 opt_Raw, 985 opt_P12FilePWFile, 986 opt_P12FilePW, 987 opt_DBPrefix, 988 opt_Debug, 989 opt_Cipher, 990 opt_CertCipher, 991 opt_KeyLength, 992 opt_CertKeyLength, 993 opt_Mac, 994 opt_IgnoreIntegrity 995 }; 996 997 static secuCommandFlag pk12util_options[] = { 998 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, 999 { /* opt_TokenName */ 'h', PR_TRUE, 0, PR_FALSE }, 1000 { /* opt_Import */ 'i', PR_TRUE, 0, PR_FALSE }, 1001 { /* opt_SlotPWFile */ 'k', PR_TRUE, 0, PR_FALSE }, 1002 { /* opt_SlotPW */ 'K', PR_TRUE, 0, PR_FALSE }, 1003 { /* opt_List */ 'l', PR_TRUE, 0, PR_FALSE }, 1004 { /* opt_Nickname */ 'n', PR_TRUE, 0, PR_FALSE }, 1005 { /* opt_Export */ 'o', PR_TRUE, 0, PR_FALSE }, 1006 { /* opt_Raw */ 'r', PR_FALSE, 0, PR_FALSE }, 1007 { /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE }, 1008 { /* opt_P12FilePW */ 'W', PR_TRUE, 0, PR_FALSE }, 1009 { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE }, 1010 { /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE }, 1011 { /* opt_Cipher */ 'c', PR_TRUE, 0, PR_FALSE }, 1012 { /* opt_CertCipher */ 'C', PR_TRUE, 0, PR_FALSE }, 1013 { /* opt_KeyLength */ 'm', PR_TRUE, 0, PR_FALSE, "key_len" }, 1014 { /* opt_CertKeyLength */ 0, PR_TRUE, 0, PR_FALSE, "cert_key_len" }, 1015 { /* opt_Mac */ 'M', PR_TRUE, 0, PR_FALSE }, 1016 { /* opt_IgnoreIntegrity */ 'I', PR_FALSE, 0, PR_FALSE } 1017 }; 1018 1019 int 1020 main(int argc, char **argv) 1021 { 1022 secuPWData slotPw = { PW_NONE, NULL }; 1023 secuPWData p12FilePw = { PW_NONE, NULL }; 1024 PK11SlotInfo *slot; 1025 char *slotname = NULL; 1026 char *import_file = NULL; 1027 char *export_file = NULL; 1028 char *dbprefix = ""; 1029 SECStatus rv; 1030 SECOidTag cipher = SEC_OID_AES_256_CBC; 1031 SECOidTag hash = SEC_OID_SHA256; 1032 SECOidTag certCipher = SEC_OID_AES_128_CBC; 1033 int keyLen = 0; 1034 int certKeyLen = 0; 1035 secuCommand pk12util; 1036 PRInt32 forceUnicode; 1037 PRBool ignoreIntegrity = PR_FALSE; 1038 1039 #ifdef _CRTDBG_MAP_ALLOC 1040 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 1041 #endif 1042 1043 pk12util.numCommands = 0; 1044 pk12util.commands = 0; 1045 pk12util.numOptions = sizeof(pk12util_options) / sizeof(secuCommandFlag); 1046 pk12util.options = pk12util_options; 1047 1048 progName = strrchr(argv[0], '/'); 1049 progName = progName ? progName + 1 : argv[0]; 1050 1051 rv = SECU_ParseCommandLine(argc, argv, progName, &pk12util); 1052 1053 if (rv != SECSuccess) 1054 Usage(); 1055 1056 pk12_debugging = pk12util.options[opt_Debug].activated; 1057 1058 if ((pk12util.options[opt_Import].activated + 1059 pk12util.options[opt_Export].activated + 1060 pk12util.options[opt_List].activated) != 1) { 1061 Usage(); 1062 } 1063 1064 if (pk12util.options[opt_Export].activated && 1065 !pk12util.options[opt_Nickname].activated) { 1066 Usage(); 1067 } 1068 1069 rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode); 1070 if (rv != SECSuccess) { 1071 SECU_PrintError(progName, 1072 "Failed to get NSS_PKCS12_DECODE_FORCE_UNICODE option"); 1073 Usage(); 1074 } 1075 pk12uForceUnicode = forceUnicode; 1076 1077 slotname = SECU_GetOptionArg(&pk12util, opt_TokenName); 1078 1079 import_file = (pk12util.options[opt_List].activated) ? SECU_GetOptionArg(&pk12util, opt_List) 1080 : SECU_GetOptionArg(&pk12util, opt_Import); 1081 export_file = SECU_GetOptionArg(&pk12util, opt_Export); 1082 1083 if (pk12util.options[opt_P12FilePWFile].activated) { 1084 p12FilePw.source = PW_FROMFILE; 1085 p12FilePw.data = PORT_Strdup(pk12util.options[opt_P12FilePWFile].arg); 1086 } 1087 1088 if (pk12util.options[opt_P12FilePW].activated) { 1089 p12FilePw.source = PW_PLAINTEXT; 1090 p12FilePw.data = PORT_Strdup(pk12util.options[opt_P12FilePW].arg); 1091 } 1092 1093 if (pk12util.options[opt_SlotPWFile].activated) { 1094 slotPw.source = PW_FROMFILE; 1095 slotPw.data = PORT_Strdup(pk12util.options[opt_SlotPWFile].arg); 1096 } 1097 1098 if (pk12util.options[opt_SlotPW].activated) { 1099 slotPw.source = PW_PLAINTEXT; 1100 slotPw.data = PORT_Strdup(pk12util.options[opt_SlotPW].arg); 1101 } 1102 1103 if (pk12util.options[opt_CertDir].activated) { 1104 SECU_ConfigDirectory(pk12util.options[opt_CertDir].arg); 1105 } 1106 if (pk12util.options[opt_DBPrefix].activated) { 1107 dbprefix = pk12util.options[opt_DBPrefix].arg; 1108 } 1109 if (pk12util.options[opt_Raw].activated) { 1110 dumpRawFile = PR_TRUE; 1111 } 1112 if (pk12util.options[opt_IgnoreIntegrity].activated) { 1113 ignoreIntegrity = PR_TRUE; 1114 } 1115 if (pk12util.options[opt_KeyLength].activated) { 1116 keyLen = atoi(pk12util.options[opt_KeyLength].arg); 1117 } 1118 if (pk12util.options[opt_CertKeyLength].activated) { 1119 certKeyLen = atoi(pk12util.options[opt_CertKeyLength].arg); 1120 } 1121 1122 P12U_Init(SECU_ConfigDirectory(NULL), dbprefix, 1123 pk12util.options[opt_List].activated); 1124 1125 if (!slotname || PL_strcmp(slotname, "internal") == 0) 1126 slot = PK11_GetInternalKeySlot(); 1127 else 1128 slot = PK11_FindSlotByName(slotname); 1129 1130 if (!slot) { 1131 SECU_PrintError(progName, "Invalid slot \"%s\"", slotname); 1132 pk12uErrno = PK12UERR_PK11GETSLOT; 1133 goto done; 1134 } 1135 1136 if (pk12util.options[opt_Cipher].activated) { 1137 char *cipherString = pk12util.options[opt_Cipher].arg; 1138 1139 cipher = PKCS12U_MapCipherFromString(cipherString, keyLen); 1140 /* We only want encryption PBE's. make sure we don't have 1141 * any MAC pbes */ 1142 if (cipher == SEC_OID_UNKNOWN) { 1143 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 1144 SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString); 1145 pk12uErrno = PK12UERR_INVALIDALGORITHM; 1146 goto done; 1147 } 1148 } 1149 1150 if (pk12util.options[opt_CertCipher].activated) { 1151 char *cipherString = pk12util.options[opt_CertCipher].arg; 1152 1153 if (PORT_Strcasecmp(cipherString, "none") == 0) { 1154 certCipher = SEC_OID_UNKNOWN; 1155 } else { 1156 certCipher = PKCS12U_MapCipherFromString(cipherString, certKeyLen); 1157 /* If the user requested a cipher and we didn't find it, then 1158 * don't just silently not encrypt. */ 1159 if (certCipher == SEC_OID_UNKNOWN) { 1160 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 1161 SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString); 1162 pk12uErrno = PK12UERR_INVALIDALGORITHM; 1163 goto done; 1164 } 1165 } 1166 } 1167 /* in FIPS mode default to encoding with pkcs5v2 for the MAC */ 1168 if (PK11_IsFIPS()) { 1169 hash = SEC_OID_HMAC_SHA256; 1170 } 1171 if (pk12util.options[opt_Mac].activated) { 1172 char *hashString = pk12util.options[opt_Mac].arg; 1173 1174 hash = PKCS12U_MapHashFromString(hashString); 1175 /* We don't support creating Mac-less pkcs 12 files */ 1176 if (hash == SEC_OID_UNKNOWN) { 1177 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 1178 SECU_PrintError(progName, "Algorithm: \"%s\"", hashString); 1179 pk12uErrno = PK12UERR_INVALIDALGORITHM; 1180 goto done; 1181 } 1182 } 1183 1184 if (pk12util.options[opt_Import].activated) { 1185 P12U_ImportPKCS12Object(import_file, slot, &slotPw, &p12FilePw, 1186 ignoreIntegrity); 1187 1188 } else if (pk12util.options[opt_Export].activated) { 1189 P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg, 1190 export_file, slot, cipher, certCipher, 1191 hash, &slotPw, &p12FilePw); 1192 1193 } else if (pk12util.options[opt_List].activated) { 1194 P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw, 1195 ignoreIntegrity); 1196 1197 } else { 1198 Usage(); 1199 pk12uErrno = PK12UERR_USAGE; 1200 } 1201 1202 done: 1203 if (import_file != NULL) 1204 PORT_ZFree(import_file, PL_strlen(import_file)); 1205 if (export_file != NULL) 1206 PORT_ZFree(export_file, PL_strlen(export_file)); 1207 if (slotPw.data != NULL) 1208 PORT_ZFree(slotPw.data, PL_strlen(slotPw.data)); 1209 if (p12FilePw.data != NULL) 1210 PORT_ZFree(p12FilePw.data, PL_strlen(p12FilePw.data)); 1211 if (slot) 1212 PK11_FreeSlot(slot); 1213 if (NSS_Shutdown() != SECSuccess) { 1214 pk12uErrno = 1; 1215 } 1216 PL_ArenaFinish(); 1217 PR_Cleanup(); 1218 return pk12uErrno; 1219 }