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/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..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 @@ -240,6 +240,7 @@ public ExitException(int errorCode) { private boolean signerSelfSigned = false; private boolean allAliasesFound = true; private boolean hasMultipleManifests = false; + private boolean weakKeyStore = false; private Throwable chainNotValidatedReason = null; private Throwable tsaChainNotValidatedReason = null; @@ -1482,6 +1483,12 @@ private void displayMessagesAndResult(boolean isSigning) { warnings.add(rb.getString("external.file.attributes.detected")); } + if (weakKeyStore) { + warnings.add(String.format(rb.getString( + "jks.storetype.warning"), + store.getType(), keystore)); + } + if ((strict) && (!errors.isEmpty())) { result = isSigning ? rb.getString("jar.signed.with.signer.errors.") @@ -2422,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 b490d386e59b2..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,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 +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/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..d3537b844da5d --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/OutdatedKeyStoreWarning.java @@ -0,0 +1,124 @@ +/* + * 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 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 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); + }); + + 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); + }); + + 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); + }); + } + } + + private static void checkWarnings(String type, RunnableWithException r) throws Exception { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream origErr = System.err; + PrintStream origOut = System.out; + + 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; + } +} 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(