RFR: 8301553: Support Password-Based Cryptography in SunPKCS11

Francisco Ferrari Bihurriet fferrari at redhat.com
Wed Feb 8 00:23:39 UTC 2023


Hi, here is a more plaintext-friendly version of the pull request
Markdown body.

NOTE: I did manual wrapping to get a reasonably good representation in
the archived version of this email [*], sorry if you are reading this in
a <72 characters screen. On the other hand, not wrapping at all would
lead to very long lines in wider screens.

[*]: https://mail.openjdk.org/pipermail/security-dev/2023-February/034438.html

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

We would like to propose an implementation for the JDK-8301553: Support
Password-Based Cryptography in SunPKCS11 [1] enhancement requirement.

In addition to pursuing the requirement goals and guidelines of
JDK-8301553 [1], 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 [2] 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 [3] 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 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
     #⁠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 different 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_PKCS5_PBKD2_HMAC_SHA1 constant.

   • As mentioned in JDK-8301553 [1], 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 fferrari at redhat.com and
mbalao at redhat.com. 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.


[1] https://bugs.openjdk.org/browse/JDK-8301553
[2] https://datatracker.ietf.org/doc/html/rfc7292#appendix-B
[3] https://datatracker.ietf.org/doc/html/rfc8018#section-6.2

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

Regards,
-- 
Francisco Ferrari Bihurriet <fferrari at redhat.com>
Senior Software Engineer
Red Hat Argentina (https://www.redhat.com)

A00A 468A 5506 5D16 9851  E3A9 CD8B C3EF 772B BD6F




More information about the security-dev mailing list