diff options
author | nelson%bolyard.com <devnull@localhost> | 2008-02-14 03:04:33 +0000 |
---|---|---|
committer | nelson%bolyard.com <devnull@localhost> | 2008-02-14 03:04:33 +0000 |
commit | 9c948952a5b74fdaff1b5c66400f8a1cbb5b2504 (patch) | |
tree | c0f9b4b82df603756228b44eed39f83061202774 | |
parent | 63ed29afa59eacf4b953a385459ab1847944cbcc (diff) | |
download | nss-hg-9c948952a5b74fdaff1b5c66400f8a1cbb5b2504.tar.gz |
Bug 401928 - Support generalized PKCS#5 v2 PBEs. r=rrelyea
-rw-r--r-- | security/nss/cmd/lib/secutil.c | 150 | ||||
-rw-r--r-- | security/nss/cmd/lib/secutil.h | 2 | ||||
-rw-r--r-- | security/nss/cmd/pk12util/pk12util.c | 134 | ||||
-rw-r--r-- | security/nss/cmd/pk12util/pk12util.h | 1 |
4 files changed, 275 insertions, 12 deletions
diff --git a/security/nss/cmd/lib/secutil.c b/security/nss/cmd/lib/secutil.c index f59d99a9c..415cd35e2 100644 --- a/security/nss/cmd/lib/secutil.c +++ b/security/nss/cmd/lib/secutil.c @@ -51,6 +51,7 @@ #include "cryptohi.h" #include "secutil.h" #include "secpkcs7.h" +#include "secpkcs5.h" #include <stdarg.h> #if !defined(_WIN32_WCE) #include <sys/stat.h> @@ -1431,13 +1432,160 @@ SECU_PrintObjectID(FILE *out, SECItem *oid, char *m, int level) return SEC_OID_UNKNOWN; } +typedef struct secuPBEParamsStr { + SECItem salt; + SECItem iterationCount; + SECItem keyLength; + SECAlgorithmID cipherAlg; + SECAlgorithmID kdfAlg; +} secuPBEParams; + +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate); + +/* SECOID_PKCS5_PBKDF2 */ +const SEC_ASN1Template secuKDF2Params[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, + { SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) }, + { SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) }, + { SEC_ASN1_INTEGER, offsetof(secuPBEParams, keyLength) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { 0 } +}; + +/* PKCS5v1 & PKCS12 */ +const SEC_ASN1Template secuPBEParamsTemp[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams) }, + { SEC_ASN1_OCTET_STRING, offsetof(secuPBEParams, salt) }, + { SEC_ASN1_INTEGER, offsetof(secuPBEParams, iterationCount) }, + { 0 } +}; + +/* SEC_OID_PKCS5_PBES2, SEC_OID_PKCS5_PBMAC1 */ +const SEC_ASN1Template secuPBEV2Params[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(secuPBEParams)}, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, kdfAlg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(secuPBEParams, cipherAlg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { 0 } +}; + +void +secu_PrintKDF2Params(FILE *out, SECItem *value, char *m, int level) +{ + PRArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + SECStatus rv; + secuPBEParams param; + + if (m) { + SECU_Indent(out, level); + fprintf (out, "%s:\n", m); + } + + if (!pool) { + SECU_Indent(out, level); + fprintf(out, "Out of memory\n"); + return; + } + + PORT_Memset(¶m, 0, sizeof param); + rv = SEC_QuickDERDecodeItem(pool, ¶m, secuKDF2Params, value); + if (rv == SECSuccess) { + SECU_PrintAsHex(out, ¶m.salt, "Salt", level+1); + SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count", + level+1); + SECU_PrintInteger(out, ¶m.keyLength, "Key Length", level+1); + SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF algorithm", level+1); + } + PORT_FreeArena(pool, PR_FALSE); +} + +void +secu_PrintPKCS5V2Params(FILE *out, SECItem *value, char *m, int level) +{ + PRArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + SECStatus rv; + secuPBEParams param; + + if (m) { + SECU_Indent(out, level); + fprintf (out, "%s:\n", m); + } + + if (!pool) { + SECU_Indent(out, level); + fprintf(out, "Out of memory\n"); + return; + } + + PORT_Memset(¶m, 0, sizeof param); + rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEV2Params, value); + if (rv == SECSuccess) { + SECU_PrintAlgorithmID(out, ¶m.kdfAlg, "KDF", level+1); + SECU_PrintAlgorithmID(out, ¶m.cipherAlg, "Cipher", level+1); + } + PORT_FreeArena(pool, PR_FALSE); +} + +void +secu_PrintPBEParams(FILE *out, SECItem *value, char *m, int level) +{ + PRArenaPool *pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + SECStatus rv; + secuPBEParams param; + + if (m) { + SECU_Indent(out, level); + fprintf (out, "%s:\n", m); + } + + if (!pool) { + SECU_Indent(out, level); + fprintf(out, "Out of memory\n"); + return; + } + + PORT_Memset(¶m, 0, sizeof(secuPBEParams)); + rv = SEC_QuickDERDecodeItem(pool, ¶m, secuPBEParamsTemp, value); + if (rv == SECSuccess) { + SECU_PrintAsHex(out, ¶m.salt, "Salt", level+1); + SECU_PrintInteger(out, ¶m.iterationCount, "Iteration Count", + level+1); + } + PORT_FreeArena(pool, PR_FALSE); +} /* This function does NOT expect a DER type and length. */ void SECU_PrintAlgorithmID(FILE *out, SECAlgorithmID *a, char *m, int level) { + SECOidTag algtag; SECU_PrintObjectID(out, &a->algorithm, m, level); + algtag = SECOID_GetAlgorithmTag(a); + if (SEC_PKCS5IsAlgorithmPBEAlgTag(algtag)) { + switch (algtag) { + case SEC_OID_PKCS5_PBKDF2: + secu_PrintKDF2Params(out, &a->parameters, "Parameters", level+1); + break; + case SEC_OID_PKCS5_PBES2: + secu_PrintPKCS5V2Params(out, &a->parameters, "Encryption", level+1); + break; + case SEC_OID_PKCS5_PBMAC1: + secu_PrintPKCS5V2Params(out, &a->parameters, "MAC", level+1); + break; + default: + secu_PrintPBEParams(out, &a->parameters, "Parameters", level+1); + break; + } + return; + } + + if (a->parameters.len == 0 || (a->parameters.len == 2 && PORT_Memcmp(a->parameters.data, "\005\000", 2) == 0)) { @@ -3552,7 +3700,7 @@ SECU_StringToSignatureAlgTag(const char *alg) SECStatus SECU_StoreCRL(PK11SlotInfo *slot, SECItem *derCrl, PRFileDesc *outFile, - const PRBool ascii, char *url) + PRBool ascii, char *url) { PORT_Assert(derCrl != NULL); if (!derCrl) { diff --git a/security/nss/cmd/lib/secutil.h b/security/nss/cmd/lib/secutil.h index 7eaae39bc..e81eed709 100644 --- a/security/nss/cmd/lib/secutil.h +++ b/security/nss/cmd/lib/secutil.h @@ -339,7 +339,7 @@ extern SECOidTag SECU_StringToSignatureAlgTag(const char *alg); * encodes with base64 and exports to file if ascii flag is set * and file is not NULL. */ extern SECStatus SECU_StoreCRL(PK11SlotInfo *slot, SECItem *derCrl, - PRFileDesc *outFile, int ascii, char *url); + PRFileDesc *outFile, PRBool ascii, char *url); /* diff --git a/security/nss/cmd/pk12util/pk12util.c b/security/nss/cmd/pk12util/pk12util.c index 147f8b305..e001dbfbb 100644 --- a/security/nss/cmd/pk12util/pk12util.c +++ b/security/nss/cmd/pk12util/pk12util.c @@ -42,6 +42,7 @@ #include "pk12util.h" #include "nss.h" #include "secport.h" +#include "secpkcs5.h" #include "certdb.h" #define PKCS12_IN_BUFFER_SIZE 200 @@ -66,6 +67,7 @@ Usage(char *progName) FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix] [-v]\n", progName); + FPS "\t\t [-c key_cipher] [-C cert_cipher] [-k key_leng]\n"); FPS "\t\t [-k slotpwfile | -K slotpw] [-w p12filepwfile | -W p12filepw]\n"); exit(PK12UERR_USAGE); @@ -592,7 +594,8 @@ p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len) void P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot, - secuPWData *slotPw, secuPWData *p12FilePw) + SECOidTag cipher, SECOidTag certCipher, + secuPWData *slotPw, secuPWData *p12FilePw) { SEC_PKCS12ExportContext *p12ecx = NULL; SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL; @@ -676,11 +679,11 @@ P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot, } keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx); - if(/*!SEC_PKCS12IsEncryptionAllowed() || */ PK11_IsFIPS()) { + if(certCipher == SEC_OID_UNKNOWN) { certSafe = keySafe; } else { - certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, - SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC); + certSafe = + SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certCipher); } if(!certSafe || !keySafe) { @@ -690,8 +693,7 @@ P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot, } if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, - CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem, - SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC) + CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem, cipher) != SECSuccess) { SECU_PrintError(progName,"add cert and key failed"); pk12uErrno = PK12UERR_ADDCERTKEY; @@ -785,6 +787,10 @@ P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot, printf(" Friendly Name: %s\n\n", dip->friendlyName->data); } + if (dip->shroudAlg) { + SECU_PrintAlgorithmID(stdout, dip->shroudAlg, + "Encryption algorithm",1); + } break; case SEC_OID_PKCS12_V1_KEY_BAG_ID: case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: @@ -796,6 +802,10 @@ P12U_ListPKCS12File(char *in_file, PK11SlotInfo *slot, printf(" Friendly Name: %s\n\n", dip->friendlyName->data); } + if (dip->shroudAlg) { + SECU_PrintAlgorithmID(stdout, dip->shroudAlg, + "Encryption algorithm",1); + } break; default: printf("unknown bag type(%d): %s\n\n", dip->type, @@ -819,6 +829,54 @@ loser: return rv; } +/* + * use the oid table description to map a user input string to a particular + * oid. + */ +SECOidTag +PKCS12U_MapCipherFromString(char *cipherString, int keyLen) +{ + SECOidTag tag; + SECOidData *oid; + SECOidTag cipher; + + /* future enhancement: accept dotted oid spec? */ + + /* future enhancement: provide 'friendlier' typed in names for + * pbe mechanisms. + */ + + /* look for the oid tag by Description */ + cipher = SEC_OID_UNKNOWN; + for (tag=1; (oid=SECOID_FindOIDByTag(tag)) != NULL ; tag++) { + /* only interested in oids that we actually understand */ + if (oid->mechanism == CKM_INVALID_MECHANISM) { + continue; + } + if (PORT_Strcasecmp(oid->desc, cipherString) != 0) { + continue; + } + /* we found a match... get the PBE version of this + * cipher... */ + if (!SEC_PKCS5IsAlgorithmPBEAlgTag(tag)) { + cipher = SEC_PKCS5GetPBEAlgorithm(tag, keyLen); + /* no eqivalent PKCS5/PKCS12 cipher, use the raw + * encryption tag we got and pass it directly in, + * pkcs12 will use the pkcsv5 mechanism */ + if (cipher == SEC_OID_PKCS5_PBES2) { + cipher = tag; + } else if (cipher == SEC_OID_PKCS5_PBMAC1) { + /* make sure we have not macing ciphers here */ + cipher = SEC_OID_UNKNOWN; + } + } else { + cipher = tag; + } + break; + } + return cipher; +} + static void p12u_EnableAllCiphers() { @@ -871,7 +929,11 @@ enum { opt_P12FilePWFile, opt_P12FilePW, opt_DBPrefix, - opt_Debug + opt_Debug, + opt_Cipher, + opt_CertCipher, + opt_KeyLength, + opt_CertKeyLength }; static secuCommandFlag pk12util_options[] = @@ -888,7 +950,11 @@ static secuCommandFlag pk12util_options[] = { /* opt_P12FilePWFile */ 'w', PR_TRUE, 0, PR_FALSE }, { /* opt_P12FilePW */ 'W', PR_TRUE, 0, PR_FALSE }, { /* opt_DBPrefix */ 'P', PR_TRUE, 0, PR_FALSE }, - { /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE } + { /* opt_Debug */ 'v', PR_FALSE, 0, PR_FALSE }, + { /* opt_Cipher */ 'c', PR_TRUE, 0, PR_FALSE }, + { /* opt_CertCipher */ 'C', PR_TRUE, 0, PR_FALSE }, + { /* opt_KeyLength */ 'k', PR_TRUE, 0, PR_FALSE }, + { /* opt_CertKeyLength */ 'K', PR_TRUE, 0, PR_FALSE } }; int @@ -902,6 +968,11 @@ main(int argc, char **argv) char *export_file = NULL; char *dbprefix = ""; SECStatus rv; + SECOidTag cipher = + SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC; + SECOidTag certCipher; + int keyLen = 0; + int certKeyLen = 0; secuCommand pk12util; pk12util.numCommands = 0; @@ -966,6 +1037,13 @@ main(int argc, char **argv) if (pk12util.options[opt_Raw].activated) { dumpRawFile = PR_TRUE; } + if (pk12util.options[opt_KeyLength].activated) { + keyLen = atoi(pk12util.options[opt_KeyLength].arg); + } + if (pk12util.options[opt_CertKeyLength].activated) { + certKeyLen = atoi(pk12util.options[opt_CertKeyLength].arg); + } + P12U_Init(SECU_ConfigDirectory(NULL), dbprefix, pk12util.options[opt_List].activated); @@ -980,13 +1058,49 @@ main(int argc, char **argv) goto done; } + if (pk12util.options[opt_Cipher].activated) { + char *cipherString = pk12util.options[opt_Cipher].arg; + + cipher = PKCS12U_MapCipherFromString(cipherString, keyLen); + /* We only want encryption PBE's. make sure we don't have + * any MAC pbes */ + if (cipher == SEC_OID_UNKNOWN) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString); + pk12uErrno = PK12UERR_INVALIDALGORITHM; + goto done; + } + } + + certCipher = PK11_IsFIPS() ? SEC_OID_UNKNOWN : + SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; + if (pk12util.options[opt_CertCipher].activated) { + char *cipherString = pk12util.options[opt_CertCipher].arg; + + if (PORT_Strcasecmp(cipherString, "none") == 0) { + certCipher = SEC_OID_UNKNOWN; + } else { + certCipher = PKCS12U_MapCipherFromString(cipherString, certKeyLen); + /* If the user requested a cipher and we didn't find it, then + * don't just silently not encrypt. */ + if (cipher == SEC_OID_UNKNOWN) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + SECU_PrintError(progName, "Algorithm: \"%s\"", cipherString); + pk12uErrno = PK12UERR_INVALIDALGORITHM; + goto done; + } + } + } + + if (pk12util.options[opt_Import].activated) { - P12U_ImportPKCS12Object(import_file, slot, &slotPw, + P12U_ImportPKCS12Object(import_file, slot, &slotPw, &p12FilePw); } else if (pk12util.options[opt_Export].activated) { P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg, - export_file, slot, &slotPw, &p12FilePw); + export_file, slot, cipher, certCipher, + &slotPw, &p12FilePw); } else if (pk12util.options[opt_List].activated) { P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw); diff --git a/security/nss/cmd/pk12util/pk12util.h b/security/nss/cmd/pk12util/pk12util.h index faf0c1f85..53751a33e 100644 --- a/security/nss/cmd/pk12util/pk12util.h +++ b/security/nss/cmd/pk12util/pk12util.h @@ -61,6 +61,7 @@ #define PK12UERR_CERTKEYSAFE 27 #define PK12UERR_ADDCERTKEY 28 #define PK12UERR_ENCODE 29 +#define PK12UERR_INVALIDALGORITHM 30 /* additions for importing and exporting PKCS 12 files */ |