Commit 71f201b8 authored by Robert Relyea's avatar Robert Relyea

Bug 1707130 NSS should use modern algorithms in PKCS#12 files by default r=mt

Also fixes:
Bug 452464 pk12util -o fails when -C option specifies AES or Camellia ciphers

Related:
Bug 1694689 Firefox should use modern algorithms in PKCS#12 files by default
Bug 452471 pk12util -o fails when -c option specifies pkcs12v2 PBE ciphers


The base of this fix is was a simple 3 line fix in pkcs12.c, changing the initial setting of cipher and cert cipher.

Overview for why this patch is larger than just 3 lines:
1. First issue was found in trying to change the mac hashing value.
   a. While the decrypt side knew how to handle SHA2 hashes, the equivalent code was not updated on the encrypt side. I refactored that code and placed the common function in p12local.c. Now p12e.c and p12d.c share common code to find the required function to produce the mac key.
   b. The prf hmac was hard coded to SHA1. I changed the code to pass the hmac matching the hashing algorithm for the mac. This required changes to p12e.c to calculate and pass the new hmac as well and adding new PK11_ExportEncryptedPrivateKey and PK11_ExportEncryptedPrivKey to take the PKCS #5 v2 parameters. I also corrected an error which prevented pkcs12 encoding of ciphers other than AES.
2. Once I've made my changes, I realized we didn't have a way of testing them. While we had code that verified that particular sets of parameters for pkcs12 worked together and could be listed and imported, we didn't have a way to verify what algorithms were actually generated by our tools.
  a. pk12util -l doesn't list the encryption used for the certs, so I updated pp to take a pkcs12 option. In doing so I had to update pp to handle indefinite encoding when decoding blocks. I also factored that decoding out in it's own function so the change only needed to be placed once. Finally I renabled a function which prints the output of an EncryptedPrivate key. This function was disabled long ago when the Encrypted Private key info was made private for NSS. It has since been exported, so these functions could easily be enabled (archeological note: I verified that this disabling was not a recent think I found I had done it back when I still have a netscape email address;).
 b. I updated tools.sh to us the new pp -t pkcs12 feature to verify that the key encryption, cert encryption, and hash functions matched what we expected when we exported a new key. I also updated tools.sh to handle the new hash variable option to pk12util.
 c. I discovered several tests commented out with comments that the don't work. I enabled those tests and discovered that they can now encrypt, but the can't decrypt because of pkcs12 policy. I updated the policy code, but I updated it to use the new NSS system wide policy mechanism. This enabled all the ciphers to work. There is still policy work to do. The pk12 policy currently only prevents ciphers from use in decrypting the certificates, not decrypting the keys and not encrypting. I left that for future work.
3. New options for pp and pk12util were added to the man pages for these tools.

---------------------------------------------------------------------------
With that in mind, here's a file by file description of the patch:

automation/abi-check/expected-report-libnss3.so.txt
-Add new exported functions. (see lib/nss/nss.def)

cmd/lib/basicutil.h:
-Removed the HAVE_EPV_TEMPLATE ifdefs (NSS has exported the Encrypted Private Key data structure for a while now.

cmd/lib/secutil.c:
global: Updated several functions to take a const char * m (message) rather than a char * m
global: Made the various PrintPKCS7 return an error code.
global: Added a state variable to be passed around the various PKCS7 Print functions. It gives the proper context to interpret PKCS7 Data Content. PKCS 12 used PKCS7 to package the various PKCS12 Safes and Bags.
-Updated SECU_StripTagAndLength to handle indefinite encoding, and to set the Error code.
-Added SECU_ExtractDERAndStep to grab the next DER Tag, Length, and Data.
-Updated secu_PrintRawStringQuotesOptional to remove the inline DER parsing and use SECU_ExtractDERAndStep().
-Updated SECU_PrintEncodedObjectID to return the SECOidTag just like SECU_PrintObjectID.
-Renable SECU_PrintPrivateKey
-Added secu_PrintPKCS12Attributes to print out the Attributes tied to a PKCS #12 Bag
-Added secu_PrintPKCS12Bag to print out a PKCS #12 Bag
-Added secu_PrintPKCS7Data, which uses the state to determine what it was printing out.
-Added secu_PrintDERPKCS7ContentInfo which is identical to the global function SECU_PrintPKCS7ContentInfo except it takes a state variable. The latter function now calls the former.
-Added secu_PrintPKCS12DigestInfo to print the Hash information of the Mac. DigestInfo is the name in the PKCS 12 spec.
-Added secu_PrintPKCS12MacData to print the Mac portion of the PKCS 12 file.
-Added SECU_PrintPKCS12 to print otu the pkcs12 file.

cmd/lib/secutil.h
-Added string for pkc12 for the command line of pp
reenabled SECU_PrintPrivateKey
-Added SECU_PrintPKCS12 for export.

cmd/pk12util/pk12util.c
-Added the -M option to specify a hash algorithm for the mac.
updated P12U_ExportPKCS12Object: pass the hash algorithm to the PasswordIntegrity handler.
-Added PKCS12U_FindTagFromString: generalized string to SECOidTag which only filters based on the oid having a matching PKCS #11 mechanism.
updated PKCS12U_MapCipherFromString to call use PKCS12U_FindTagFromString to get the candidate tag before doing it's post processing to decide if the tag is really an encryption algorithm.
-Added PKCS12U_MapHashFromString with is like MapCipherFromString except it verifies the resulting tag is a hash object.
-Updated main to 1) change the default cipher, change the default certCipher, and process the new hash argument. NOTE: in the old code we did not encrypt the certs in FIPS mode. That's because the certs were encrypted with RC4 in the default pkcs12 file, which wasn't a FIPS algorithm. Since AES is, we can use it independent on whether or not we are in FIPS mode.

