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. 
In p
 articular, 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 #⁠11 level was simple: 
key algorithm -> PKCS #⁠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 #⁠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 differe
 nt 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 #⁠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 #⁠11 API in the expected encoding. Password encoding must be UTF-8 
for PKCS #⁠5 v2.1 derivation and BMPString (UTF-16 big endian) for PKCS 
#⁠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_PK
 CS5_PBKD2_HMAC_SHA1``` constant.
    * As mentioned in 
[JDK-8301553](https://bugs.openjdk.org/browse/JDK-8301553), for some algorithms 
there isn't a PKCS #⁠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 
#⁠5 derivation for Mac (PBMAC1) might be considered in the future as a 
PKCS #⁠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 #⁠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 #⁠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 #⁠11 
token in the PKCS #⁠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 #⁠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 #⁠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 #⁠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.

-------------

Commit messages:
 - 8301553: Support Password-Based Cryptography in SunPKCS11

Changes: https://git.openjdk.org/jdk/pull/12396/files
 Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=12396&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8301553
  Stats: 2935 lines in 28 files changed: 2541 ins; 254 del; 140 mod
  Patch: https://git.openjdk.org/jdk/pull/12396.diff
  Fetch: git fetch https://git.openjdk.org/jdk pull/12396/head:pull/12396

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

Reply via email to