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