JDK-8245169 : EncryptedPrivateKeyInfo incorrectly decodes KDF algorithm
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.crypto
  • Affected Version: 11
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2020-05-15
  • Updated: 2021-08-23
  • Resolved: 2021-08-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11
11-poolResolved
Related Reports
Duplicate :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
macOS 10.15.4
openjdk version "11.0.7" 2020-04-14
OpenJDK Runtime Environment (build 11.0.7+10)
OpenJDK 64-Bit Server VM (build 11.0.7+10, mixed mode)

A DESCRIPTION OF THE PROBLEM :
When decoding a PKCS#8 encrypted private key using PBKDF2WithHmacSHA256 as the KDF, Java 11.0.7 incorrectly decodes it as PBKDF2WithHmacSHA1 and therefore fails to decrypt the key. This is fixed in Java 12 and later as a side-effect of https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8076190.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Generate a password encrypted PEM private key with OpenSSL 1.1.1 and the default encryption parameters. I converted an RSA ssh key using:
/usr/local/Cellar/openssl@1.1/1.1.1g/bin/openssl pkcs8 -topk8 -in ssh_ecdsa_key_pw -out openssl_ecdsa_key_pkcs8_v2.pem
The resulting PEM file data is included in the test case attached to this ticket. The default algorithm is PBKDF2 with HMAC-SHA256 and 2048 iterations then AES-CBC.
2. Decode the data from the PEM file using var keyInfo = new EncryptedPrivateKeyInfo(data)
3. Obtain a correct Cipher instance based on the AlgorithmParameters (see source code of attached test case for details)
4. Attempt to decrypt the private key using keyInfo.getKeySpec(cipher)

NOTE: there is probably a distinct bug here in all versions of Java in that keyInfo.getKeySpec(decryptionKey) always fails because it is unable to find the PBES2 cipher by OID. The workaround is to lookup the Cipher manually using keySpec.getAlgParameters().toString() which seems fragile.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The private key is correctly decrypted and a PKCS8EncodedKeySpec is returned.
ACTUAL -
Exception in thread "main" java.security.spec.InvalidKeySpecException: Cannot retrieve the PKCS8EncodedKeySpec
	at java.base/javax.crypto.EncryptedPrivateKeyInfo.getKeySpec(EncryptedPrivateKeyInfo.java:255)
	at test.main(test.java:22)
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
	at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
	at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
	at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
	at java.base/com.sun.crypto.provider.PBES2Core.engineDoFinal(PBES2Core.java:323)
	at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
	at java.base/javax.crypto.EncryptedPrivateKeyInfo.getKeySpec(EncryptedPrivateKeyInfo.java:250)

This is due to the wrong PBKDF2 algorithm being selected (SHA1 vs SHA-256).

---------- BEGIN SOURCE ----------
import static java.nio.charset.StandardCharsets.UTF_8;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Base64;

public class test {
    public static void main(String... args) throws Exception {

        EncryptedPrivateKeyInfo keyInfo = new EncryptedPrivateKeyInfo(getKeyData());
        Key decryptionKey = new SecretKeySpec("changeit".getBytes(UTF_8), "PBE");

        // NB: keyInfo.getKeySpec(decryptionKey) results in:
        // Exception in thread "main" java.security.NoSuchAlgorithmException: Cannot find any provider supporting 1.2.840.113549.1.5.13
        // This is probably also a bug/RFE...
        // Workaround is to use the toString() method on the algorithm parameters
        AlgorithmParameters params = keyInfo.getAlgParameters();
        Cipher cipher = Cipher.getInstance(params.toString());
        cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);