cmd/pp/pp.c
-Added the pkcs12 option which calls SECU_PrintPKCS12 from secutil.c

lib/nss/nss.def
-Add exports to the new PK11_ExportEncryptedPrivKeyInfoV2 and PK11_ExportEncryptedPrivateKeyInfoV2 (V2 means PKCS 5 v2, not Version 2 of ExportEncrypted*Info).
-Add export for the old HASH_GetHMACOidTagByHashOidTag which should have been exported long ago to avoid the proliferation of copies of this function in places like ssl.

lib/pk11wrap/pk11akey.c
-Add PK11_ExportEncryptedPrivKeyInfoV2 (which the old function now calls), which takes the 3 PKCS 5 v2 parameters. The underlying pkcs5 code can fill in missing tags if necessary, but supplying all three gives the caller full control of the underlying pkcs5 PBE used.
-Add PK11_ExportEncryptedPrivateKeyInfoV2, same as the above function except it takes a cert which is used to look up the private key. It's the function that pkcs12 actually uses, but the former was exported for completeness.

lib/pk11wrap/pk11pub.h
-Added the new PK11_ExportEncryptedPriv*KeyInfoV2 functions.

lib/pkcs12/p12d.c
-Remove the switch statement and place it in p12local.c so that p12e.c can use the same function.

lib/pkc12/p12e.c
-Remove the unnecessary privAlg check so we can encode any mechanism we support. This only prevented encoding certificates in the pk12 file, not the keys.
-add code to get the hmac used in the pbe prf from the integrity hash, which is under application control.
-Do the same for key encryption, then use the new PK11_ExportEncryptedPrivateKeyInfo to pass that hash value.
-Use the new sec_pkcs12_algtag_to_keygen_mech so there is only one switch statement to update rather than 2.
-Update the hash data to old the length of the largest hash rather than the length of a SHA1 hash.

lib/pkcs12/p12local.c
- Add new function new sec_pkcs12_algtag_to_keygen_mech to factor out the common switch statement between p12e and p12d.

lib/pkcs12/p12local.h
-Export the new sec_pkcs12_algtag_to_keygen_mech

lib/pkcs12/p12plcy.c
-Map the old p12 policy functions to use the new NSS_GetAlgorithmPolicy. We keep the old table so that applications can change the policy with the old PKCS12 specific defines (so the old code keeps working). NOTE: policies now default to true rather than false.

lib/util/secoidt.h
-Add new NSS_USE_ALG_IN_PKCS12 used by pk11plcy.c
NOTE: I have not updated the policy table in pk11wrap/pk11pars.c, so we can't yet control pkcs12 policy with the nss system policy table. That's a patch for another time.

test/tools/tool.sh
-global: Remove trailing spaces
-global: DEFAULT is changed to 'default'
-Update the PBE mechanism to exactly match the string in secoid.c. PKCS #12 does case independent compares, so case doesn't matter there, but now I'm comparing to the output of pp, and I didn't want to spend the time to figure out case independent compares in bash.
-Add our defauts and shell variables at the top so there are easy to change in the future.
export_with_*** have all been colapsed into a single export_p12_file which handles taking 'default' and turning off that argument.
-Add for loops for the hash functions.
-Restore the camellia ciphers back now that they work.
-Restore the pkcs12V2pbe back now that they work.
-Collect various pbe types into single variables and use those variables in loops
-Reduce the number of tests ran in optimized mode (which takes 60x the time to do a pbe then than debug mode based on a larger iterator).
-Add verify_p12 which dumps out the p12 file and makes sure the expected CERT_ENCRYPTION, KEY_ENCRYPTION, and HASH are used.

doc/pp.xml
-Add pkcs12 option

doc/pk12util.xml
-Add -M option
-Update synopsis with options in the description but not in the synopsis

