Summary
-------
Define some system/Security properties to determine what parameters should be used when generating a PKCS12 keystore.
Problem
-------
1. JDK's PKCS 12 keystore implementation hardcodes PBE (password based encryption) algorithms and other parameters.
2. JDK's PKCS 12 keystore, the current default keystore type, always requires a password to access the certificate stored inside it. This is not the same as the previously default keystore type JKS, where the password is only used for integrity check. This inconsistency is breaking some existing applications where no password is given.
Solution
--------
See spec.
We would also like to take this chance to change the name of a security property from `keystore.PKCS12.keyProtectionAlgorithm` to `keystore.pkcs12.keyProtectionAlgorithm` described in `KeyStore.java`. Traditionally, lowercase characters are used in security property names. Also, there is no behavior change for this update, since the implementation is already reading names in both lowercase and uppercase (in this order).
keytool will be updated to recognize the password-less pkcs12 keystore format (i.e. both certProtectionAlgorithm and macAlgorithm being NONE), where it should not prompt for store password when creating or reading such a keystore.
Specification
-------------
(Note that the only SE-specific change is the addition of the new HmacPBE algorithm names to the Standard Algorithm Names Specification. The rest of the changes are JDK-specific.)
1) Add the following lines into `conf/security/java.security`:
#
# PKCS12 KeyStore properties
#
# The following properties, if configured, are used by the PKCS12 KeyStore
# implementation during the creation of a new keystore. Several of the
# properties may also be used when modifying an existing keystore. The
# properties can be overridden by a KeyStore API that specifies its own
# algorithms and parameters.
#
# If an existing PKCS12 keystore is loaded and then stored, the algorithm and
# parameter used to generate the existing Mac will be reused. If the existing
# keystore does not have a Mac, no Mac will be created while storing. If there
# is at least one certificate in the existing keystore, the algorithm and
# parameter used to encrypt the last certificate in the existing keystore will
# be reused to encrypt all certificates while storing. If the last certificate
# in the existing keystore is not encrypted, all certificates will be stored
# unencrypted. If there is no certificate in the existing keystore, any newly
# added certificate will be encrypted (or stored unencrypted if algorithm
# value is "NONE") using the "keystore.pkcs12.certProtectionAlgorithm" and
# "keystore.pkcs12.certPbeIterationCount" values defined here. Existing private
# and secret key(s) are not changed. Newly set private and secret key(s) will
# be encrypted using the "keystore.pkcs12.keyProtectionAlgorithm" and
# "keystore.pkcs12.keyPbeIterationCount" values defined here.
#
# In order to apply new algorithms and parameters to all entries in an
# existing keystore, one can create a new keystore and add entries in the
# existing keystore into the new keystore. This can be achieved by calling the
# "keytool -importkeystore" command.
#
# If a system property of the same name is also specified, it supersedes the
# security property value defined here.
#
# If the property is set to an illegal value, for example,
# an iteration count that is not a positive integer, or an unknown algorithm
# name, an exception will be thrown when the property is used.
# If the property is not set or empty, a default value will be used.
#
# Note: These properties are currently used by the JDK Reference implementation.
# They are not guaranteed to be examined and used by other implementations.
# The algorithm used to encrypt a certificate. This can be any non-Hmac PBE
# algorithm defined in the Cipher section of the Java Security Standard
# Algorithm Names Specification. When set to "NONE", the certificate
# is not encrypted. The default value is "PBEWithSHA1AndRC2_40".
#keystore.pkcs12.certProtectionAlgorithm = PBEWithSHA1AndRC2_40
# The iteration count used by the PBE algorithm when encrypting a certificate.
# This value must be a positive integer. The default value is 50000.
#keystore.pkcs12.certPbeIterationCount = 50000
# The algorithm used to encrypt a private key or secret key. This can be
# any non-Hmac PBE algorithm defined in the Cipher section of the Java
# Security Standard Algorithm Names Specification. The value must not be "NONE".
# The default value is "PBEWithSHA1AndDESede".
#keystore.pkcs12.keyProtectionAlgorithm = PBEWithSHA1AndDESede
# The iteration count used by the PBE algorithm when encrypting a private key
# or a secret key. This value must be a positive integer. The default value
# is 50000.
#keystore.pkcs12.keyPbeIterationCount = 50000
# The algorithm used to calculate the optional MacData at the end of a PKCS12
# file. This can be any HmacPBE algorithm defined in the Mac section of the
# Java Security Standard Algorithm Names Specification. When set to "NONE",
# no Mac is generated. The default value is "HmacPBESHA1".
#keystore.pkcs12.macAlgorithm = HmacPBESHA1
# The iteration count used by the MacData algorithm. This value must be a
# positive integer. The default value is 100000.
#keystore.pkcs12.macIterationCount = 100000
Below are some explanations on why existing encrypted private keys retain their different algorithms but the encrypted certificates must use the same algorithm:
The `KeyStore` class is designed to be protected by a single store password and multiple key passwords. Once a keystore is loaded (after providing the store password), all certificates are in clear text, while reading each key in a key entry needs an individual key password. This means we must encrypt all certificates using the same store password. If different algorithms are used to encrypt different certificates, an attacker can decrypt the one using the weakest algorithm and then gain access to others using stronger algorithms.
In fact, storing certificates using the same algorithm is doable since they are already decrypted after loading and the `store` method has an argument for the new store password. On the other hand, one can only call `getKey(alias, password)` to get a decrypted key and the key is still stored encrypted inside a `KeyStore` object, and there is no way to re-encrypt all keys with a single algorithm when storing them.
2) Add several new Mac algorithms to the `SunJCE` provider:
HmacPBESHA1
HmacPBESHA224
HmacPBESHA256
HmacPBESHA384
HmacPBESHA512
HmacPBESHA512/224
HmacPBESHA512/256
They will be added to the Mac section of the Java Security Standard Algorithm Names Specification, and the SunJCE/Mac section of the JDK Providers Documentation.
3) Make some changes to `java.security.KeyStore$PasswordProtection` in `KeyStore.java`. First, the security properties were never meant to be a part of the Java SE spec and we do not require all third-party providers to implement them. These sentences should have been put under an `@implNote` tag. Second, we have actually never implemented the `keystore.<type>.keyProtectionAlgorithm` properties for other keystore types like JKS and JCEKS. Since the settings for PKCS12 have been expanded this time and well-documented inside `java.security`, we can simply remove the sentences here.
/**
* Gets the name of the protection algorithm.
* If none was set then the keystore provider will use its default
- * protection algorithm. The name of the default protection algorithm
- * for a given keystore type is set using the
- * {@code 'keystore.<type>.keyProtectionAlgorithm'} security property.
- * For example, the
- * {@code keystore.PKCS12.keyProtectionAlgorithm} property stores the
- * name of the default key protection algorithm used for PKCS12
- * keystores. If the security property is not set, an
- * implementation-specific algorithm will be used.
+ * protection algorithm.
*
* @return the algorithm name, or {@code null} if none was set
*
* @since 1.8
*/
public String getProtectionAlgorithm()