RFR: 8301553: Support Password-Based Cryptography in SunPKCS11

Martin Balao mbalao at openjdk.org
Sat Mar 18 06:11:23 UTC 2023

On Wed, 15 Mar 2023 00:32:09 GMT, Valerie Peng <valeriep at openjdk.org> wrote:

>> We would like to propose an implementation for the [JDK-8301553: Support Password-Based Cryptography in SunPKCS11](https://bugs.openjdk.org/browse/JDK-8301553) enhancement requirement.
>> In addition to pursuing the requirement goals and guidelines of [JDK-8301553](https://bugs.openjdk.org/browse/JDK-8301553), we want to share the following implementation notes (grouped per altered file):
>>   * ```src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java``` (modified)
>>     * This file contains the ```SunJCE``` implementation for the [PKCS #12 General Method for Password Integrity](https://datatracker.ietf.org/doc/html/rfc7292#appendix-B) algorithms. It has been modified with the intent of consolidating all parameter checks in a common file (```src/java.base/share/classes/sun/security/util/PBEUtil.java```), that can be used both by ```SunJCE``` and ```SunPKCS11```. This change does not only serve the purpose of avoiding duplicated code but also ensuring alignment and compatibility between different implementations of the same algorithms. No changes have been made to parameter checks themselves.
>>     * The new ```PBEUtil::getPBAKeySpec``` method introduced for parameters checking takes both a ```Key``` and a ```AlgorithmParameterSpec``` instance (same as the ```HmacPKCS12PBECore::engineInit``` method), and returns a ```PBEKeySpec``` instance which consolidates all the data later required to proceed with the computation (password, salt and iteration count).
>>   * ```src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java``` (modified)
>>     * This file contains the ```SunJCE``` implementation for the [PKCS #5 Password-Based Encryption Scheme](https://datatracker.ietf.org/doc/html/rfc8018#section-6.2) algorithms, which use PBKD2 algorithms underneath for key derivation. In the same spirit than for the ```HmacPKCS12PBECore``` case, we decided to consolidate common code for parameters validation and default values in a single file (```src/java.base/share/classes/sun/security/util/PBEUtil.java```), that can serve both ```SunJCE``` and ```SunPKCS11``` and ensure compatibility. However, instead of a single static method at the implementation level (see ```PBEUtil::getPBAKeySpec```), we create an instance of an auxiliary class and invoke an instance method (```PBEUtil.PBES2Params::getPBEKeySpec```). The reason is to persist parameters data that has to be consistent between calls to ```PBES2Core::engineInit``` (in its multiple overloads) and ```PBES2Core::engineGetParameters```, given a single ```PBES2Core``` instance. I
 n particular, a call to any of these methods can potentially modify the state in an observable way by means of generating a random IV and a salt. Previous to the proposed patch, this data was persisted in the ```PBES2Core::ivSpec``` and ```PBES2Core::salt``` instance fields. For compatibility purposes, we decided to preserve ```SunJCE```'s current behavior.
>>   * ```src/java.base/share/classes/sun/security/util/PBEUtil.java``` (new file)
>>     * This utility file contains the PBE parameters checking routines and default values that are used by both ```SunJCE``` and ```SunPKCS11```. These routines are invoked from ```HmacPKCS12PBECore``` (```SunJCE```), ```PBES2Core``` (```SunJCE```), ```P11PBECipher``` (```SunPKCS11```) and ```P11Mac``` (```SunPKCS11```). As previously noted, the goals are to avoid duplicate code and to improve compatibility between different security providers that implement PBE algorithms.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java``` (modified)
>>     * An utility function to determine if the token is NSS is now called. This function is in a common utility class (```P11Util```) and invoked from ```P11Key``` and ```P11SecretKeyFactory``` too.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java``` (modified)
>>     * A new type of P11 key is introduced: ```P11PBEKey```. This new type represents a secret key that exists inside the token. Thus, this type inherits from ```P11SecretKey```. At the same time, this type holds data used for key derivation. Thus, this type implements the ```javax.crypto.interfaces.PBEKey``` interface. In addition to the conceptual modeling, there are practical advantages of identifying a key by this new ```P11PBEKey``` type and holding the data used for derivation: 1) if the key is used in another token (different than the one where it was originally derived), a new derivation must take place; 2) if the key is passed to a non-```SunPKCS11``` security provider, its key translation method might use derivation data to derive again; and, 3) it's possible to return the ```PBEKeySpec``` for the key (see for example ```P11SecretKeyFactory::engineGetKeySpec```).
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java``` (modified)
>>     * We decided to integrate PBE algorithms to the existing ```P11Mac``` service because the changes required have a low impact on the existing code. When the ```P11Mac``` instance is created, we use the algorithm to get PBE key information (if available). Only PBE algorithms have this type of information. In the ```P11Mac::engineInit``` method, we now need to handle the PBE service case. In such case, if the key is a ```P11Key```, we check parameters and that the key implements ```javax.crypto.interfaces.PBEKey``` by calling ```PBEUtil::checkKeyParams```. In other words, the key has to be a ```P11PBEKey``` and the parameters used for its derivation must match the ones passed in the invocation to ```P11Mac::engineInit```. If the key is not a ```P11Key```, a PBE derivation is needed. As for the ```SunJCE``` case, we go through parameters processing in ```PBEUtil::getPBAKeySpec```.
>>     * There are two cases in which we need to call ```P11SecretKeyFactory::convertKey```. One is when the service is not PBE, as we did before the proposed change. In the PBE case, we must call this function because it might be possible that, if the key token is not the same than the service's token, a new key derivation is required.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java``` (new file)
>>     * Contrary to the ```P11Mac``` case, we decided to separate PBE ```Cipher``` from non-PBE ```Cipher``` in a different class. There is some additional complexity or gap between the two that we prefer to keep simple. A PBE ```Cipher``` uses a non-PBE ```Cipher``` service underneath and forwards most of its operations, but adds wrapping code to potentially derive keys during initialization (see ```P11PBECipher::engineInit```). The code associated to key derivation and parameters consistency checking is analogous to the one described for ```P11Mac```.
>>     * ```P11PBECipher``` has a ```P11PBECipher::engineGetParameters``` method which calls ```PBEUtil.PBES2Params::getAlgorithmParameters``` and can potentially initialize an IV and a salt with a random value, as explained in the comments for ```PBES2Core```.
>>     * A ```P11PBECipher``` service accepts PBE keys only.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java``` (modified)
>>     * The first significant change to this class that we want to discuss is the introduction of the ```KeyInfo``` class and the refactoring of the previous ```keyTypes``` map. Previous to the proposed change, the key information that we needed to retain for key creation at the PKCS #&#x2060;11 level was simple: key algorithm -> PKCS #&#x2060;11 native key type. With PBE, we must consider not only the algorithm name and key type but also (depending on the case) the mechanism that has to be used for derivation, the underlying derivation function and the key length. As an example, to derive a key for the PBEWithHmacSHA512AndAES_256 algorithm, we need to know that this algorithm maps to a PKCS #&#x2060;11 derivation mechanism value of ```CKM_PKCS5_PBKD2```, a derivation function value of ```CKP_PKCS5_PBKD2_HMAC_SHA512```, a derived key type of ```CKK_AES``` —so it can be used in an AES Cipher service— and a key length of 256 bits. A new hierarchy of classes to represent these diff
 erent entries on the mapping structure has been introduced (see ```KeyInfo```, ```PBEKeyInfo```, ```AESPBEKeyInfo```, ```PBKDF2KeyInfo``` and ```P12MacPBEKeyInfo```). The methods to add or find entries in the new map have been adjusted. The previous pseudo key types strategy (```HMAC```, ```SSLMAC```), that allows any key type to be used in a HMAC service, has not been modified.
>>     * The second significant change to this class was in the ```P11SecretKeyFactory::convertKey``` method. When checking if a key can be used in a service —notice here that the service can be any of ```SecretKeyFactory```, ```Cipher``` or ```Mac```—, the following rules apply:
>>       * If the key algorithm matches the service algorithm, the use is allowed
>>       * If the key algorithm does not match the service algorithm and the service is not one of the pseudo types, further checks are needed. The ```KeyInfo``` structure for the key and service algorithms are obtained from the map and the ```KeyInfo::checkUse``` method is invoked. The following principles apply to make a decision: 1) PBE services require a ```javax.crypto.interfaces.PBEKey``` of the same algorithm —we cannot use an AES key, for example, in a PBEWithHmacSHA512AndAES_256 ```Cipher``` service—, 2) PBKD2 keys can be used on any service —there is no information about the key purpose to make a decision— and 3) keys can be used in a service if their underlying type match —as an example, a PBEWithHmacSHA512AndAES_256 PBE key has an underlying type of ```CKK_AES``` and can be used in an AES ```Cipher``` service—.
>>     * The third significant change to this class was the addition of the ```P11SecretKeyFactory::derivePBEKey``` function. This function does different checks and creates the PKCS #&#x2060;11 structures to make a native call to ```C_GenerateKey``` to derive the key. They key returned, in case of success, is of ```P11PBEKey``` type. It is worth mentioning that this function is the only path to invoke a native key derivation. It's used not only by the PBE ```P11SecretKeyFactory``` service but also by PBE ```Cipher``` and PBE ```Mac``` services when they need to derive a key.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java``` (modified)
>>     * Added some utility functions. One of them is ```P11Util::encodePassword``` which serves the purpose of encoding a password in a way that can go through native library truncation (OpenJDK) and reach the PKCS #&#x2060;11 API in the expected encoding. Password encoding must be UTF-8 for PKCS #&#x2060;5 v2.1 derivation and BMPString (UTF-16 big endian) for PKCS #&#x2060;12 v1.1 General Method for Password Integrity derivation. By avoiding a modification to the existing native ```jCharArrayToCKUTF8CharArray``` function (```p11_util.c```), we reduce the risk of breaking existing code.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java``` (modified)
>>     * The new PBE algorithms are registered for ```SunPKCS11``` services. It's worth noting that there is an additional (and optional) ```requiredMechs``` array to specify a list of native mechanisms that must be available in the token for the service to be enabled. To explain the need for this structure, we will focus on the HmacPBESHA1 ```Mac``` service example. On the one hand, we require the ```CKM_SHA_1_HMAC``` mechanism to be available in the token because this is, ultimately, a SHA-1 HMAC operation. However, the ```CKM_PBA_SHA1_WITH_SHA1_HMAC``` mechanism must be available as well for the preceding PBE key derivation. In the PBKDF2 case, we leverage on this structure to require a mechanism associated to the derivation function. The assumption is that, for example, if the ```CKM_PKCS5_PBKD2``` and ```CKM_SHA_1_HMAC``` mechanisms are available, a PBKDF2 derivation using HMAC SHA-1 underneath will be available. In this case, the derivation function is represented by the ```CKP
 _PKCS5_PBKD2_HMAC_SHA1``` constant.
>>     * As mentioned in [JDK-8301553](https://bugs.openjdk.org/browse/JDK-8301553), for some algorithms there isn't a PKCS #&#x2060;11 constant and we use the NSS vendor-specific one. This code can be easily be updated in the future if a constant is introduced to the standard. We don't know if that will ever happen as the newer PKCS #&#x2060;5 derivation for Mac (PBMAC1) might be considered in the future as a PKCS #&#x2060;12 integrity scheme replacement.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java``` (modified)
>>     * Minor comment fix. This mistake was probably the result of using the ```CK_PKCS5_PBKD2_PARAMS``` file as a template and forgetting to update the comment.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java``` (modified)
>>     * New constructors for the ```CK_PBE_PARAMS```, ```CK_PKCS5_PBKD2_PARAMS``` and ```CK_PKCS5_PBKD2_PARAMS2``` structures that can be used along with a ```CK_MECHANISM```.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java``` (modified)
>>     * Some minor adjustments to comments and a constructor to make this class usable with the PKCS #&#x2060;12 General Method for Password Integrity derivation.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java``` (modified)
>>     * Same than for ```CK_PBE_PARAMS```. It is worth noting that this structure is the one used in PKCS #&#x2060;11 revisions previous to v2.40 Errata 01. Given that NSS has decided to keep using it —even when it's not compliant with the latest revisions of the v2.40 and v3.0 standards—, we make an exception for it.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2.java``` (new file)
>>     * The new structure for passing PBE parameters to the PKCS #&#x2060;11 token in the PKCS #&#x2060;5 v2.1 derivation scheme.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java``` (modified)
>>     * Same comment than for ```CK_ECDH1_DERIVE_PARAMS```.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java``` (modified)
>>     * More visibility of major and minor versions of the PKCS #&#x2060;11 standard implemented by a token is needed to decide between the ```CK_PKCS5_PBKD2_PARAMS``` and ```CK_PKCS5_PBKD2_PARAMS2``` structures.
>>   * ```src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java``` (modified)
>>     * New constants added.
>>   * ```src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c``` (modified)
>>     * Adjustments made to work with the structures to pass parameters to the token for PBE derivation. It's worth noting that native PBKD2 parameter structures have a tag before the data so we can execute the correct logic to free up resources, once the operation is completed. This is how we differentiate a ```CK_PKCS5_PBKD2_PARAMS``` from a ```CK_PKCS5_PBKD2_PARAMS2``` one.
>>   * ```src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c``` (modified)
>>     * Adjustments to work with the new PBE parameter structures.
>>     * A bug affecting non-null Java arrays whose length is 0 and need to be converted to native PKCS #&#x2060;11 arrays has been fixed. For these arrays, it was possible that some platforms return ```NULL``` as a result of calling memory allocation functions when the size was 0 and an ```OutOfMemory``` exception was incorrectly thrown.
>>   * ```src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h``` (modified)
>>     * Native constants and structures added.
>> Test files
>>   * ```test/jdk/sun/security/pkcs11/Cipher/PBECipher.java``` (new file)
>>     * Tests the PBE ```Cipher``` service in ```SunPKCS11```, cross-comparing results against ```SunJCE``` (if available) and static data.
>>     * The PBE ```Cipher``` service is tested with different types of keys: derived from data in a ```PBEParameterSpec```, derived with a ```SunPKCS11``` ```SecretKeyFactory``` service, derived from data in ```AlgorithmParameters``` and derived from data contained in a ```javax.crypto.interfaces.PBEKey``` instance.
>>   * ```test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java``` (new file)
>>     * Tests that, for several PBE algorithms, PKCS #&#x2060;12 key stores (with Privacy and Integrity) written using ```SunPKCS11``` underneath can be read using ```SunJCE``` underneath.
>>   * ```test/jdk/sun/security/pkcs11/Mac/MacSameTest.java``` (modified)
>>     * This test was not expecting PBE services to be available in the ```SunPKCS11``` security provider, and needs to invoke a new function (```PKCS11Test::generateKey```) to generate a random key (password).
>>   * ```test/jdk/sun/security/pkcs11/Mac/PBAMac.java``` (new file)
>>     * Similar to ```PBECipher``` but these are the possible types of keys: derived from data in a ```PBEParameterSpec```, derived with a ```SunPKCS11``` ```SecretKeyFactory``` service and derived from data contained in a ```javax.crypto.interfaces.PBEKey``` instance.
>>   * ```test/jdk/sun/security/pkcs11/Mac/ReinitMac.java``` (modified)
>>     * Same issue fixed than for ```MacSameTest```.
>>   * ```test/jdk/sun/security/pkcs11/PKCS11Test.java``` (modified)
>>     * Functions to generate random keys or passwords for PBE and non-PBE algorithms.
>>   * ```test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java``` (new file)
>>     * In addition to testing derived keys for different algorithms against ```SunJCE``` and static assertion data, this test asserts: 1) different types of valid and invalid key conversions, and 2) invalid or inconsistent parameters passed for key derivation. Keys are derived with data contained in a ```PBEKeySpec``` or in a ```javax.crypto.interfaces.PBEKey``` instance.
>>     * Both an empty and a unicode password, containing a non-ASCII character, are used during this test.
>> Testing
>>   * No regressions have been observed in the ```jdk/sun/security/pkcs11``` category (```SunPKCS11```).
>>   * No regressions have been observed in the ```jdk/com/sun/crypto/provider``` category (```SunJCE```).
>>   * No regressions have been observed in the JDK Tier-1 category.
>>   * Anecdotally, a partial version of the proposed patch containing ```Cipher``` and ```Mac``` changes is shipped in Red Hat Enterprise Linux builds of OpenJDK 17 since November 2022, without any known issues at this moment.
>> This contribution is co-authored between @franferrax and @martinuy. We are both under the cover of the OCA agreement per our employer (Red Hat). We look forward to sharing this new feature for the benefit of the broad OpenJDK community and users.
> src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java line 118:
>> 116:                 return true;
>> 117:             }
>> 118:             return ki.keyType == si.keyType;
> So, for non-PBE key info, algos do not have to match?

For services and keys cases in which algorithms identity-match —irrespective if they are PBE or non-PBE—, KeyInfo::checkUse is not called and execution moves forward as if the check passed (see [here](https://github.com/openjdk/jdk/blob/ab7ffd56bb8b93d513023d0136df55a6375c3286/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java#L285)). The same is true for services that accept any key type, such as those whose pseudo types are PCKK_HMAC or PCKK_SSLMAC.

The ki.keyType == si.keyType success value affects cases in which algorithms are different but it's still possible to use the key in the service. One example that would hit this path is a PBE key derived for AES that it's used in an AES Cipher service. For non-PBE keys and services cases, one example is algorithms "RC4" and "ARCFOUR" that have both the underlying CKK_RC4 key type. Notice that this latter case is not new: previous to this enhancement proposal, key types were compared as well (see [here](https://github.com/openjdk/jdk/blob/jdk-21%2B14/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java#L147)).

For non-PBE keys and services cases, what is new with this enhancement is to accept them if their algorithms are identity-equal. This condition necessarily means that key types are equal —the opposite is obviously not true—. One minor detail, when we refer to the algorithms equality trivial pass condition, it's an object identity comparison for performance.


PR: https://git.openjdk.org/jdk/pull/12396

More information about the security-dev mailing list