From c2b90c0780e9102e27fed6ed73bd7ebb23c991bf Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Tue, 30 Sep 2025 23:12:09 -0700 Subject: [PATCH 1/5] 8353749: Improve security warning when using JKS or JCEKS keystores --- .../share/classes/java/security/KeyStore.java | 18 +++ .../keytool/resources/keytool.properties | 3 +- .../sun/security/tools/jarsigner/Main.java | 18 +++ .../jarsigner/resources/jarsigner.properties | 2 + .../KeyStore/TestOutdatedKeyStore.java | 152 ++++++++++++++++++ .../compatibility/Compatibility.java | 5 +- .../tools/jarsigner/warnings/Test.java | 7 + .../keytool/OutdatedKeyStoreWarning.java | 80 +++++++++ .../sun/security/tools/keytool/WeakAlg.java | 86 +++++++--- 9 files changed, 350 insertions(+), 21 deletions(-) create mode 100644 test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java create mode 100644 test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 8f3d4ba29fd8e..68006c2e73c37 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -829,6 +829,19 @@ private String getProviderName() { return (provider == null) ? "(no provider)" : provider.getName(); } + private static void outdatedKeyStoreLog(String type) { + if (type != null && kdebug != null && + (type.equalsIgnoreCase("JKS") || + type.equalsIgnoreCase("JCEKS"))) { + kdebug.println("WARNING: " + type.toUpperCase(Locale.ROOT) + + " uses outdated cryptographic algorithm and" + + " will be removed in a future release." + + " Migrate to PKCS12 using:\n" + + "keytool -importkeystore -srckeystore " + + " -destkeystore -deststoretype pkcs12"); + } + } + /** * Returns a {@code KeyStore} object of the specified type. * @@ -886,6 +899,7 @@ public static KeyStore getInstance(String type) try { Object[] objs = Security.getImpl(type, "KeyStore", (String)null); + outdatedKeyStoreLog(type); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new KeyStoreException(type + " not found", e); @@ -951,6 +965,7 @@ public static KeyStore getInstance(String type, String provider) try { Object[] objs = Security.getImpl(type, "KeyStore", provider); + outdatedKeyStoreLog(type); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); } catch (NoSuchAlgorithmException nsae) { throw new KeyStoreException(type + " not found", nsae); @@ -1012,6 +1027,7 @@ public static KeyStore getInstance(String type, Provider provider) try { Object[] objs = Security.getImpl(type, "KeyStore", provider); + outdatedKeyStoreLog(type); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); } catch (NoSuchAlgorithmException nsae) { throw new KeyStoreException(type + " not found", nsae); @@ -1891,6 +1907,8 @@ private static final KeyStore getInstance(File file, char[] password, keystore.keyStoreSpi.engineLoad(dataStream, param); keystore.initialized = true; } + + outdatedKeyStoreLog(keystore.getType()); return keystore; } } diff --git a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties index fb03573a7d593..751e3af9c9c44 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties +++ b/src/java.base/share/classes/sun/security/tools/keytool/resources/keytool.properties @@ -321,7 +321,8 @@ Unable.to.parse.denyAfter.string.in.exception.message=Unable to parse denyAfter whose.sigalg.weak=%1$s uses the %2$s signature algorithm which is considered a security risk. whose.key.disabled=%1$s uses a %2$s which is considered a security risk and is disabled. whose.key.weak=%1$s uses a %2$s which is considered a security risk. It will be disabled in a future update. -jks.storetype.warning=The %1$s keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12". +jks.storetype.warning=%1$s uses outdated cryptographic algorithms and will be removed in a future release. Migrate to PKCS12 using:\n\ +keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12 migrate.keystore.warning=Migrated "%1$s" to %4$s. The %2$s keystore is backed up as "%3$s". backup.keystore.warning=The original keystore "%1$s" is backed up as "%3$s"... importing.keystore.status=Importing keystore %1$s to %2$s... diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index f2c76058434dd..d10a0b71fa736 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -167,6 +167,7 @@ public ExitException(int errorCode) { char[] storepass; // keystore password boolean protectedPath; // protected authentication path String storetype; // keystore type + String realStoreType; String providerName; // provider name List providers = null; // list of provider names List providerClasses = null; // list of provider classes @@ -240,6 +241,7 @@ public ExitException(int errorCode) { private boolean signerSelfSigned = false; private boolean allAliasesFound = true; private boolean hasMultipleManifests = false; + private boolean outdatedFormat = false; private Throwable chainNotValidatedReason = null; private Throwable tsaChainNotValidatedReason = null; @@ -1482,6 +1484,12 @@ private void displayMessagesAndResult(boolean isSigning) { warnings.add(rb.getString("external.file.attributes.detected")); } + if (outdatedFormat) { + warnings.add(String.format(rb.getString( + "outdated.storetype.warning"), + realStoreType, keystore)); + } + if ((strict) && (!errors.isEmpty())) { result = isSigning ? rb.getString("jar.signed.with.signer.errors.") @@ -2400,6 +2408,16 @@ void loadKeyStore(String keyStoreName, boolean prompt) { (rb.getString("Enter.Passphrase.for.keystore.")); } + File storeFile = new File(keyStoreName); + if (storeFile.isFile()) { + KeyStore keyStore = KeyStore.getInstance(storeFile, storepass); + realStoreType = keyStore.getType(); + if (realStoreType.equalsIgnoreCase("JKS") + || realStoreType.equalsIgnoreCase("JCEKS")) { + outdatedFormat = true; + } + } + try { if (nullStream) { store.load(null, storepass); diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties index b490d386e59b2..b86bb9bf5c26f 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties @@ -222,3 +222,5 @@ entry.1.is.signed.in.jarinputstream.but.is.not.signed.in.jarfile=Entry %s is sig jar.contains.internal.inconsistencies.result.in.different.contents.via.jarfile.and.jarinputstream=This JAR file contains internal inconsistencies that may result in different contents when reading via JarFile and JarInputStream: signature.verification.failed.on.entry.1.when.reading.via.jarinputstream=Signature verification failed on entry %s when reading via JarInputStream signature.verification.failed.on.entry.1.when.reading.via.jarfile=Signature verification failed on entry %s when reading via JarFile +outdated.storetype.warning=%1$s uses outdated cryptographic algorithms and will be removed in a future release. Migrate to PKCS12 using:\n\ +keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12 diff --git a/test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java b/test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java new file mode 100644 index 0000000000000..6f75bcc3f6f52 --- /dev/null +++ b/test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; +import java.util.Locale; + +/* + * @test + * @bug 8353749 + * @summary Check warnings for JKS and JCEKS KeyStore + * @run main/othervm -Djava.security.debug=keystore TestOutdatedKeyStore + */ + +public class TestOutdatedKeyStore { + + private static final String[] KS_TYPES = { + "jks", "jceks" + }; + + private static final String KS_WARNING1 = + "uses outdated cryptographic algorithm and will be removed " + + "in a future release. Migrate to PKCS12 using:"; + + private static final String KS_WARNING2= + "keytool -importkeystore -srckeystore " + + "-destkeystore -deststoretype pkcs12"; + + public static void main(String[] args) throws Exception { + for (String type : KS_TYPES) { + testGetInstance1(type); + testGetInstance2(type); + testGetInstance3(type); + testGetInstance4(type); + testGetInstance5(type); + } + System.out.println("All tests completed."); + } + + // Test getInstance(String type) + private static void testGetInstance1(String type) throws Exception { + System.out.println("Test getInstance(String type) with type: " + + type); + checkWarnings(type, () -> { + KeyStore ks = KeyStore.getInstance(type); + }); + } + + // Test getInstance(String type, String provider) + private static void testGetInstance2(String type) throws Exception { + System.out.println("Test getInstance(String type, String provider) with type: " + + type); + String provider = Security.getProviders("KeyStore." + type)[0].getName(); + checkWarnings(type, () -> { + KeyStore ks = KeyStore.getInstance(type, provider); + }); + } + + // Test getInstance(String type, Provider provider) + private static void testGetInstance3(String type) throws Exception { + System.out.println("Test getInstance(String type, Provider provider) with type: " + + type); + Provider provider = Security.getProviders("KeyStore." + type)[0]; + checkWarnings(type, () -> { + KeyStore ks = KeyStore.getInstance(type, provider); + }); + } + + // Test getInstance(File file, char[] password) + private static void testGetInstance4(String type) throws Exception { + System.out.println("Test getInstance(File file, char[] password) with type: " + + type); + File ksFile = createKeystore(type); + checkWarnings(type, () -> { + KeyStore ks = KeyStore.getInstance(ksFile, "changeit".toCharArray()); + }); + } + + // Test getInstance(File file, LoadStoreParameter param) + private static void testGetInstance5(String type) throws Exception { + System.out.println("Test getInstance(File file, LoadStoreParameter param) with type: " + + type); + File ksFile = createKeystore(type); + KeyStore.LoadStoreParameter param = new KeyStore.LoadStoreParameter() { + @Override + public KeyStore.ProtectionParameter getProtectionParameter() { + return new KeyStore.PasswordProtection("changeit".toCharArray()); + } + }; + checkWarnings(type, () -> { + KeyStore ks = KeyStore.getInstance(ksFile, param); + }); + } + + private static File createKeystore(String type) throws Exception { + File ksFile = File.createTempFile("kstore", ".tmp"); + ksFile.deleteOnExit(); + KeyStore ks = KeyStore.getInstance(type); + ks.load(null, null); + try (FileOutputStream fos = new FileOutputStream(ksFile)) { + ks.store(fos, "changeit".toCharArray()); + } + return ksFile; + } + + private static void checkWarnings(String type, RunnableWithException r) throws Exception { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream origErr = System.err; + try { + System.setErr(new PrintStream(bOut)); + r.run(); + } finally { + System.setErr(origErr); + } + + String msg = bOut.toString(); + if (!msg.contains("WARNING: " + type.toUpperCase(Locale.ROOT)) || + !msg.contains(KS_WARNING1) || + !msg.contains(KS_WARNING2)) { + throw new RuntimeException("Expected warning not found for " + type + ":\n" + msg); + } + } + + interface RunnableWithException { + void run() throws Exception; + } +} diff --git a/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java b/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java index 25bb67c230be3..ff3800f065383 100644 --- a/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java +++ b/test/jdk/sun/security/tools/jarsigner/compatibility/Compatibility.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8217375 8260286 8267319 + * @bug 8217375 8260286 8267319 8353749 * @summary This test is used to verify the compatibility of jarsigner across * different JDK releases. It also can be used to check jar signing (w/ * and w/o TSA) and to verify some specific signing and digest algorithms. @@ -850,6 +850,9 @@ private static Status verifyingStatus(SignItem signItem, VerifyItem if (Test.CERTIFICATE_SELF_SIGNED.equals(line)) continue; if (Test.HAS_EXPIRED_CERT_VERIFYING_WARNING.equals(line) && signItem.certInfo.expired) continue; + + if (line.contains(Test.OUTDATED_KEYSTORE_WARNING1)) continue; + if (line.contains(Test.OUTDATED_KEYSTORE_WARNING2)) continue; System.out.println("verifyingStatus: unexpected line: " + line); return Status.ERROR; // treat unexpected warnings as error } diff --git a/test/jdk/sun/security/tools/jarsigner/warnings/Test.java b/test/jdk/sun/security/tools/jarsigner/warnings/Test.java index 224fa754eec79..97c05d7c2cb1f 100644 --- a/test/jdk/sun/security/tools/jarsigner/warnings/Test.java +++ b/test/jdk/sun/security/tools/jarsigner/warnings/Test.java @@ -151,6 +151,13 @@ public abstract class Test { static final String WEAK_KEY_WARNING = "will be disabled in a future update."; + static final String OUTDATED_KEYSTORE_WARNING1 + = "uses outdated cryptographic algorithms and will be " + + "removed in a future release. Migrate to PKCS12 using:"; + + static final String OUTDATED_KEYSTORE_WARNING2 + = "keytool -importkeystore -srckeystore"; + static final String JAR_SIGNED = "jar signed."; static final String JAR_VERIFIED = "jar verified."; diff --git a/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java b/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java new file mode 100644 index 0000000000000..712dbcf563862 --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8353749 + * @summary Validate that keytool and jarsigner emit warnings for + * JKS and JCEKS keystore + * @library /test/lib + */ + +import java.nio.file.Path; + +import jdk.test.lib.SecurityTools; +import jdk.test.lib.util.JarUtils; + +public class OutdatedKeyStoreWarning { + + public static void main(String[] args) throws Exception { + String[] ksTypes = {"JKS", "JCEKS"}; + + for (String type : ksTypes) { + String ksFile = type.toLowerCase() + ".ks"; + String KS_WARNING = type + " uses outdated cryptographic algorithms and " + + "will be removed in a future release. Migrate to PKCS12 using:"; + + SecurityTools.keytool(String.format( + "-genkeypair -keystore %s -storetype %s -storepass changeit " + + "-keypass changeit -keyalg ec -alias a1 -dname CN=me", + ksFile, type.toLowerCase())) + .shouldContain("Warning:") + .shouldContain(KS_WARNING) + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12") + .shouldHaveExitValue(0); + + JarUtils.createJarFile(Path.of("unsigned.jar"), Path.of("."), + Path.of(ksFile)); + + SecurityTools.jarsigner(String.format( + "-keystore %s -storetype %s -storepass changeit -signedjar signed.jar " + + "unsigned.jar a1", + ksFile, type.toLowerCase())) + .shouldContain("Warning:") + .shouldContain(KS_WARNING) + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12") + .shouldHaveExitValue(0); + + SecurityTools.jarsigner(String.format( + "-verify -keystore %s -storetype %s -storepass changeit signed.jar", + ksFile, type.toLowerCase())) + .shouldContain("Warning:") + .shouldContain(KS_WARNING) + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12") + .shouldHaveExitValue(0); + } + } +} diff --git a/test/jdk/sun/security/tools/keytool/WeakAlg.java b/test/jdk/sun/security/tools/keytool/WeakAlg.java index 13c610cd55f5d..de2435a4f4bc4 100644 --- a/test/jdk/sun/security/tools/keytool/WeakAlg.java +++ b/test/jdk/sun/security/tools/keytool/WeakAlg.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8171319 8177569 8182879 8172404 + * @bug 8171319 8177569 8182879 8172404 8353749 * @summary keytool should print out warnings when reading or generating * cert/cert req using weak algorithms * @library /test/lib @@ -226,7 +226,8 @@ static void jksTypeCheck() throws Exception { // No warning for cacerts, all certs kt0("-cacerts -list -storepass changeit") - .shouldNotContain("proprietary format"); + .shouldNotContain("outdated cryptographic algorithms") + .shouldNotContain("keytool -importkeystore -srckeystore"); rm("ks"); rm("ks2"); @@ -242,7 +243,10 @@ static void jksTypeCheck() throws Exception { // warn if migrating to JKS importkeystore("ks", "ks2", "-deststoretype jks") - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); rm("ks"); rm("ks2"); @@ -252,17 +256,35 @@ static void jksTypeCheck() throws Exception { kt("-importcert -alias b -file a.crt -storetype jks -noprompt") .shouldNotContain("Warning:"); kt("-genkeypair -keyalg DSA -alias a -dname CN=A") - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-list") - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-list -storetype pkcs12") // warn if JKS used as PKCS12 - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-exportcert -alias a -file a.crt") - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-printcert -file a.crt") // warning since -keystore option is supported - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-certreq -alias a -file a.req") - .shouldContain("JKS keystore uses a proprietary format"); + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-printcertreq -file a.req") // no warning if keystore not touched .shouldNotContain("Warning:"); @@ -276,21 +298,42 @@ static void jksTypeCheck() throws Exception { rm("ks"); kt("-genkeypair -keyalg DSA -alias a -dname CN=A -storetype jceks") - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-list") - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-importcert -alias b -file a.crt -noprompt") - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-exportcert -alias a -file a.crt") - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-printcert -file a.crt") // warning since -keystore option is supported - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-certreq -alias a -file a.req") - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); kt("-printcertreq -file a.req") .shouldNotContain("Warning:"); kt("-genseckey -alias c -keyalg AES -keysize 128") - .shouldContain("JCEKS keystore uses a proprietary format"); + .shouldContain("JCEKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12"); } static void checkImportKeyStore() throws Exception { @@ -336,7 +379,10 @@ static void checkInplaceImportKeyStore() throws Exception { // Migration importkeystore("ks", "ks", "-deststoretype jks") .shouldContain("Warning:") - .shouldContain("JKS keystore uses a proprietary format") + .shouldContain("JKS uses outdated cryptographic algorithms" + + " and will be removed") + .shouldMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12") .shouldMatch("Migrated.*JKS.*PKCS12.*ks.old5"); Asserts.assertEQ( @@ -346,7 +392,9 @@ static void checkInplaceImportKeyStore() throws Exception { importkeystore("ks", "ks", "-srcstoretype PKCS12") .shouldContain("Warning:") - .shouldNotContain("proprietary format") + .shouldNotContain("outdated cryptographic algorithms") + .shouldNotMatch("keytool -importkeystore -srckeystore." + + "*-destkeystore.*-deststoretype pkcs12") .shouldMatch("Migrated.*PKCS12.*JKS.*ks.old6"); Asserts.assertEQ( From 9fc01d182bffbaf3eb0e78f0b856454479b2ff77 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 9 Oct 2025 23:38:57 -0700 Subject: [PATCH 2/5] Remove test TestOutdatedKeyStore.java --- .../KeyStore/TestOutdatedKeyStore.java | 152 ------------------ 1 file changed, 152 deletions(-) delete mode 100644 test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java diff --git a/test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java b/test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java deleted file mode 100644 index 6f75bcc3f6f52..0000000000000 --- a/test/jdk/java/security/KeyStore/TestOutdatedKeyStore.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.PrintStream; -import java.security.KeyStore; -import java.security.Provider; -import java.security.Security; -import java.util.Locale; - -/* - * @test - * @bug 8353749 - * @summary Check warnings for JKS and JCEKS KeyStore - * @run main/othervm -Djava.security.debug=keystore TestOutdatedKeyStore - */ - -public class TestOutdatedKeyStore { - - private static final String[] KS_TYPES = { - "jks", "jceks" - }; - - private static final String KS_WARNING1 = - "uses outdated cryptographic algorithm and will be removed " + - "in a future release. Migrate to PKCS12 using:"; - - private static final String KS_WARNING2= - "keytool -importkeystore -srckeystore " + - "-destkeystore -deststoretype pkcs12"; - - public static void main(String[] args) throws Exception { - for (String type : KS_TYPES) { - testGetInstance1(type); - testGetInstance2(type); - testGetInstance3(type); - testGetInstance4(type); - testGetInstance5(type); - } - System.out.println("All tests completed."); - } - - // Test getInstance(String type) - private static void testGetInstance1(String type) throws Exception { - System.out.println("Test getInstance(String type) with type: " - + type); - checkWarnings(type, () -> { - KeyStore ks = KeyStore.getInstance(type); - }); - } - - // Test getInstance(String type, String provider) - private static void testGetInstance2(String type) throws Exception { - System.out.println("Test getInstance(String type, String provider) with type: " - + type); - String provider = Security.getProviders("KeyStore." + type)[0].getName(); - checkWarnings(type, () -> { - KeyStore ks = KeyStore.getInstance(type, provider); - }); - } - - // Test getInstance(String type, Provider provider) - private static void testGetInstance3(String type) throws Exception { - System.out.println("Test getInstance(String type, Provider provider) with type: " - + type); - Provider provider = Security.getProviders("KeyStore." + type)[0]; - checkWarnings(type, () -> { - KeyStore ks = KeyStore.getInstance(type, provider); - }); - } - - // Test getInstance(File file, char[] password) - private static void testGetInstance4(String type) throws Exception { - System.out.println("Test getInstance(File file, char[] password) with type: " - + type); - File ksFile = createKeystore(type); - checkWarnings(type, () -> { - KeyStore ks = KeyStore.getInstance(ksFile, "changeit".toCharArray()); - }); - } - - // Test getInstance(File file, LoadStoreParameter param) - private static void testGetInstance5(String type) throws Exception { - System.out.println("Test getInstance(File file, LoadStoreParameter param) with type: " - + type); - File ksFile = createKeystore(type); - KeyStore.LoadStoreParameter param = new KeyStore.LoadStoreParameter() { - @Override - public KeyStore.ProtectionParameter getProtectionParameter() { - return new KeyStore.PasswordProtection("changeit".toCharArray()); - } - }; - checkWarnings(type, () -> { - KeyStore ks = KeyStore.getInstance(ksFile, param); - }); - } - - private static File createKeystore(String type) throws Exception { - File ksFile = File.createTempFile("kstore", ".tmp"); - ksFile.deleteOnExit(); - KeyStore ks = KeyStore.getInstance(type); - ks.load(null, null); - try (FileOutputStream fos = new FileOutputStream(ksFile)) { - ks.store(fos, "changeit".toCharArray()); - } - return ksFile; - } - - private static void checkWarnings(String type, RunnableWithException r) throws Exception { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - PrintStream origErr = System.err; - try { - System.setErr(new PrintStream(bOut)); - r.run(); - } finally { - System.setErr(origErr); - } - - String msg = bOut.toString(); - if (!msg.contains("WARNING: " + type.toUpperCase(Locale.ROOT)) || - !msg.contains(KS_WARNING1) || - !msg.contains(KS_WARNING2)) { - throw new RuntimeException("Expected warning not found for " + type + ":\n" + msg); - } - } - - interface RunnableWithException { - void run() throws Exception; - } -} From 3a2334f0c18a9c68156a60db41a8d6fd344a9a81 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 9 Oct 2025 23:40:58 -0700 Subject: [PATCH 3/5] Revert changes to KeyStore.java --- .../share/classes/java/security/KeyStore.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 68006c2e73c37..8f3d4ba29fd8e 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -829,19 +829,6 @@ private String getProviderName() { return (provider == null) ? "(no provider)" : provider.getName(); } - private static void outdatedKeyStoreLog(String type) { - if (type != null && kdebug != null && - (type.equalsIgnoreCase("JKS") || - type.equalsIgnoreCase("JCEKS"))) { - kdebug.println("WARNING: " + type.toUpperCase(Locale.ROOT) + - " uses outdated cryptographic algorithm and" + - " will be removed in a future release." + - " Migrate to PKCS12 using:\n" + - "keytool -importkeystore -srckeystore " + - " -destkeystore -deststoretype pkcs12"); - } - } - /** * Returns a {@code KeyStore} object of the specified type. * @@ -899,7 +886,6 @@ public static KeyStore getInstance(String type) try { Object[] objs = Security.getImpl(type, "KeyStore", (String)null); - outdatedKeyStoreLog(type); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new KeyStoreException(type + " not found", e); @@ -965,7 +951,6 @@ public static KeyStore getInstance(String type, String provider) try { Object[] objs = Security.getImpl(type, "KeyStore", provider); - outdatedKeyStoreLog(type); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); } catch (NoSuchAlgorithmException nsae) { throw new KeyStoreException(type + " not found", nsae); @@ -1027,7 +1012,6 @@ public static KeyStore getInstance(String type, Provider provider) try { Object[] objs = Security.getImpl(type, "KeyStore", provider); - outdatedKeyStoreLog(type); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); } catch (NoSuchAlgorithmException nsae) { throw new KeyStoreException(type + " not found", nsae); @@ -1907,8 +1891,6 @@ private static final KeyStore getInstance(File file, char[] password, keystore.keyStoreSpi.engineLoad(dataStream, param); keystore.initialized = true; } - - outdatedKeyStoreLog(keystore.getType()); return keystore; } } From 8246e4de57dcd153988c6559ce8197cf4b390629 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 9 Oct 2025 23:43:35 -0700 Subject: [PATCH 4/5] Updates with review comments --- .../com/sun/crypto/provider/JceKeyStore.java | 8 ++ .../sun/security/provider/JavaKeyStore.java | 14 ++- .../sun/security/tools/jarsigner/Main.java | 23 ++-- .../jarsigner/resources/jarsigner.properties | 2 +- .../keytool/OutdatedKeyStoreWarning.java | 106 +++++++++++++----- 5 files changed, 105 insertions(+), 48 deletions(-) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java index ec8e0f3757ddb..96b7e917fbb6b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java @@ -862,6 +862,14 @@ public void engineLoad(InputStream stream, char[] password) secretKeyCount); } + if (debug != null) { + debug.println("WARNING: JCEKS uses outdated cryptographic " + + "algorithms and will be removed in a future " + + "release. Migrate to PKCS12 using:\n" + + "keytool -importkeystore -srckeystore " + + "-destkeystore -deststoretype pkcs12"); + } + /* * If a password has been provided, we check the keyed digest * at the end. If this check fails, the store has been tampered diff --git a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java index 3e771c015f4d8..a26df28d0a109 100644 --- a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -790,6 +790,18 @@ public void engineLoad(InputStream stream, char[] password) privateKeyCount + ". trusted key count: " + trustedKeyCount); } + if (debug != null) { + String type = this.getClass().getSimpleName(). + toUpperCase(Locale.ROOT); + if (type.equals("JKS")){ + debug.println("WARNING: JKS uses outdated cryptographic " + + "algorithms and will be removed in a future " + + "release. Migrate to PKCS12 using:\n" + + "keytool -importkeystore -srckeystore " + + "-destkeystore -deststoretype pkcs12"); + } + } + /* * If a password has been provided, we check the keyed digest * at the end. If this check fails, the store has been tampered diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index d10a0b71fa736..508e03077fad5 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -167,7 +167,6 @@ public ExitException(int errorCode) { char[] storepass; // keystore password boolean protectedPath; // protected authentication path String storetype; // keystore type - String realStoreType; String providerName; // provider name List providers = null; // list of provider names List providerClasses = null; // list of provider classes @@ -241,7 +240,7 @@ public ExitException(int errorCode) { private boolean signerSelfSigned = false; private boolean allAliasesFound = true; private boolean hasMultipleManifests = false; - private boolean outdatedFormat = false; + private boolean weakKeyStore = false; private Throwable chainNotValidatedReason = null; private Throwable tsaChainNotValidatedReason = null; @@ -1484,10 +1483,10 @@ private void displayMessagesAndResult(boolean isSigning) { warnings.add(rb.getString("external.file.attributes.detected")); } - if (outdatedFormat) { + if (weakKeyStore) { warnings.add(String.format(rb.getString( - "outdated.storetype.warning"), - realStoreType, keystore)); + "jks.storetype.warning"), + storetype, keystore)); } if ((strict) && (!errors.isEmpty())) { @@ -2408,16 +2407,6 @@ void loadKeyStore(String keyStoreName, boolean prompt) { (rb.getString("Enter.Passphrase.for.keystore.")); } - File storeFile = new File(keyStoreName); - if (storeFile.isFile()) { - KeyStore keyStore = KeyStore.getInstance(storeFile, storepass); - realStoreType = keyStore.getType(); - if (realStoreType.equalsIgnoreCase("JKS") - || realStoreType.equalsIgnoreCase("JCEKS")) { - outdatedFormat = true; - } - } - try { if (nullStream) { store.load(null, storepass); @@ -2440,6 +2429,10 @@ void loadKeyStore(String keyStoreName, boolean prompt) { is.close(); } } + if (store.getType().equalsIgnoreCase("JKS") + || store.getType().equalsIgnoreCase("JCEKS")) { + weakKeyStore = true; + } } Enumeration aliases = store.aliases(); while (aliases.hasMoreElements()) { diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties index b86bb9bf5c26f..a16420daf8f18 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties @@ -222,5 +222,5 @@ entry.1.is.signed.in.jarinputstream.but.is.not.signed.in.jarfile=Entry %s is sig jar.contains.internal.inconsistencies.result.in.different.contents.via.jarfile.and.jarinputstream=This JAR file contains internal inconsistencies that may result in different contents when reading via JarFile and JarInputStream: signature.verification.failed.on.entry.1.when.reading.via.jarinputstream=Signature verification failed on entry %s when reading via JarInputStream signature.verification.failed.on.entry.1.when.reading.via.jarfile=Signature verification failed on entry %s when reading via JarFile -outdated.storetype.warning=%1$s uses outdated cryptographic algorithms and will be removed in a future release. Migrate to PKCS12 using:\n\ +jks.storetype.warning=%1$s uses outdated cryptographic algorithms and will be removed in a future release. Migrate to PKCS12 using:\n\ keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12 diff --git a/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java b/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java index 712dbcf563862..d3537b844da5d 100644 --- a/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java +++ b/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java @@ -25,56 +25,100 @@ * @test * @bug 8353749 * @summary Validate that keytool and jarsigner emit warnings for - * JKS and JCEKS keystore + * JKS and JCEKS keystore with java.security.debug=keystore * @library /test/lib */ import java.nio.file.Path; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.util.Locale; import jdk.test.lib.SecurityTools; import jdk.test.lib.util.JarUtils; public class OutdatedKeyStoreWarning { + private static final String KS_WARNING1 = + "uses outdated cryptographic algorithms and will be removed " + + "in a future release. Migrate to PKCS12 using:"; + + private static final String KS_WARNING2= + "keytool -importkeystore -srckeystore " + + "-destkeystore -deststoretype pkcs12"; + public static void main(String[] args) throws Exception { String[] ksTypes = {"JKS", "JCEKS"}; for (String type : ksTypes) { String ksFile = type.toLowerCase() + ".ks"; - String KS_WARNING = type + " uses outdated cryptographic algorithms and " + - "will be removed in a future release. Migrate to PKCS12 using:"; + String cmdWarning = type + " " + KS_WARNING1; + + checkWarnings(type, () -> { + SecurityTools.keytool(String.format( + "-genkeypair -keystore %s -storetype %s -storepass changeit " + + "-keypass changeit -keyalg ec -alias a1 -dname CN=me " + + "-J-Djava.security.debug=keystore", + ksFile, type.toLowerCase())) + .shouldContain("Warning:") + .shouldContain(cmdWarning) + .shouldContain(KS_WARNING2) + .shouldHaveExitValue(0); + }); - SecurityTools.keytool(String.format( - "-genkeypair -keystore %s -storetype %s -storepass changeit " + - "-keypass changeit -keyalg ec -alias a1 -dname CN=me", - ksFile, type.toLowerCase())) - .shouldContain("Warning:") - .shouldContain(KS_WARNING) - .shouldMatch("keytool -importkeystore -srckeystore." + - "*-destkeystore.*-deststoretype pkcs12") - .shouldHaveExitValue(0); + JarUtils.createJarFile(Path.of("unsigned.jar"), Path.of("."), Path.of(ksFile)); + checkWarnings(type, () -> { + SecurityTools.jarsigner(String.format( + "-keystore %s -storetype %s -storepass changeit -signedjar signed.jar " + + "unsigned.jar a1 " + + "-J-Djava.security.debug=keystore", + ksFile, type.toLowerCase())) + .shouldContain("Warning:") + .shouldContain(cmdWarning) + .shouldContain(KS_WARNING2) + .shouldHaveExitValue(0); + }); - JarUtils.createJarFile(Path.of("unsigned.jar"), Path.of("."), - Path.of(ksFile)); + checkWarnings(type, () -> { + SecurityTools.jarsigner(String.format( + "-verify -keystore %s -storetype %s -storepass changeit signed.jar " + + "-J-Djava.security.debug=keystore", + ksFile, type.toLowerCase())) + .shouldContain("Warning:") + .shouldContain(cmdWarning) + .shouldContain(KS_WARNING2) + .shouldHaveExitValue(0); + }); + } + } - SecurityTools.jarsigner(String.format( - "-keystore %s -storetype %s -storepass changeit -signedjar signed.jar " + - "unsigned.jar a1", - ksFile, type.toLowerCase())) - .shouldContain("Warning:") - .shouldContain(KS_WARNING) - .shouldMatch("keytool -importkeystore -srckeystore." + - "*-destkeystore.*-deststoretype pkcs12") - .shouldHaveExitValue(0); + private static void checkWarnings(String type, RunnableWithException r) throws Exception { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream origErr = System.err; + PrintStream origOut = System.out; - SecurityTools.jarsigner(String.format( - "-verify -keystore %s -storetype %s -storepass changeit signed.jar", - ksFile, type.toLowerCase())) - .shouldContain("Warning:") - .shouldContain(KS_WARNING) - .shouldMatch("keytool -importkeystore -srckeystore." + - "*-destkeystore.*-deststoretype pkcs12") - .shouldHaveExitValue(0); + try { + PrintStream pStream = new PrintStream(bOut); + System.setErr(pStream); + System.setOut(pStream); + r.run(); + } finally { + System.setErr(origErr); + System.setOut(origOut); } + + String msg = bOut.toString(); + if (!msg.contains("WARNING: " + type.toUpperCase(Locale.ROOT)) || + !msg.contains(KS_WARNING1) || + !msg.contains(KS_WARNING2) || + !msg.contains("Warning:")) { + throw new RuntimeException("Expected warning not found for " + type + ":\n" + msg); + } + } + + @FunctionalInterface + interface RunnableWithException { + void run() throws Exception; } } From 452e5cdaedc6738d5ffa6e37d632f7cacede94cb Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Fri, 10 Oct 2025 00:13:47 -0700 Subject: [PATCH 5/5] Updates with review comments --- .../share/classes/sun/security/tools/jarsigner/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index 508e03077fad5..59fbff435eefe 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -1486,7 +1486,7 @@ private void displayMessagesAndResult(boolean isSigning) { if (weakKeyStore) { warnings.add(String.format(rb.getString( "jks.storetype.warning"), - storetype, keystore)); + store.getType(), keystore)); } if ((strict) && (!errors.isEmpty())) {