        // Works in Java 12+, BadPaddingException in Java 11:
        keyInfo.getKeySpec(cipher);
    }

    private static final Base64.Decoder DECODER = Base64.getDecoder();
    private static byte[] getKeyData() {
        String pem = 
"MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIgirdJtYu1XACAggA" +
"MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAWjKvvq4ZyQFPGI2tfmtrdBIIE" +
"0OjhVdYFD3Uw5Mj5JyhiiQCGrQ3/JBt1iNrJ38SzvSg7SCKbU94XRGlU6OsmufFi" +
"53mZOAbD3jyr4zM7kPc7Zh9BsdwXCkfxzzI6gokNDFeZ6EzHHUMBOwDUcRmzOShc" +
"tYytz5x9NGnxlA6OAyXHUXi5H6/9wqIMDNKGWPzNCXBcB3xfMQ8dy92NbK4ksZG2" +
"6TSXQG+Kqd4wRxZUm6AeTl28TBcXfsl7lavSP3m78+6k448TdkkI3W/AAK/81YBk" +
"uyhicCHXAbmoDf1ZHScVk54SuBOInRgp1pTGvjqCDw0bLWSWTdcHveQYlPac0S5Z" +
"jFRJDopN80plb/DFLVEgP3KRuIJFAHwzy6+0vKLxWVJJ/EBN8HYeaAVoyiV2zSX8" +
"F8wqIJt6o9vD4suBbUhPTygqIKHjU+wLNFEoLDwVPuUmjStH1uwaPvuFj0rtmT/7" +
"ef3aXJGU8hlt/cU7DrPMRn9qON+CLyzNI3CEFSqSfEdZ33DYjJp7EmXnCLHTEKpm" +
"o5U9dViAQKPCaJGEvuORxVi0+u0N8ake5DceyFojiPk6Ccd96Jvl2tkVKcJu4EpB" +
"0XGho+J2xnMWIIJ3Vc2K9dCMxSy+GonO0wHWVVnd2ye24I3Fhbd9mtVGuHnTrbAa" +
"9VH1MhcnQu2x/TVpt9wpTqhoMyufzOniBNoQRnwCHXy9MJrlDamXbBISO7SxaQPl" +
"jnkDPK6uGA6Ee+0GC8oE9drhrVlOgRbW29edRVpuag63q04yPT4Wn8sSMcR+Af9s" +
"jcgnzsDZrZ0iu66zmDaPhWBRX+dOLJWedOnBGGtD+en3117+GjRb7fw5Dz2QIuDU" +
"nM9oAenEk83zWW2cZd7ocU/8U0eZ66PGRgEzhfiITvXKw1gPxUD/CGLoNmmQJhhP" +
"QocdfGzyz1aoCut+wlD43lAoJOBDx/vYN6WaboeDq7Q3B/x/vhfRQxrMdUXfBt4Y" +
"ayJS6qgMeapHyiVGLbLxbxG1KSa7jSa1PIxoJuP0C0cGs+b8vpGlHbuy8HkepToM" +
"57zZxRJy7e9BEBTV0lJwygUvrqKjV8Mx6zszBnuXu8myC5GWZz6ttyC9mvAWqCdz" +
"DWrMeNwOIb88wl5XVYtVHN4BLDXE5uq9WKr2vSyy1MBFJ3Kj+0Qhq+w8NwNgFO73" +
"tEnbMb06h3C5oxTKdT3R8SzXyEqhZKZax1sEIE/w4fK6O/WoYzCRQX/WyaQJ1aKc" +
"N5KwZnF8f7aDZf58mMRMnvILnRHFHU5v0iYRGkxC7TMPJnUc5JpCkw7B+Qe6ItJu" +
"AxD4VrWNMvnT20TGp2b3MebrFn0sYvP5KG/wH1ipr3HkQo0e1gOnBDWKs1OQu4sP" +
"qYO2r6m33gVFgQeW/qhmw7A0datAcNTo45Hy80sFtCWzlu+Te6avCy6h8ErBi2dM" +
"JxtdNsqGjTrOgxLpALp/XDR0cUhWV7eJ+aKxKB4uYS4oSVHyrZQ5y2q5Ap6hwUUb" +
"8EOEEDzFcl+dzNR2GDUK4V6rVqsM6XX4SadtTOO2GDVck8F1i9z6cqKciH5VkNVZ" +
"AgciRt6oO1O3xkwOy2rpoLsy6AYseRgqkYsKcSE79bAcg1lavNlpin3kS4QfebFw" +
"/iI8DQfRtmesl0jljm3CPV3Hq/9i1nwX5c5oKiE1L95D";
        return DECODER.decode(pem);
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use Java 12 or later.

FREQUENCY : always



Comments
With JDK-8076190 now in 11 (11.0.12, 11.0.12-oracle) and based on the previous analysis that it fixes this bug, it's my understanding that we can proceed to close it. It's not exactly a 'duplicate' but I will set that as the resolution and link to JDK-8076190. See the previous comments on this ticket.
23-08-2021

As Sustaining group owns the older releases, e.g. jdk11, unassign myself from this bug and mark fix-version to 11-pool per above evaluation.
26-06-2020

This bug is only applicable to JDK 11 and earlier releases. If JDK-8076190 isn't backported, this issue can be fixed by applying the PBES2Parameters changes (diff is included as above).
26-06-2020

To be more precise, the fix for this bug is inside the src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java of Max's fix for JDK-8076190: http://cr.openjdk.java.net/~weijun/8076190/webrev.07/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java.sdiff.html The supplied keystore does not have the optional key length field which leads to the parsing of KDF being skipped unexpectedly.
26-06-2020

The observations on Windows 10: JDK 11: Fail JDK 14: Pass JDK 15: Pass ILW=MML=P4
18-05-2020