Differential Revision: https://phabricator.services.mozilla.com/D113699
parent cbb5c314
2 Added functions:
5 Added functions:
'function SECOidTag HASH_GetHMACOidTagByHashOidTag(SECOidTag)' {HASH_GetHMACOidTagByHashOidTag@@NSS_3.65}
'function PK11Context* PK11_CreateContextByPrivKey(CK_MECHANISM_TYPE, CK_ATTRIBUTE_TYPE, SECKEYPrivateKey*, const SECItem*)' {PK11_CreateContextByPrivKey@@NSS_3.65}
'function PK11Context* PK11_CreateContextByPubKey(CK_MECHANISM_TYPE, CK_ATTRIBUTE_TYPE, SECKEYPublicKey*, const SECItem*, void*)' {PK11_CreateContextByPubKey@@NSS_3.65}
'function SECKEYEncryptedPrivateKeyInfo* PK11_ExportEncryptedPrivKeyInfoV2(PK11SlotInfo*, SECOidTag, SECOidTag, SECOidTag, SECItem*, SECKEYPrivateKey*, int, void*)' {PK11_ExportEncryptedPrivKeyInfoV2@@NSS_3.65}
'function SECKEYEncryptedPrivateKeyInfo* PK11_ExportEncryptedPrivateKeyInfoV2(PK11SlotInfo*, SECOidTag, SECOidTag, SECOidTag, SECItem*, CERTCertificate*, int, void*)' {PK11_ExportEncryptedPrivateKeyInfoV2@@NSS_3.65}
1 function with some indirect sub-type change:
......
......@@ -54,10 +54,8 @@ extern void SECU_PrintAsHex(FILE *out, const SECItem *i, const char *m,
/* dump a buffer in hex and ASCII */
extern void SECU_PrintBuf(FILE *out, const char *msg, const void *vp, int len);
#ifdef HAVE_EPV_TEMPLATE
/* Dump contents of private key */
extern int SECU_PrintPrivateKey(FILE *out, SECItem *der, char *m, int level);
#endif
/* Init PKCS11 stuff */
extern SECStatus SECU_PKCS11Init(PRBool readOnly);
......
This diff is collapsed.
......@@ -26,6 +26,7 @@
#define SEC_CT_CERTIFICATE_REQUEST "certificate-request"
#define SEC_CT_CERTIFICATE_ID "certificate-identity"
#define SEC_CT_PKCS7 "pkcs7"
#define SEC_CT_PKCS12 "pkcs12"
#define SEC_CT_CRL "crl"
#define SEC_CT_NAME "name"
......@@ -235,10 +236,8 @@ extern void SECU_PrintTrustFlags(FILE *out, CERTCertTrust *trust, char *m,
extern int SECU_PrintSubjectPublicKeyInfo(FILE *out, SECItem *der, char *m,
int level);
#ifdef HAVE_EPV_TEMPLATE
/* Dump contents of private key */
extern int SECU_PrintPrivateKey(FILE *out, SECItem *der, char *m, int level);
#endif
/* Dump contents of an RSA public key */
extern void SECU_PrintRSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level);
......@@ -253,7 +252,8 @@ extern int SECU_PrintFingerprints(FILE *out, SECItem *derCert, char *m,
/* Pretty-print any PKCS7 thing */
extern int SECU_PrintPKCS7ContentInfo(FILE *out, SECItem *der, char *m,
int level);
/* Pretty-print a pkcs12 file */
extern SECStatus SECU_PrintPKCS12(FILE *out, const SECItem *der, char *m, int level);
/* Init PKCS11 stuff */
extern SECStatus SECU_PKCS11Init(PRBool readOnly);
......
......@@ -16,6 +16,7 @@
#include "nss.h"
#include "secport.h"
#include "secpkcs5.h"
#include "sechash.h"
#include "certdb.h"
#define PKCS12_IN_BUFFER_SIZE 200
......@@ -43,7 +44,7 @@ Usage()
FPS "Usage: %s -o exportfile -n certname [-d certdir] [-P dbprefix]\n",
progName);
FPS "\t\t [-c key_cipher] [-C cert_cipher]\n"
FPS "\t\t [-c key_cipher] [-C cert_cipher] [-M mac_alg]\n"
"\t\t [-m | --key_len keyLen] [--cert_key_len certKeyLen] [-v]\n");
FPS "\t\t [-k slotpwfile | -K slotpw]\n"
"\t\t [-w p12filepwfile | -W p12filepw]\n");
......@@ -625,7 +626,7 @@ p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len)
void
P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
SECOidTag cipher, SECOidTag certCipher,
SECOidTag cipher, SECOidTag certCipher, SECOidTag hash,
secuPWData *slotPw, secuPWData *p12FilePw)
{
SEC_PKCS12ExportContext *p12ecx = NULL;
......@@ -690,7 +691,7 @@ P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
goto loser;
}
if (SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, SEC_OID_SHA1) !=
if (SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, hash) !=
SECSuccess) {
SECU_PrintError(progName, "PKCS12 add password integrity failed");
pk12uErrno = PK12UERR_PK12ADDPWDINTEG;
......@@ -722,8 +723,8 @@ P12U_ExportPKCS12Object(char *nn, char *outfile, PK11SlotInfo *inSlot,
}
if (SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert,
CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem, cipher) !=
SECSuccess) {
CERT_GetDefaultCertDB(), keySafe, NULL,
PR_TRUE, pwitem, cipher) != SECSuccess) {
SECU_PrintError(progName, "add cert and key failed");
pk12uErrno = PK12UERR_ADDCERTKEY;
goto loser;
......@@ -857,6 +858,27 @@ loser:
return rv;
}
SECOidTag
PKCS12U_FindTagFromString(char *cipherString)
{
SECOidTag tag;
SECOidData *oid;
/* future enhancement: accept dotted oid spec? */
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;
}
return tag;
}
return SEC_OID_UNKNOWN;
}
/*
* use the oid table description to map a user input string to a particular
* oid.
......@@ -865,46 +887,55 @@ 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 */
tag = PKCS12U_FindTagFromString(cipherString);
if (tag == SEC_OID_UNKNOWN) {
return tag;
}
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 {
/* 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;
}
break;
} else {
cipher = tag;
}
return cipher;
}
SECOidTag
PKCS12U_MapHashFromString(char *hashString)
{
SECOidTag hashAlg;
/* look for the oid tag by Description */
hashAlg = PKCS12U_FindTagFromString(hashString);
if (hashAlg == SEC_OID_UNKNOWN) {
return hashAlg;
}
/* make sure it's a hashing oid */
if (HASH_GetHashTypeByOidTag(hashAlg) == HASH_AlgNULL) {
return SEC_OID_UNKNOWN;
}
return hashAlg;
}
static void
p12u_EnableAllCiphers()
{
......@@ -917,7 +948,7 @@ p12u_EnableAllCiphers()
SEC_PKCS12EnableCipher(PKCS12_AES_CBC_128, 1);
SEC_PKCS12EnableCipher(PKCS12_AES_CBC_192, 1);
SEC_PKCS12EnableCipher(PKCS12_AES_CBC_256, 1);
SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
SEC_PKCS12SetPreferredCipher(PKCS12_AES_CBC_256, 1);
}
static PRUintn
......@@ -963,7 +994,8 @@ enum {
opt_Cipher,
opt_CertCipher,
opt_KeyLength,
opt_CertKeyLength
opt_CertKeyLength,
opt_Mac
};
static secuCommandFlag pk12util_options[] =
......@@ -984,7 +1016,8 @@ static secuCommandFlag pk12util_options[] =
{ /* opt_Cipher */ 'c', PR_TRUE, 0, PR_FALSE },
{ /* opt_CertCipher */ 'C', PR_TRUE, 0, PR_FALSE },
{ /* opt_KeyLength */ 'm', PR_TRUE, 0, PR_FALSE, "key_len" },
{ /* opt_CertKeyLength */ 0, PR_TRUE, 0, PR_FALSE, "cert_key_len" }
{ /* opt_CertKeyLength */ 0, PR_TRUE, 0, PR_FALSE, "cert_key_len" },
{ /* opt_Mac */ 'M', PR_TRUE, 0, PR_FALSE, PR_FALSE }
};
int
......@@ -998,9 +1031,9 @@ 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;
SECOidTag cipher = SEC_OID_AES_256_CBC;
SECOidTag hash = SEC_OID_SHA256;
SECOidTag certCipher = SEC_OID_AES_128_CBC;
int keyLen = 0;
int certKeyLen = 0;
secuCommand pk12util;
......@@ -1114,7 +1147,6 @@ main(int argc, char **argv)
}
}
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;
......@@ -1132,6 +1164,18 @@ main(int argc, char **argv)
}
}
}
if (pk12util.options[opt_Mac].activated) {
char *hashString = pk12util.options[opt_Mac].arg;
hash = PKCS12U_MapHashFromString(hashString);
/* We don't support creating Mac-less pkcs 12 files */
if (hash == SEC_OID_UNKNOWN) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
SECU_PrintError(progName, "Algorithm: \"%s\"", hashString);
pk12uErrno = PK12UERR_INVALIDALGORITHM;
goto done;
}
}
if (pk12util.options[opt_Import].activated) {
P12U_ImportPKCS12Object(import_file, slot, &slotPw, &p12FilePw);
......@@ -1139,7 +1183,7 @@ main(int argc, char **argv)
} else if (pk12util.options[opt_Export].activated) {
P12U_ExportPKCS12Object(pk12util.options[opt_Nickname].arg,
export_file, slot, cipher, certCipher,
&slotPw, &p12FilePw);
hash, &slotPw, &p12FilePw);
} else if (pk12util.options[opt_List].activated) {
P12U_ListPKCS12File(import_file, slot, &slotPw, &p12FilePw);
......
......@@ -27,14 +27,13 @@ Usage(char *progName)
progName);
fprintf(stderr, "Pretty prints a file containing ASN.1 data in DER or ascii format.\n");
fprintf(stderr, "%-14s Specify input and display type:", "-t type");
#ifdef HAVE_EPV_TEMPLATE
fprintf(stderr, " %s (sk),", SEC_CT_PRIVATE_KEY);
#endif
fprintf(stderr, "\n");
fprintf(stderr, "%-14s %s (pk), %s (c), %s (cr),\n", "", SEC_CT_PUBLIC_KEY,
SEC_CT_CERTIFICATE, SEC_CT_CERTIFICATE_REQUEST);
fprintf(stderr, "%-14s %s (ci), %s (p7), %s or %s (n).\n", "", SEC_CT_CERTIFICATE_ID,
SEC_CT_PKCS7, SEC_CT_CRL, SEC_CT_NAME);
fprintf(stderr, "%-14s %s (ci), %s (p7), %s (p12), %s or %s (n).\n", "",
SEC_CT_CERTIFICATE_ID, SEC_CT_PKCS7, SEC_CT_PKCS12,
SEC_CT_CRL, SEC_CT_NAME);
fprintf(stderr, "%-14s (Use either the long type name or the shortcut.)\n", "");
fprintf(stderr, "%-14s Input is in ascii encoded form (RFC1113)\n",
"-a");
......@@ -159,11 +158,9 @@ main(int argc, char **argv)
SECU_PrintCertificateRequest);
} else if (PORT_Strcmp(typeTag, SEC_CT_CRL) == 0) {
rv = SECU_PrintSignedData(outFile, &data, "CRL", 0, SECU_PrintCrl);
#ifdef HAVE_EPV_TEMPLATE
} else if (PORT_Strcmp(typeTag, SEC_CT_PRIVATE_KEY) == 0 ||
PORT_Strcmp(typeTag, "sk") == 0) {
rv = SECU_PrintPrivateKey(outFile, &data, "Private Key", 0);
#endif
} else if (PORT_Strcmp(typeTag, SEC_CT_PUBLIC_KEY) == 0 ||
PORT_Strcmp(typeTag, "pk") == 0) {
rv = SECU_PrintSubjectPublicKeyInfo(outFile, &data, "Public Key", 0);
......@@ -174,6 +171,9 @@ main(int argc, char **argv)
} else if (PORT_Strcmp(typeTag, SEC_CT_NAME) == 0 ||
PORT_Strcmp(typeTag, "n") == 0) {
rv = SECU_PrintDERName(outFile, &data, "Name", 0);
} else if (PORT_Strcmp(typeTag, SEC_CT_PKCS12) == 0 ||
PORT_Strcmp(typeTag, "p12") == 0) {
rv = SECU_PrintPKCS12(outFile, &data, "PKCS #12 File", 0);
} else {
fprintf(stderr, "%s: don't know how to print out '%s' files\n",
progName, typeTag);
......
......@@ -28,11 +28,17 @@
<cmdsynopsis>
<command>pk12util</command>
<arg>-i p12File|-l p12File|-o p12File</arg>
<arg>-c keyCipher</arg>
<arg>-C certCipher</arg>
<arg>-d [sql:]directory</arg>
<arg>-h tokenname</arg>
<arg>-m | --key-len keyLength</arg>
<arg>-M hashAlg</arg>
<arg>-n certname</arg>
<arg>-P dbprefix</arg>
<arg>-r</arg>
<arg>-v</arg>
<arg>--cert-key-len certKeyLength</arg>
<arg>-k slotPasswordFile|-K slotPassword</arg>
<arg>-w p12filePasswordFile|-W p12filePassword</arg>
</cmdsynopsis>
......@@ -107,6 +113,12 @@
<listitem><para>Specify the desired length of the symmetric key to be used to encrypt the private key.</para></listitem>
</varlistentry>
<varlistentry>
<term>-M hashAlg</term>
<listitem><para>Specify the hash algorithm used in the pkcs #12 mac. This algorithm also specifies the HMAC used in the prf when using pkcs #5 v2.</para></listitem>
</varlistentry>
<varlistentry>
<term>--cert-key-len certKeyLength</term>
<listitem><para>Specify the desired length of the symmetric key to be used to encrypt the certificates and other meta-data.</para></listitem>
......
......@@ -40,7 +40,7 @@
<title>Description</title>
<para><command>pp </command>pretty-prints private and public key, certificate, certificate-request,
pkcs7 or crl files
pkcs7, pkcs12 or crl files
</para>
</refsection>
......@@ -53,7 +53,7 @@
<varlistentry>
<term><option>-t </option> <replaceable>type</replaceable></term>
<listitem>
<simpara>specify the input, one of {private-key | public-key | certificate | certificate-request | pkcs7 | crl}</simpara>
<simpara>specify the input, one of {private-key | public-key | certificate | certificate-request | pkcs7 | pkcs12 | crl | name}</simpara>
<simpara></simpara>
</listitem>
</varlistentry>
......
......@@ -1224,6 +1224,9 @@ PK11_HPKE_ImportContext;
;+ global:
PK11_CreateContextByPubKey;
PK11_CreateContextByPrivKey;
PK11_ExportEncryptedPrivKeyInfoV2;
PK11_ExportEncryptedPrivateKeyInfoV2;
HASH_GetHMACOidTagByHashOidTag;
;+ local:
;+ *;
;+};
......@@ -1969,14 +1969,20 @@ PK11_ExportPrivateKeyInfo(CERTCertificate *cert, void *wincx)
return pki;
}
/* V2 refers to PKCS #5 V2 here. If a PKCS #5 v1 or PKCS #12 pbe is passed
* for pbeTag, then encTag and hashTag are ignored. If pbe is an encryption
* algorithm, then PKCS #5 V2 is used with prfTag for the prf. If prfTag isn't
* supplied prf will be SEC_OID_HMAC_SHA1 */
SECKEYEncryptedPrivateKeyInfo *
PK11_ExportEncryptedPrivKeyInfo(
PK11_ExportEncryptedPrivKeyInfoV2(
PK11SlotInfo *slot, /* optional, encrypt key in this slot */
SECOidTag algTag, /* encrypt key with this algorithm */
SECOidTag pbeAlg, /* PBE algorithm to encrypt the with key */
SECOidTag encAlg, /* Encryption algorithm to Encrypt the key with */
SECOidTag prfAlg, /* Hash algorithm for PRF */
SECItem *pwitem, /* password for PBE encryption */
SECKEYPrivateKey *pk, /* encrypt this private key */
int iteration, /* interations for PBE alg */
void *wincx) /* context for password callback ? */
void *pwArg) /* context for password callback */
{
SECKEYEncryptedPrivateKeyInfo *epki = NULL;
PLArenaPool *arena = NULL;
......@@ -1997,7 +2003,7 @@ PK11_ExportEncryptedPrivKeyInfo(
return NULL;
}
algid = sec_pkcs5CreateAlgorithmID(algTag, SEC_OID_UNKNOWN, SEC_OID_UNKNOWN,
algid = sec_pkcs5CreateAlgorithmID(pbeAlg, encAlg, prfAlg,
&pbeAlgTag, 0, NULL, iteration);
if (algid == NULL) {
return NULL;
......@@ -2026,7 +2032,7 @@ PK11_ExportEncryptedPrivKeyInfo(
slot = pk->pkcs11Slot;
}
}
key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, wincx);
key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, pwArg);
if (key == NULL) {
rv = SECFailure;
goto loser;
......@@ -2121,24 +2127,59 @@ loser:
}
SECKEYEncryptedPrivateKeyInfo *
PK11_ExportEncryptedPrivateKeyInfo(
PK11_ExportEncryptedPrivKeyInfo(
PK11SlotInfo *slot, /* optional, encrypt key in this slot */
SECOidTag algTag, /* PBE algorithm to encrypt the with key */
SECItem *pwitem, /* password for PBE encryption */
SECKEYPrivateKey *pk, /* encrypt this private key */
int iteration, /* interations for PBE alg */
void *pwArg) /* context for password callback */
{
return PK11_ExportEncryptedPrivKeyInfoV2(slot, algTag, SEC_OID_UNKNOWN,
SEC_OID_UNKNOWN, pwitem, pk,
iteration, pwArg);
}
/* V2 refers to PKCS #5 V2 here. If a PKCS #5 v1 or PKCS #12 pbe is passed
* for pbeTag, then encTag and hashTag are ignored. If pbe is an encryption
* algorithm, then PKCS #5 V2 is used with prfTag for the prf. If prfTag isn't
* supplied prf will be SEC_OID_HMAC_SHA1 */
SECKEYEncryptedPrivateKeyInfo *
PK11_ExportEncryptedPrivateKeyInfoV2(
PK11SlotInfo *slot, /* optional, encrypt key in this slot */
SECOidTag algTag, /* encrypt key with this algorithm */
SECOidTag pbeAlg, /* PBE algorithm to encrypt the with key */
SECOidTag encAlg, /* Encryption algorithm to Encrypt the key with */
SECOidTag prfAlg, /* HMAC algorithm for PRF*/
SECItem *pwitem, /* password for PBE encryption */
CERTCertificate *cert, /* wrap priv key for this user cert */
int iteration, /* interations for PBE alg */
void *wincx) /* context for password callback ? */
void *pwArg) /* context for password callback */
{
SECKEYEncryptedPrivateKeyInfo *epki = NULL;
SECKEYPrivateKey *pk = PK11_FindKeyByAnyCert(cert, wincx);
SECKEYPrivateKey *pk = PK11_FindKeyByAnyCert(cert, pwArg);
if (pk != NULL) {
epki = PK11_ExportEncryptedPrivKeyInfo(slot, algTag, pwitem, pk,
iteration, wincx);
epki = PK11_ExportEncryptedPrivKeyInfoV2(slot, pbeAlg, encAlg, prfAlg,
pwitem, pk, iteration,
pwArg);
SECKEY_DestroyPrivateKey(pk);
}
return epki;
}
SECKEYEncryptedPrivateKeyInfo *
PK11_ExportEncryptedPrivateKeyInfo(
PK11SlotInfo *slot, /* optional, encrypt key in this slot */
SECOidTag algTag, /* encrypt key with this algorithm */
SECItem *pwitem, /* password for PBE encryption */
CERTCertificate *cert, /* wrap priv key for this user cert */
int iteration, /* interations for PBE alg */
void *pwArg) /* context for password callback */
{
return PK11_ExportEncryptedPrivateKeyInfoV2(slot, algTag, SEC_OID_UNKNOWN,
SEC_OID_UNKNOWN, pwitem, cert,
iteration, pwArg);
}
SECItem *
PK11_DEREncodePublicKey(const SECKEYPublicKey *pubk)
{
......
......@@ -627,10 +627,20 @@ SECKEYPrivateKeyInfo *PK11_ExportPrivateKeyInfo(
CERTCertificate *cert, void *wincx);
SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivKeyInfo(
PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
SECKEYPrivateKey *pk, int iteration, void *wincx);
SECKEYPrivateKey *pk, int iteration, void *pwArg);
SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfo(
PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
CERTCertificate *cert, int iteration, void *wincx);
CERTCertificate *cert, int iteration, void *pwArg);
/* V2 refers to PKCS #5 V2 here. If a PKCS #5 v1 or PKCS #12 pbe is passed
* for pbeTag, then encTag and hashTag are ignored. If pbe is an encryption
* algorithm, then PKCS #5 V2 is used with prfTag for the prf. If prfTag isn't
* supplied prf will be SEC_OID_HMAC_SHA1 */
SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivKeyInfoV2(
PK11SlotInfo *slot, SECOidTag pbeTag, SECOidTag encTag, SECOidTag prfTag,
SECItem *pwitem, SECKEYPrivateKey *pk, int iteration, void *pwArg);
SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfoV2(
PK11SlotInfo *slot, SECOidTag pbeTag, SECOidTag encTag, SECOidTag prfTag,
SECItem *pwitem, CERTCertificate *cert, int iteration, void *pwArg);
SECKEYPrivateKey *PK11_FindKeyByDERCert(PK11SlotInfo *slot,
CERTCertificate *cert, void *wincx);
SECKEYPublicKey *PK11_MakeKEAPubKey(unsigned char *data, int length);
......
......@@ -1353,32 +1353,10 @@ sec_pkcs12_decoder_verify_mac(SEC_PKCS12DecoderContext *p12dcx)
iteration);
algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm);
switch (algtag) {
case SEC_OID_SHA1:
integrityMech = CKM_NSS_PBE_SHA1_HMAC_KEY_GEN;
break;
case SEC_OID_MD5:
integrityMech = CKM_NSS_PBE_MD5_HMAC_KEY_GEN;
break;
case SEC_OID_MD2:
integrityMech = CKM_NSS_PBE_MD2_HMAC_KEY_GEN;
break;
case SEC_OID_SHA224:
integrityMech = CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN;
break;
case SEC_OID_SHA256:
integrityMech = CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN;
break;
case SEC_OID_SHA384:
integrityMech = CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN;
break;
case SEC_OID_SHA512:
integrityMech = CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN;
break;
default:
goto loser;
integrityMech = sec_pkcs12_algtag_to_keygen_mech(algtag);
if (integrityMech == CKM_INVALID_MECHANISM) {
goto loser;
}
symKey = PK11_KeyGen(NULL, integrityMech, params, 0, NULL);
PK11_DestroyPBEParams(params);
params = NULL;
......
......@@ -14,6 +14,7 @@
#include "secpkcs7.h"
#include "secasn1.h"
#include "secerr.h"
#include "sechash.h"
#include "pk11func.h"
#include "p12plcy.h"
#include "p12local.h"
......@@ -379,11 +380,18 @@ SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt,
safeInfo->itemCount = 0;
/* create the encrypted safe */
if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg) &&
PK11_AlgtagToMechanism(privAlg) == CKM_AES_CBC) {
if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg)) {
SECOidTag prfAlg = SEC_OID_UNKNOWN;
/* if we have password integrity set, use that to set the integrity
* hash algorithm to set our password PRF. If we haven't set it, just
* let the low level code pick it */
if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
prfAlg = HASH_GetHMACOidTagByHashOidTag(
p12ctxt->integrityInfo.pwdInfo.algorithm);
}
safeInfo->cinfo = SEC_PKCS7CreateEncryptedDataWithPBEV2(SEC_OID_PKCS5_PBES2,
privAlg,
SEC_OID_UNKNOWN,
prfAlg,
0,
p12ctxt->pwfn,
p12ctxt->pwfnarg);
......@@ -1213,6 +1221,7 @@ SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *sa
/* extract the key encrypted */
SECKEYEncryptedPrivateKeyInfo *epki = NULL;
PK11SlotInfo *slot = NULL;
SECOidTag prfAlg = SEC_OID_UNKNOWN;
if (!sec_pkcs12_encode_password(p12ctxt->arena, &uniPwitem, algorithm,
pwitem)) {
......@@ -1220,6 +1229,14 @@ SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *sa
goto loser;
}
/* if we have password integrity set, use that to set the integrity
* hash algorithm to set our password PRF. If we haven't set it, just
* let the low level code pick it */
if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
prfAlg = HASH_GetHMACOidTagByHashOidTag(
p12ctxt->integrityInfo.pwdInfo.algorithm);
}
/* we want to make sure to take the key out of the key slot */
if (PK11_IsInternal(p12ctxt->slot)) {
slot = PK11_GetInternalKeySlot();
......@@ -1227,10 +1244,15 @@ SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *sa
slot = PK11_ReferenceSlot(p12ctxt->slot);
}
epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm,
&uniPwitem, cert,
NSS_PBE_DEFAULT_ITERATION_COUNT,
p12ctxt->wincx);
/* passing algorithm as the pbe will force the PBE code to
* automatically handle the selection between using the algorithm
* as a the pbe algorithm, or using the algorithm as a cipher
* and building a pkcs5 pbe */
epki = PK11_ExportEncryptedPrivateKeyInfoV2(slot, algorithm,
SEC_OID_UNKNOWN, prfAlg,
&uniPwitem, cert,
NSS_PBE_DEFAULT_ITERATION_COUNT,
p12ctxt->wincx);
PK11_FreeSlot(slot);
if (!epki) {
PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
......@@ -1595,18 +1617,10 @@ sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
SECITEM_ZfreeItem(&pwd, PR_FALSE);
/* get the PBA Mechanism to generate the key */
switch (p12exp->integrityInfo.pwdInfo.algorithm) {
case SEC_OID_SHA1:
integrityMechType = CKM_PBA_SHA1_WITH_SHA1_HMAC;
break;
case SEC_OID_MD5:
integrityMechType = CKM_NSS_PBE_MD5_HMAC_KEY_GEN;
break;
case SEC_OID_MD2:
integrityMechType = CKM_NSS_PBE_MD2_HMAC_KEY_GEN;
break;
default:
goto loser;
integrityMechType = sec_pkcs12_algtag_to_keygen_mech(
p12exp->integrityInfo.pwdInfo.algorithm);
if (integrityMechType == CKM_INVALID_MECHANISM) {
goto loser;
}
/* generate the key */
......@@ -1837,7 +1851,8 @@ loser:
static SECStatus
sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
{
SECItem hmac = { siBuffer, NULL, 0 };
unsigned char hmacData[HASH_LENGTH_MAX];
unsigned int hmacLen;
SECStatus rv;
SGNDigestInfo *di = NULL;
void *dummy;
......@@ -1856,13 +1871,8 @@ sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
}
/* finish the hmac */
hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH);
if (!hmac.data) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH);
rv = PK11_DigestFinal(p12ecx->hmacCx, hmacData, &hmacLen, HASH_LENGTH_MAX);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
......@@ -1871,7 +1881,7 @@ sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
/* create the digest info */
di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm,
hmac.data, hmac.len);
hmacData, hmacLen);
if (!di) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
rv = SECFailure;
......@@ -1896,11 +1906,9 @@ loser:
if (di) {
SGN_DestroyDigestInfo(di);
}
if (hmac.data) {
SECITEM_ZfreeItem(&hmac, PR_FALSE);
}
PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
p12ecx->hmacCx = NULL;
PORT_Memset(hmacData, 0, hmacLen);
return rv;
}
......
......@@ -44,6 +44,37 @@ sec_pkcs12_algtag_to_mech(SECOidTag algtag)
return CKM_INVALID_MECHANISM;
}
CK_MECHANISM_TYPE
sec_pkcs12_algtag_to_keygen_mech(SECOidTag algtag)
{
switch (algtag) {
case SEC_OID_SHA1:
return CKM_NSS_PBE_SHA1_HMAC_KEY_GEN;
break;
case SEC_OID_MD5:
return CKM_NSS_PBE_MD5_HMAC_KEY_GEN;
break;
case SEC_OID_MD2:
return CKM_NSS_PBE_MD2_HMAC_KEY_GEN;
break;
case SEC_OID_SHA224:
return CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN;
break;
case SEC_OID_SHA256:
return CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN;
break;
case SEC_OID_SHA384:
return CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN;
break;
case SEC_OID_SHA512:
return CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN;
break;
default:
break;
}
return CKM_INVALID_MECHANISM;
}
/* helper functions */
/* returns proper bag type template based upon object type tag */
const SEC_ASN1Template *
......
......@@ -38,6 +38,7 @@ extern PRBool sec_pkcs12_convert_item_to_unicode(PLArenaPool *arena, SECItem *de
SECItem *src, PRBool zeroTerm,
PRBool asciiConvert, PRBool toUnicode);
extern CK_MECHANISM_TYPE sec_pkcs12_algtag_to_mech(SECOidTag algtag);