Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8214513

A PKCS12 keystore from Java 8 using custom PBE parameters cannot be read in Java 11

    XMLWordPrintable

Details

    • b23
    • x86_64
    • os_x
    • Verified

    Backports

      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


        Attachments

          Issue Links

            Activity

              People

                weijun Weijun Wang
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: