JDK-8214513 : A PKCS12 keystore from Java 8 using custom PBE parameters cannot be read in Java 11
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 11,12
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: os_x
  • CPU: x86_64
  • Submitted: 2018-11-28
  • Updated: 2020-08-20
  • Resolved: 2018-12-05
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 JDK 12
11.0.3Fixed 12 b23Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
 jdk-14.0.2ADDITIONAL SYSTEM INFORMATION :
Mac OS X 10.14.1
OpenJDK 11.0.1
Oracle JDK 1.8.0_192

A DESCRIPTION OF THE PROBLEM :
A private key that has been saved to a PKCS12 keystore using custom PBE parameters in Java 8 (1.8.0_192) cannot be read in Java 11.0.1 and vice versa. It appears the ASN.1 encoding of the PBE parameters has incompatibly changed at some point between these two releases. 

REGRESSION : Last worked in version 8u192

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a PKCS#12 keystore in Java 8 and add a private key entry using a custom PBE scheme:

        KeyStore.PrivateKeyEntry privateKeyEntry = new PrivateKeyEntry(keyPair.getPrivate(),
                new Certificate[] { cert });
        keyStore.setEntry("server", privateKeyEntry,
                new PasswordProtection("password".toCharArray(), "PBEWithHmacSHA512AndAES_256",
                        new PBEParameterSpec(salt, 100_000)));

Save the PKCS12 keystore to a file. Now try to load the same keystore and key from Java 11.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The key is loaded correctly without error.
ACTUAL -
Exception in thread "main" java.security.UnrecoverableKeyException: Private key not stored as PKCS#8 EncryptedPrivateKeyInfo: java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 42)
	at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:373)
	at java.base/sun.security.util.KeyStoreDelegator.engineGetKey(KeyStoreDelegator.java:90)
	at java.base/java.security.KeyStore.getKey(KeyStore.java:1057)
	at PemTest.main(PemTest.java:72)
Caused by: java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 42)
	at java.base/sun.security.util.ObjectIdentifier.<init>(ObjectIdentifier.java:257)
	at java.base/sun.security.util.DerInputStream.getOID(DerInputStream.java:320)
	at java.base/com.sun.crypto.provider.PBES2Parameters.parseKDF(PBES2Parameters.java:282)
	at java.base/com.sun.crypto.provider.PBES2Parameters.engineInit(PBES2Parameters.java:267)
	at java.base/java.security.AlgorithmParameters.init(AlgorithmParameters.java:312)
	at java.base/sun.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:132)
	at java.base/sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:114)
	at java.base/sun.security.x509.AlgorithmId.parse(AlgorithmId.java:374)
	at java.base/sun.security.pkcs.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:80)
	at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:362)
	... 3 more


---------- BEGIN SOURCE ----------

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import javax.crypto.spec.PBEParameterSpec;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

public class Pkcs12Test {

    public static void main(String[] args) throws Exception {
        
        // *** RUN THIS AS JAVA 8 ***
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
        keyPairGenerator.initialize(256);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        SecureRandom secureRandom = SecureRandom.getInstanceStrong();
        BigInteger serial = new BigInteger(159, secureRandom);
        X500Name self = new X500Name("cn=localhost");

        X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(self, serial,
                new Date(), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(3650)), self,
                keyPair.getPublic());
        
        X509CertificateHolder certHolder = certificateBuilder.build(new JcaContentSignerBuilder("SHA256WithECDSA")
                .setSecureRandom(secureRandom)
                .build(keyPair.getPrivate()));
        X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder);

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(null, null);

        byte[] salt = new byte[20];
        new SecureRandom().nextBytes(salt);

        KeyStore.PrivateKeyEntry privateKeyEntry = new PrivateKeyEntry(keyPair.getPrivate(),
                new Certificate[] { cert });
        keyStore.setEntry("server", privateKeyEntry,
                new PasswordProtection("password".toCharArray(), "PBEWithHmacSHA512AndAES_256",
                        new PBEParameterSpec(salt, 100_000)));


        keyStore.store(new FileOutputStream("/tmp/keystore.p12"), "changeit".toCharArray());

        
        /*** RUN THIS AS JAVA 11 ***/
        keyStore.load(new FileInputStream("/tmp/keystore.p12"), "changeit".toCharArray());
        Key key = keyStore.getKey("server", "password".toCharArray());

        System.out.println("Reloaded key: " + key);
    }
}

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

CUSTOMER SUBMITTED WORKAROUND :
Either do not use a custom PBE encryption scheme or recreate keystores from scratch when moving to Java 11.

FREQUENCY : always



Comments
Fix Request: This bugfix shall be backported to jdk11. It is a small local fix and applies cleanly. Before pushing we'll run jtreg tests including the new regression test that comes with the fix.
19-02-2019

Verified by running test/jdk/sun/security/pkcs12/WrongPBES2.java on jdk12+26
04-01-2019

Yes. I confirmed that after backing out JDK-8202837 the keystore generated by the example code with JDK 8 is parseable. I also confirmed that the keystore cannot be parsed by openssl (while the keystore generated with JDK 11 can be). Do you think it's worth adding some workaround code to parse this incorrect encoding?
01-12-2018

[~weijun] could this be caused by JDK-8202837?
30-11-2018

To reproduce the issue, run the attached test case JI9058300.java with JDK 8u191 and JI9058300a.java with JDK-11.0.1/JDK-12. JDK 11.0.1- Fail JDK 12-ea+21 - Fail Output: Exception in thread "main" java.security.UnrecoverableKeyException: Private key not stored as PKCS#8 EncryptedPrivateKeyInfo: java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 42) at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:373) at java.base/sun.security.util.KeyStoreDelegator.engineGetKey(KeyStoreDelegator.java:90) at java.base/java.security.KeyStore.getKey(KeyStore.java:1057) at JI9058300a.main(JI9058300a.java:12) Caused by: java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 42) at java.base/sun.security.util.ObjectIdentifier.<init>(ObjectIdentifier.java:257) at java.base/sun.security.util.DerInputStream.getOID(DerInputStream.java:320) at java.base/com.sun.crypto.provider.PBES2Parameters.parseKDF(PBES2Parameters.java:282) at java.base/com.sun.crypto.provider.PBES2Parameters.engineInit(PBES2Parameters.java:267) at java.base/java.security.AlgorithmParameters.init(AlgorithmParameters.java:312) at java.base/sun.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:132) at java.base/sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:114) at java.base/sun.security.x509.AlgorithmId.parse(AlgorithmId.java:374) at java.base/sun.security.pkcs.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:80) at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:362) ... 3 more
29-11-2018