< prev index next >

src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java

Print this page

        

*** 67,88 **** import sun.security.util.DerInputStream; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; import sun.security.pkcs.ContentInfo; import sun.security.x509.AlgorithmId; import sun.security.pkcs.EncryptedPrivateKeyInfo; /** * This class provides the keystore implementation referred to as "PKCS12". * Implements the PKCS#12 PFX protected using the Password privacy mode. * The contents are protected using Password integrity mode. * ! * Currently we support following PBE algorithms: ! * - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys ! * - pbeWithSHAAnd40BitRC2CBC to encrypt certificates * * Supported encryption of various implementations : * * Software and mode. Certificate encryption Private key encryption * --------------------------------------------------------------------- --- 67,91 ---- import sun.security.util.DerInputStream; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; import sun.security.pkcs.ContentInfo; + import sun.security.util.SecurityProperties; import sun.security.x509.AlgorithmId; import sun.security.pkcs.EncryptedPrivateKeyInfo; /** * This class provides the keystore implementation referred to as "PKCS12". * Implements the PKCS#12 PFX protected using the Password privacy mode. * The contents are protected using Password integrity mode. * ! * Currently these PBE algorithms are used by default: ! * - PBEWithSHA1AndDESede to encrypt private keys, iteration count 50000. ! * - PBEWithSHA1AndRC2_40 to encrypt certificates, iteration count 50000. ! * ! * The default Mac algorithm is HmacPBESHA1, iteration count 100000. * * Supported encryption of various implementations : * * Software and mode. Certificate encryption Private key encryption * ---------------------------------------------------------------------
*** 121,148 **** * * @author Seema Malkani * @author Jeff Nisewanger * @author Jan Luehe * - * @see KeyProtector * @see java.security.KeyStoreSpi - * @see KeyTool - * - * */ public final class PKCS12KeyStore extends KeyStoreSpi { public static final int VERSION_3 = 3; - private static final String[] KEY_PROTECTION_ALGORITHM = { - "keystore.pkcs12.keyProtectionAlgorithm", - "keystore.PKCS12.keyProtectionAlgorithm" - }; - private static final int MAX_ITERATION_COUNT = 5000000; - private static final int PBE_ITERATION_COUNT = 50000; // default - private static final int MAC_ITERATION_COUNT = 100000; // default private static final int SALT_LEN = 20; // friendlyName, localKeyId, trustedKeyUsage private static final String[] CORE_ATTRIBUTES = { "1.2.840.113549.1.9.20", --- 124,140 ----
*** 159,193 **** private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20}; private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21}; private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1}; - private static final int pbeWithSHAAnd40BitRC2CBC[] = - {1, 2, 840, 113549, 1, 12, 1, 6}; - private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = - {1, 2, 840, 113549, 1, 12, 1, 3}; private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; // TODO: temporary Oracle OID /* * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } */ private static final int TrustedKeyUsage[] = {2, 16, 840, 1, 113894, 746875, 1, 1}; private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0}; ! private static ObjectIdentifier PKCS8ShroudedKeyBag_OID; ! private static ObjectIdentifier CertBag_OID; ! private static ObjectIdentifier SecretBag_OID; ! private static ObjectIdentifier PKCS9FriendlyName_OID; ! private static ObjectIdentifier PKCS9LocalKeyId_OID; ! private static ObjectIdentifier PKCS9CertType_OID; ! private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID; ! private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID; ! private static ObjectIdentifier pbes2_OID; ! private static ObjectIdentifier TrustedKeyUsage_OID; ! private static ObjectIdentifier[] AnyUsage; private int counter = 0; // private key count // Note: This is a workaround to allow null localKeyID attribute --- 151,179 ---- private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20}; private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21}; private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1}; private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; // TODO: temporary Oracle OID /* * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } */ private static final int TrustedKeyUsage[] = {2, 16, 840, 1, 113894, 746875, 1, 1}; private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0}; ! private static final ObjectIdentifier PKCS8ShroudedKeyBag_OID; ! private static final ObjectIdentifier CertBag_OID; ! private static final ObjectIdentifier SecretBag_OID; ! private static final ObjectIdentifier PKCS9FriendlyName_OID; ! private static final ObjectIdentifier PKCS9LocalKeyId_OID; ! private static final ObjectIdentifier PKCS9CertType_OID; ! private static final ObjectIdentifier pbes2_OID; ! private static final ObjectIdentifier TrustedKeyUsage_OID; ! private static final ObjectIdentifier[] AnyUsage; private int counter = 0; // private key count // Note: This is a workaround to allow null localKeyID attribute
*** 198,207 **** --- 184,204 ---- private int secretKeyCount = 0; // certificate count private int certificateCount = 0; + // Alg/params used for *this* keystore. Initialized as -1 for ic and + // null for algorithm names. When an existing file is read, they will be + // assigned inside engineLoad() so storing an existing keystore uses the + // old alg/params. This makes sure if a keystore is created password-less + // it will be password-less forever. Otherwise, engineStore() will read + // the default values. These fields are always reset when load() is called. + private String certProtectionAlgorithm = null; + private int certPbeIterationCount = -1; + private String macAlgorithm = null; + private int macIterationCount = -1; + // the source of randomness private SecureRandom random; static { try {
*** 209,228 **** CertBag_OID = new ObjectIdentifier(certBag); SecretBag_OID = new ObjectIdentifier(secretBag); PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name); PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId); PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType); - pbeWithSHAAnd40BitRC2CBC_OID = - new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC); - pbeWithSHAAnd3KeyTripleDESCBC_OID = - new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC); pbes2_OID = new ObjectIdentifier(pbes2); TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage); AnyUsage = new ObjectIdentifier[]{ new ObjectIdentifier(AnyExtendedKeyUsage)}; } catch (IOException ioe) { ! // should not happen } } // A keystore entry and associated attributes private static class Entry { --- 206,221 ---- CertBag_OID = new ObjectIdentifier(certBag); SecretBag_OID = new ObjectIdentifier(secretBag); PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name); PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId); PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType); pbes2_OID = new ObjectIdentifier(pbes2); TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage); AnyUsage = new ObjectIdentifier[]{ new ObjectIdentifier(AnyExtendedKeyUsage)}; } catch (IOException ioe) { ! throw new AssertionError("OID not initialized", ioe); } } // A keystore entry and associated attributes private static class Entry {
*** 379,389 **** throw new IOException("Invalid PBE algorithm parameters"); } ic = pbeSpec.getIterationCount(); if (ic > MAX_ITERATION_COUNT) { ! throw new IOException("PBE iteration count too large"); } } else { ic = 0; } --- 372,382 ---- throw new IOException("Invalid PBE algorithm parameters"); } ic = pbeSpec.getIterationCount(); if (ic > MAX_ITERATION_COUNT) { ! throw new IOException("key PBE iteration count too large"); } } else { ic = 0; }
*** 415,425 **** Key tmp = kfac.generatePrivate(kspec); if (debug != null) { debug.println("Retrieved a protected private key at alias" + " '" + alias + "' (" + ! new AlgorithmId(algOid).getName() + " iterations: " + ic + ")"); } return tmp; // decode secret key } else { --- 408,418 ---- Key tmp = kfac.generatePrivate(kspec); if (debug != null) { debug.println("Retrieved a protected private key at alias" + " '" + alias + "' (" + ! mapPBEParamsToAlgorithm(algOid, algParams) + " iterations: " + ic + ")"); } return tmp; // decode secret key } else {
*** 440,450 **** } if (debug != null) { debug.println("Retrieved a protected secret key at alias " + "'" + alias + "' (" + ! new AlgorithmId(algOid).getName() + " iterations: " + ic + ")"); } return tmp; } }, password); --- 433,443 ---- } if (debug != null) { debug.println("Retrieved a protected secret key at alias " + "'" + alias + "' (" + ! mapPBEParamsToAlgorithm(algOid, algParams) + " iterations: " + ic + ")"); } return tmp; } }, password);
*** 689,699 **** entry.alias = alias.toLowerCase(Locale.ENGLISH); // add the entry entries.put(alias.toLowerCase(Locale.ENGLISH), entry); } catch (Exception nsae) { ! throw new KeyStoreException("Key protection " + " algorithm not found: " + nsae, nsae); } } /** --- 682,692 ---- entry.alias = alias.toLowerCase(Locale.ENGLISH); // add the entry entries.put(alias.toLowerCase(Locale.ENGLISH), entry); } catch (Exception nsae) { ! throw new KeyStoreException("Key protection" + " algorithm not found: " + nsae, nsae); } } /**
*** 786,803 **** } /* * Generate PBE Algorithm Parameters */ ! private AlgorithmParameters getPBEAlgorithmParameters(String algorithm) ! throws IOException ! { AlgorithmParameters algParams = null; // create PBE parameters from salt and iteration count PBEParameterSpec paramSpec = ! new PBEParameterSpec(getSalt(), PBE_ITERATION_COUNT); try { algParams = AlgorithmParameters.getInstance(algorithm); algParams.init(paramSpec); } catch (Exception e) { throw new IOException("getPBEAlgorithmParameters failed: " + --- 779,795 ---- } /* * Generate PBE Algorithm Parameters */ ! private AlgorithmParameters getPBEAlgorithmParameters( ! String algorithm, int iterationCount) throws IOException { AlgorithmParameters algParams = null; // create PBE parameters from salt and iteration count PBEParameterSpec paramSpec = ! new PBEParameterSpec(getSalt(), iterationCount); try { algParams = AlgorithmParameters.getInstance(algorithm); algParams.init(paramSpec); } catch (Exception e) { throw new IOException("getPBEAlgorithmParameters failed: " +
*** 856,872 **** } return skey; } /* ! * Encrypt private key using Password-based encryption (PBE) * as defined in PKCS#5. * * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is * used to derive the key and IV. * ! * @return encrypted private key encoded as EncryptedPrivateKeyInfo */ private byte[] encryptPrivateKey(byte[] data, KeyStore.PasswordProtection passwordProtection) throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException { --- 848,865 ---- } return skey; } /* ! * Encrypt private key or secret key using Password-based encryption (PBE) * as defined in PKCS#5. * * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is * used to derive the key and IV. * ! * @return encrypted private key or secret key encoded as ! * EncryptedPrivateKeyInfo */ private byte[] encryptPrivateKey(byte[] data, KeyStore.PasswordProtection passwordProtection) throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
*** 884,914 **** passwordProtection.getProtectionParameters(); if (algParamSpec != null) { algParams = AlgorithmParameters.getInstance(algorithm); algParams.init(algParamSpec); } else { ! algParams = getPBEAlgorithmParameters(algorithm); } } else { // Check default key protection algorithm for PKCS12 keystores ! algorithm = AccessController.doPrivileged( ! new PrivilegedAction<String>() { ! public String run() { ! String prop = ! Security.getProperty( ! KEY_PROTECTION_ALGORITHM[0]); ! if (prop == null) { ! prop = Security.getProperty( ! KEY_PROTECTION_ALGORITHM[1]); ! } ! return prop; ! } ! }); ! if (algorithm == null || algorithm.isEmpty()) { ! algorithm = "PBEWithSHA1AndDESede"; ! } ! algParams = getPBEAlgorithmParameters(algorithm); } ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm); if (pbeOID == null) { throw new IOException("PBE algorithm '" + algorithm + --- 877,894 ---- passwordProtection.getProtectionParameters(); if (algParamSpec != null) { algParams = AlgorithmParameters.getInstance(algorithm); algParams.init(algParamSpec); } else { ! algParams = getPBEAlgorithmParameters(algorithm, ! defaultKeyPbeIterationCount()); } } else { // Check default key protection algorithm for PKCS12 keystores ! algorithm = defaultKeyProtectionAlgorithm(); ! algParams = getPBEAlgorithmParameters(algorithm, ! defaultKeyPbeIterationCount()); } ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm); if (pbeOID == null) { throw new IOException("PBE algorithm '" + algorithm +
*** 962,972 **** AlgorithmParameters algParams) throws NoSuchAlgorithmException { // Check for PBES2 algorithms if (algorithm.equals((Object)pbes2_OID) && algParams != null) { return algParams.toString(); } ! return algorithm.toString(); } /** * Assigns the given certificate to the given alias. * --- 942,952 ---- AlgorithmParameters algParams) throws NoSuchAlgorithmException { // Check for PBES2 algorithms if (algorithm.equals((Object)pbes2_OID) && algParams != null) { return algParams.toString(); } ! return new AlgorithmId(algorithm).getName(); } /** * Assigns the given certificate to the given alias. *
*** 1187,1200 **** * the keystore data could not be stored */ public synchronized void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { - // password is mandatory when storing - if (password == null) { - throw new IllegalArgumentException("password can't be null"); - } // -- Create PFX DerOutputStream pfx = new DerOutputStream(); // PFX version (always write the latest version) --- 1167,1176 ----
*** 1223,1242 **** --- 1199,1230 ---- } // -- create EncryptedContentInfo if (certificateCount > 0) { + if (certProtectionAlgorithm == null) { + certProtectionAlgorithm = defaultCertProtectionAlgorithm(); + } + if (certPbeIterationCount < 0) { + certPbeIterationCount = defaultCertPbeIterationCount(); + } + if (debug != null) { debug.println("Storing " + certificateCount + " certificate(s) in a PKCS#7 encryptedData"); } byte[] encrData = createEncryptedData(password); + if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) { ContentInfo encrContentInfo = new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, new DerValue(encrData)); encrContentInfo.encode(authSafeContentInfo); + } else { + ContentInfo dataContentInfo = new ContentInfo(encrData); + dataContentInfo.encode(authSafeContentInfo); + } } // wrap as SequenceOf ContentInfos DerOutputStream cInfo = new DerOutputStream(); cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
*** 1247,1259 **** contentInfo.encode(authSafe); byte[] authSafeData = authSafe.toByteArray(); pfx.write(authSafeData); // -- MAC byte[] macData = calculateMac(password, authenticatedSafe); pfx.write(macData); ! // write PFX to output stream DerOutputStream pfxout = new DerOutputStream(); pfxout.write(DerValue.tag_Sequence, pfx); byte[] pfxData = pfxout.toByteArray(); stream.write(pfxData); --- 1235,1254 ---- contentInfo.encode(authSafe); byte[] authSafeData = authSafe.toByteArray(); pfx.write(authSafeData); // -- MAC + if (macAlgorithm == null) { + macAlgorithm = defaultMacAlgorithm(); + } + if (macIterationCount < 0) { + macIterationCount = defaultMacIterationCount(); + } + if (!macAlgorithm.equalsIgnoreCase("NONE")) { byte[] macData = calculateMac(password, authenticatedSafe); pfx.write(macData); ! } // write PFX to output stream DerOutputStream pfxout = new DerOutputStream(); pfxout.write(DerValue.tag_Sequence, pfx); byte[] pfxData = pfxout.toByteArray(); stream.write(pfxData);
*** 1460,1514 **** return entry.attributes; } /* - * Generate Hash. - */ - private byte[] generateHash(byte[] data) throws IOException - { - byte[] digest = null; - - try { - MessageDigest md = MessageDigest.getInstance("SHA1"); - md.update(data); - digest = md.digest(); - } catch (Exception e) { - throw new IOException("generateHash failed: " + e, e); - } - return digest; - } - - - /* * Calculate MAC using HMAC algorithm (required for password integrity) * * Hash-based MAC algorithm combines secret key with message digest to * create a message authentication code (MAC) */ private byte[] calculateMac(char[] passwd, byte[] data) throws IOException { byte[] mData = null; ! String algName = "SHA1"; try { // Generate a random salt. byte[] salt = getSalt(); // generate MAC (MAC key is generated within JCE) ! Mac m = Mac.getInstance("HmacPBESHA1"); PBEParameterSpec params = ! new PBEParameterSpec(salt, MAC_ITERATION_COUNT); SecretKey key = getPBEKey(passwd); m.init(key, params); m.update(data); byte[] macResult = m.doFinal(); // encode as MacData MacData macData = new MacData(algName, macResult, salt, ! MAC_ITERATION_COUNT); DerOutputStream bytes = new DerOutputStream(); bytes.write(macData.getEncoded()); mData = bytes.toByteArray(); } catch (Exception e) { throw new IOException("calculateMac failed: " + e, e); --- 1455,1491 ---- return entry.attributes; } /* * Calculate MAC using HMAC algorithm (required for password integrity) * * Hash-based MAC algorithm combines secret key with message digest to * create a message authentication code (MAC) */ private byte[] calculateMac(char[] passwd, byte[] data) throws IOException { byte[] mData = null; ! String algName = macAlgorithm.substring(7); try { // Generate a random salt. byte[] salt = getSalt(); // generate MAC (MAC key is generated within JCE) ! Mac m = Mac.getInstance(macAlgorithm); PBEParameterSpec params = ! new PBEParameterSpec(salt, macIterationCount); SecretKey key = getPBEKey(passwd); m.init(key, params); m.update(data); byte[] macResult = m.doFinal(); // encode as MacData MacData macData = new MacData(algName, macResult, salt, ! macIterationCount); DerOutputStream bytes = new DerOutputStream(); bytes.write(macData.getEncoded()); mData = bytes.toByteArray(); } catch (Exception e) { throw new IOException("calculateMac failed: " + e, e);
*** 1762,1780 **** --- 1739,1761 ---- DerOutputStream safeBagValue = new DerOutputStream(); safeBagValue.write(DerValue.tag_SequenceOf, out); byte[] safeBagData = safeBagValue.toByteArray(); // encrypt the content (EncryptedContentInfo) + if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) { byte[] encrContentInfo = encryptContent(safeBagData, password); // -- SEQUENCE of EncryptedData DerOutputStream encrData = new DerOutputStream(); DerOutputStream encrDataContent = new DerOutputStream(); encrData.putInteger(0); encrData.write(encrContentInfo); encrDataContent.write(DerValue.tag_Sequence, encrData); return encrDataContent.toByteArray(); + } else { + return safeBagData; + } } /* * Create SafeContent Data content type. * Includes encrypted secret key in a SafeBag of type SecretBag.
*** 1879,1929 **** private byte[] encryptContent(byte[] data, char[] password) throws IOException { byte[] encryptedData = null; // create AlgorithmParameters ! AlgorithmParameters algParams = ! getPBEAlgorithmParameters("PBEWithSHA1AndRC2_40"); DerOutputStream bytes = new DerOutputStream(); - AlgorithmId algId = - new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams); - algId.encode(bytes); - byte[] encodedAlgId = bytes.toByteArray(); - try { // Use JCE SecretKey skey = getPBEKey(password); ! Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40"); cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); encryptedData = cipher.doFinal(data); if (debug != null) { debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + ")"); } - } catch (Exception e) { - throw new IOException("Failed to encrypt" + - " safe contents entry: " + e, e); - } - // create EncryptedContentInfo DerOutputStream bytes2 = new DerOutputStream(); bytes2.putOID(ContentInfo.DATA_OID); bytes2.write(encodedAlgId); // Wrap encrypted data in a context-specific tag. DerOutputStream tmpout2 = new DerOutputStream(); tmpout2.putOctetString(encryptedData); bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ! false, (byte)0), tmpout2); // wrap EncryptedContentInfo in a Sequence DerOutputStream out = new DerOutputStream(); out.write(DerValue.tag_Sequence, bytes2); return out.toByteArray(); } /** * Loads the keystore from the given input stream. * --- 1860,1915 ---- private byte[] encryptContent(byte[] data, char[] password) throws IOException { byte[] encryptedData = null; + + try { // create AlgorithmParameters ! AlgorithmParameters algParams = getPBEAlgorithmParameters( ! certProtectionAlgorithm, certPbeIterationCount); DerOutputStream bytes = new DerOutputStream(); // Use JCE SecretKey skey = getPBEKey(password); ! Cipher cipher = Cipher.getInstance(certProtectionAlgorithm); cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); encryptedData = cipher.doFinal(data); + AlgorithmId algId = new AlgorithmId( + mapPBEAlgorithmToOID(certProtectionAlgorithm), + cipher.getParameters()); + // cipher.getParameters() now has IV + algId.encode(bytes); + byte[] encodedAlgId = bytes.toByteArray(); + if (debug != null) { debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + ")"); } // create EncryptedContentInfo DerOutputStream bytes2 = new DerOutputStream(); bytes2.putOID(ContentInfo.DATA_OID); bytes2.write(encodedAlgId); // Wrap encrypted data in a context-specific tag. DerOutputStream tmpout2 = new DerOutputStream(); tmpout2.putOctetString(encryptedData); bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, ! false, (byte) 0), tmpout2); // wrap EncryptedContentInfo in a Sequence DerOutputStream out = new DerOutputStream(); out.write(DerValue.tag_Sequence, bytes2); return out.toByteArray(); + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + throw new IOException("Failed to encrypt" + + " safe contents entry: " + e, e); + } } /** * Loads the keystore from the given input stream. *
*** 1942,1955 **** * keystore could not be loaded */ public synchronized void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { ! DataInputStream dis; ! CertificateFactory cf = null; ! ByteArrayInputStream bais = null; ! byte[] encoded = null; if (stream == null) return; // reset the counter --- 1928,1943 ---- * keystore could not be loaded */ public synchronized void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { ! ! // Reset config when loading a different keystore. ! certProtectionAlgorithm = null; ! certPbeIterationCount = -1; ! macAlgorithm = null; ! macIterationCount = -1; if (stream == null) return; // reset the counter
*** 1985,1994 **** --- 1973,1984 ---- // reset the counters at the start privateKeyCount = 0; secretKeyCount = 0; certificateCount = 0; + boolean seeEncBag = false; + /* * Spin over the ContentInfos. */ for (int i = 0; i < count; i++) { ContentInfo safeContents;
*** 2010,2019 **** --- 2000,2024 ---- if (debug != null) { debug.println("Warning: skipping PKCS#7 encryptedData" + " - no password was supplied"); } + // No password to decrypt ENCRYPTED_DATA_OID. *Skip it*. + // This means user will see a PrivateKeyEntry without + // certificates and a whole TrustedCertificateEntry will + // be lost. This is not a perfect solution but alternative + // solutions are more disruptive: + // + // We cannot just fail, since KeyStore.load(is, null) + // has been known to never fail because of a null password. + // + // We cannot just throw away the whole PrivateKeyEntry, + // this is too silent and no one will notice anything. + // + // We also cannot fail when getCertificate() on such a + // PrivateKeyEntry is called, since the method has not + // specified this behavior. continue; } DerInputStream edi = safeContents.getContent().toDerInputStream();
*** 2053,2069 **** "Invalid PBE algorithm parameters"); } ic = pbeSpec.getIterationCount(); if (ic > MAX_ITERATION_COUNT) { ! throw new IOException("PBE iteration count too large"); } } if (debug != null) { debug.println("Loading PKCS#7 encryptedData " + ! "(" + new AlgorithmId(algOid).getName() + " iterations: " + ic + ")"); } try { RetryWithZero.run(pass -> { --- 2058,2079 ---- "Invalid PBE algorithm parameters"); } ic = pbeSpec.getIterationCount(); if (ic > MAX_ITERATION_COUNT) { ! throw new IOException("cert PBE iteration count too large"); } + + certProtectionAlgorithm + = mapPBEParamsToAlgorithm(algOid, algParams); + certPbeIterationCount = ic; + seeEncBag = true; } if (debug != null) { debug.println("Loading PKCS#7 encryptedData " + ! "(" + mapPBEParamsToAlgorithm(algOid, algParams) + " iterations: " + ic + ")"); } try { RetryWithZero.run(pass -> {
*** 2084,2095 **** throw new IOException("public key protected PKCS12" + " not supported"); } } // The MacData is optional. ! if (password != null && s.available() > 0) { MacData macData = new MacData(s); int ic = macData.getIterations(); try { if (ic > MAX_ITERATION_COUNT) { --- 2094,2113 ---- throw new IOException("public key protected PKCS12" + " not supported"); } } + // No ENCRYPTED_DATA_OID but see certificate. Must be passwordless. + if (!seeEncBag && certificateCount > 0) { + certProtectionAlgorithm = "NONE"; + } + // The MacData is optional. ! if (s.available() > 0) { ! // If there is no password, we cannot fail. KeyStore.load(is, null) ! // has been known to never fail because of a null password. ! if (password != null) { MacData macData = new MacData(s); int ic = macData.getIterations(); try { if (ic > MAX_ITERATION_COUNT) {
*** 2101,2112 **** macData.getDigestAlgName().toUpperCase(Locale.ENGLISH); // Change SHA-1 to SHA1 algName = algName.replace("-", ""); // generate MAC (MAC key is created within JCE) ! Mac m = Mac.getInstance("HmacPBE" + algName); PBEParameterSpec params = new PBEParameterSpec(macData.getSalt(), ic); RetryWithZero.run(pass -> { SecretKey key = getPBEKey(pass); --- 2119,2133 ---- macData.getDigestAlgName().toUpperCase(Locale.ENGLISH); // Change SHA-1 to SHA1 algName = algName.replace("-", ""); + macAlgorithm = "HmacPBE" + algName; + macIterationCount = ic; + // generate MAC (MAC key is created within JCE) ! Mac m = Mac.getInstance(macAlgorithm); PBEParameterSpec params = new PBEParameterSpec(macData.getSalt(), ic); RetryWithZero.run(pass -> { SecretKey key = getPBEKey(pass);
*** 2121,2136 **** if (!MessageDigest.isEqual(macData.getDigest(), macResult)) { throw new UnrecoverableKeyException("Failed PKCS12" + " integrity checking"); } ! return (Void)null; }, password); } catch (Exception e) { throw new IOException("Integrity check failed: " + e, e); } } /* * Match up private keys with certificate chains. */ PrivateKeyEntry[] list = --- 2142,2160 ---- if (!MessageDigest.isEqual(macData.getDigest(), macResult)) { throw new UnrecoverableKeyException("Failed PKCS12" + " integrity checking"); } ! return (Void) null; }, password); } catch (Exception e) { throw new IOException("Integrity check failed: " + e, e); } } + } else { + macAlgorithm = "NONE"; + } /* * Match up private keys with certificate chains. */ PrivateKeyEntry[] list =
*** 2165,2176 **** break; } cert = certsMap.get(issuerDN); } /* Update existing KeyEntry in entries table */ ! if (chain.size() > 0) entry.chain = chain.toArray(new Certificate[chain.size()]); } } if (debug != null) { debug.println("PKCS12KeyStore load: private key count: " + --- 2189,2206 ---- break; } cert = certsMap.get(issuerDN); } /* Update existing KeyEntry in entries table */ ! if (chain.size() > 0) { entry.chain = chain.toArray(new Certificate[chain.size()]); + } else { + // Remove private key entries where there is no associated + // certs. Most likely the keystore is loaded with a null + // password. + entries.remove(entry); + } } } if (debug != null) { debug.println("PKCS12KeyStore load: private key count: " +
*** 2182,2191 **** --- 2212,2261 ---- certsMap.clear(); keyList.clear(); } /** + * Returns if a pkcs12 file is password-less. This means no cert is + * encrypted and there is no Mac. Please note that the private key + * can be encrypted. + * + * This is a simplified version of {@link #engineLoad} that only looks + * at the ContentInfo types. + * + * @param f the pkcs12 file + * @return if it's password-less + * @throws IOException + */ + public static boolean isPasswordless(File f) throws IOException { + + try (FileInputStream stream = new FileInputStream(f)) { + DerValue val = new DerValue(stream); + DerInputStream s = val.toDerInputStream(); + + s.getInteger(); // skip version + + ContentInfo authSafe = new ContentInfo(s); + DerInputStream as = new DerInputStream(authSafe.getData()); + for (DerValue seq : as.getSequence(2)) { + DerInputStream sci = new DerInputStream(seq.toByteArray()); + ContentInfo safeContents = new ContentInfo(sci); + if (safeContents.getContentType() + .equals(ContentInfo.ENCRYPTED_DATA_OID)) { + // Certificate encrypted + return false; + } + } + + if (s.available() > 0) { + // The MacData exists. + return false; + } + } + return true; + } + + /** * Locates a matched CertEntry from certEntries, and returns its cert. * @param entry the KeyEntry to match * @return a certificate, null if not found */ private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
*** 2337,2357 **** * null keyId, we should skip it entirely. */ if (bagItem instanceof KeyEntry) { KeyEntry entry = (KeyEntry)bagItem; - if (bagItem instanceof PrivateKeyEntry) { if (keyId == null) { // Insert a localKeyID for the privateKey // Note: This is a workaround to allow null localKeyID // attribute in pkcs12 with one private key entry and // associated cert-chain if (privateKeyCount == 1) { keyId = "01".getBytes("UTF8"); } else { continue; } } } entry.keyId = keyId; // restore date if it exists String keyIdStr = new String(keyId, "UTF8"); --- 2407,2430 ---- * null keyId, we should skip it entirely. */ if (bagItem instanceof KeyEntry) { KeyEntry entry = (KeyEntry)bagItem; if (keyId == null) { + if (bagItem instanceof PrivateKeyEntry) { // Insert a localKeyID for the privateKey // Note: This is a workaround to allow null localKeyID // attribute in pkcs12 with one private key entry and // associated cert-chain if (privateKeyCount == 1) { keyId = "01".getBytes("UTF8"); } else { continue; } + } else { + // keyId in a SecretKeyEntry is not significant + keyId = "00".getBytes("UTF8"); } } entry.keyId = keyId; // restore date if it exists String keyIdStr = new String(keyId, "UTF8");
*** 2418,2423 **** --- 2491,2575 ---- private String getUnfriendlyName() { counter++; return (String.valueOf(counter)); } + + // 8076190: Customizing the generation of a PKCS12 keystore + + private static String defaultCertProtectionAlgorithm() { + String result = SecurityProperties.privilegedGetOverridable( + "keystore.pkcs12.certProtectionAlgorithm"); + return (result != null && !result.isEmpty()) + ? result : "PBEWithSHA1AndRC2_40"; + } + + private static int defaultCertPbeIterationCount() { + String result = SecurityProperties.privilegedGetOverridable( + "keystore.pkcs12.certPbeIterationCount"); + return (result != null && !result.isEmpty()) + ? string2IC("certPbeIterationCount", result) : 50000; + } + + // Read both "keystore.pkcs12.keyProtectionAlgorithm" and + // "keystore.PKCS12.keyProtectionAlgorithm" for compatibility. + private static String defaultKeyProtectionAlgorithm() { + String result = AccessController.doPrivileged(new PrivilegedAction<String>() { + public String run() { + String result; + String name1 = "keystore.pkcs12.keyProtectionAlgorithm"; + String name2 = "keystore.PKCS12.keyProtectionAlgorithm"; + result = System.getProperty(name1); + if (result != null) { + return result; + } + result = System.getProperty(name2); + if (result != null) { + return result; + } + result = Security.getProperty(name1); + if (result != null) { + return result; + } + return Security.getProperty(name2); + } + }); + return (result != null && !result.isEmpty()) + ? result : "PBEWithSHA1AndDESede"; + } + + private static int defaultKeyPbeIterationCount() { + String result = SecurityProperties.privilegedGetOverridable( + "keystore.pkcs12.keyPbeIterationCount"); + return (result != null && !result.isEmpty()) + ? string2IC("keyPbeIterationCount", result) : 50000; + } + + private static String defaultMacAlgorithm() { + String result = SecurityProperties.privilegedGetOverridable( + "keystore.pkcs12.macAlgorithm"); + return (result != null && !result.isEmpty()) + ? result : "HmacPBESHA1"; + } + + private static int defaultMacIterationCount() { + String result = SecurityProperties.privilegedGetOverridable( + "keystore.pkcs12.macIterationCount"); + return (result != null && !result.isEmpty()) + ? string2IC("macIterationCount", result) : 100000; + } + + private static int string2IC(String type, String value) { + int number; + try { + number = Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("keystore.pkcs12." + type + + " is not a number: " + value); + } + if (number <= 0 || number > MAX_ITERATION_COUNT) { + throw new IllegalArgumentException("Invalid keystore.pkcs12." + + type + ": " + value); + } + return number; + } }
< prev index next >