diff --git a/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java b/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java index 14a56e33f38..d4bab44434b 100644 --- a/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java +++ b/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java @@ -210,6 +210,8 @@ public CodeSigner[] verify(Hashtable verifiedSigners, JarConstraintsParameters params = getParams(verifiedSigners, sigFileSigners); + // If only disabled algorithms are used. + boolean onlyDisabledAlgs = (params == null) ? false : true; for (int i=0; i < digests.size(); i++) { MessageDigest digest = digests.get(i); if (params != null) { @@ -218,11 +220,11 @@ public CodeSigner[] verify(Hashtable verifiedSigners, name + " entry"); DisabledAlgorithmConstraints.jarConstraints() .permits(digest.getAlgorithm(), params, false); + onlyDisabledAlgs = false; } catch (GeneralSecurityException e) { if (debug != null) { debug.println("Digest algorithm is restricted: " + e); } - return null; } } byte [] manHash = manifestHashes.get(i); @@ -241,6 +243,11 @@ public CodeSigner[] verify(Hashtable verifiedSigners, " digest error for "+name); } + // If there were only entries with disabled algorithms, return null. + if (onlyDisabledAlgs) { + return null; + } + // take it out of sigFileSigners and put it in verifiedSigners... signers = sigFileSigners.remove(name); if (signers != null) { diff --git a/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java b/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java index 4d191d7b3cd..8d718775cdb 100644 --- a/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java +++ b/test/jdk/java/util/jar/JarFile/jarVerification/MultiProviderTest.java @@ -40,16 +40,15 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import jdk.test.lib.JDKToolFinder; -import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Utils; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.security.SecurityUtils; import jdk.test.lib.util.JarUtils; import static java.nio.file.StandardOpenOption.CREATE; @@ -99,7 +98,7 @@ public static void main(String[] args) throws Throwable { public static void initialize() throws Throwable { if (signJars) { - genKey(); + SecurityUtils.genKeyPair(KEYSTORE, ALIAS, STOREPASS, KEYPASS, "rsa", "SHA256withRSA"); } for (int i = 0; i < NUM_JARS; i++) { String p = "FooProvider" + i; @@ -111,7 +110,8 @@ public static void initialize() throws Throwable { CompilerUtils.compile(javaPath, Path.of(System.getProperty("test.classes")), "-cp", Utils.TEST_CLASS_PATH); createJar(jarPath, p, List.of(p)); if (signJars) { - signJar(TEST_CLASS_PATH + File.separator + jarName); + SecurityUtils.signJarFile(TEST_CLASS_PATH + File.separator + jarName, + KEYSTORE, STOREPASS, KEYPASS, "SHA-256", ALIAS); } COMBO_CP += TEST_CLASS_PATH + File.separator + jarName + File.pathSeparator; } @@ -133,53 +133,5 @@ private static void createJar(Path jar, String provider, List files) thr } JarUtils.createJarFile(Path.of(TEST_CLASS_PATH, jar.getFileName().toString()), xdir); } - - private static void genKey() throws Throwable { - String keytool = JDKToolFinder.getJDKTool("keytool"); - Files.deleteIfExists(Paths.get(KEYSTORE)); - ProcessTools.executeCommand(keytool, - "-J-Duser.language=en", - "-J-Duser.country=US", - "-genkey", - "-keyalg", "rsa", - "-alias", ALIAS, - "-keystore", KEYSTORE, - "-keypass", KEYPASS, - "-dname", "cn=sample", - "-storepass", STOREPASS - ).shouldHaveExitValue(0); - } - - - private static OutputAnalyzer signJar(String jarName) throws Throwable { - List args = new ArrayList<>(); - args.add("-verbose"); - args.add(jarName); - args.add(ALIAS); - - return jarsigner(args); - } - - private static OutputAnalyzer jarsigner(List extra) - throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner") - .addVMArg("-Duser.language=en") - .addVMArg("-Duser.country=US") - .addToolArg("-keystore") - .addToolArg(KEYSTORE) - .addToolArg("-storepass") - .addToolArg(STOREPASS) - .addToolArg("-keypass") - .addToolArg(KEYPASS); - for (String s : extra) { - if (s.startsWith("-J")) { - launcher.addVMArg(s.substring(2)); - } else { - launcher.addToolArg(s); - } - } - return ProcessTools.executeCommand(launcher.getCommand()); - } - } diff --git a/test/jdk/sun/security/tools/jarsigner/JarWithOneNonDisabledDigestAlg.java b/test/jdk/sun/security/tools/jarsigner/JarWithOneNonDisabledDigestAlg.java new file mode 100644 index 00000000000..da9f1745f03 --- /dev/null +++ b/test/jdk/sun/security/tools/jarsigner/JarWithOneNonDisabledDigestAlg.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021, 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 8278851 + * @summary Correct signer logic for jars signed with multiple weak digestalgs + * @library /test/lib + * @build jdk.test.lib.util.JarUtils + * jdk.test.lib.security.SecurityUtils + * @run main/othervm JarWithOneNonDisabledDigestAlg + */ + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.CodeSigner; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import jdk.test.lib.security.SecurityUtils; +import jdk.test.lib.util.JarUtils; + +public class JarWithOneNonDisabledDigestAlg { + + static final String JARNAME = "signed.jar"; + private static final String KEYSTORE = "keystore.jks"; + private static final String SHA256ALIAS = "SHA256ALIAS"; + private static final String SHA1ALIAS = "SHA1ALIAS"; + private static final String MD5ALIAS = "MD5ALIAS"; + private static final String STOREPASS = "changeit"; + private static final String KEYPASS = "changeit"; + private static final String TESTFILE = "testfile"; + + public static void main(String[] args) throws Throwable { + SecurityUtils.removeFromDisabledAlgs("jdk.jar.disabledAlgorithms", List.of("SHA1")); + Files.write(Path.of(TESTFILE), "testFile".getBytes()); + JarUtils.createJarFile(Path.of(JARNAME), Path.of("."), Path.of(TESTFILE)); + SecurityUtils.genKeyPair(KEYSTORE, SHA256ALIAS, STOREPASS, KEYPASS, "rsa", "SHA256withRSA"); + SecurityUtils.genKeyPair(KEYSTORE, SHA1ALIAS, STOREPASS, KEYPASS, "rsa", "SHA1withRSA"); + SecurityUtils.genKeyPair(KEYSTORE, MD5ALIAS, STOREPASS, KEYPASS, "rsa", "MD5withRSA"); + + SecurityUtils.signJarFile(JARNAME, KEYSTORE, STOREPASS, KEYPASS, "MD5", SHA1ALIAS).shouldHaveExitValue(0); + SecurityUtils.signJarFile(JARNAME, KEYSTORE, STOREPASS, KEYPASS, "SHA1", SHA1ALIAS).shouldHaveExitValue(0); + //SecurityUtils.signJarFile(JARNAME, KEYSTORE, STOREPASS, KEYPASS, "SHA-256", SHA1ALIAS).shouldHaveExitValue(0); + + + try (JarFile jf = new JarFile(JARNAME, true)) { + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.isDirectory() || isSigningRelated(entry.getName())) { + continue; + } + InputStream is = jf.getInputStream(entry); + while (is.read() != -1); + CodeSigner[] signers = entry.getCodeSigners(); + if (signers == null) { + throw new Exception("JarEntry " + entry.getName() + + " is not signed"); + } + } + } + } + + private static boolean isSigningRelated(String name) { + name = name.toUpperCase(Locale.ENGLISH); + if (!name.startsWith("META-INF/")) { + return false; + } + name = name.substring(9); + if (name.indexOf('/') != -1) { + return false; + } + return name.endsWith(".SF") + || name.endsWith(".DSA") + || name.endsWith(".RSA") + || name.endsWith(".EC") + || name.equals("MANIFEST.MF"); + } +} + diff --git a/test/lib/jdk/test/lib/security/SecurityUtils.java b/test/lib/jdk/test/lib/security/SecurityUtils.java index c0818d7662a..22d55acb3a8 100644 --- a/test/lib/jdk/test/lib/security/SecurityUtils.java +++ b/test/lib/jdk/test/lib/security/SecurityUtils.java @@ -24,12 +24,19 @@ package jdk.test.lib.security; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.KeyStore; import java.security.Security; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + /** * Common library for various security test helper functions. */ @@ -104,5 +111,98 @@ private static boolean anyMatch(String value, List algs) { return false; } + /** + * Sign a jar file (overwrite original file) + * + * @param src the original jar file name + * @param ks keystore to use + * @param storePass the keystore password + * @param keyPass the key password + * @param digestAlg digest algorithm + * @param alias alias to use + * + * @throws IOException + */ + public static OutputAnalyzer signJarFile(String src, String ks, + String storePass, String keyPass, String digestAlg, String alias) throws Throwable { + return signJarFile(src, src, ks, storePass, keyPass, digestAlg, alias); + } + + /** + * Sign a jar file + * + * @param src the original jar file name + * @param dest the new/signed jar file name + * @param ks keystore to use + * @param storePass the keystore password + * @param keyPass the key password + * @param digestAlg digest algorithm + * @param alias alias to use + * + * @throws Throwable + */ + public static OutputAnalyzer signJarFile(String src, String dest, String ks, + String storePass, String keyPass, String digestAlg, String alias) throws Throwable { + List args = new ArrayList<>(); + args.add("-verbose"); + args.add("-signedjar"); + args.add(dest); + args.add("-keystore"); + args.add(ks); + args.add("-storepass"); + args.add(storePass); + args.add("-keypass"); + args.add(keyPass); + args.add("-digestalg"); + args.add(digestAlg); + args.add(src); + args.add(alias); + return jarsigner(args).shouldHaveExitValue(0); + } + + private static OutputAnalyzer jarsigner(List extra) + throws Throwable { + jdk.test.lib.JDKToolLauncher launcher = jdk.test.lib.JDKToolLauncher.createUsingTestJDK("jarsigner") + .addVMArg("-Duser.language=en") + .addVMArg("-Duser.country=US"); + for (String s : extra) { + if (s.startsWith("-J")) { + launcher.addVMArg(s.substring(2)); + } else { + launcher.addToolArg(s); + } + } + return ProcessTools.executeCommand(launcher.getCommand()); + } + + /** + * Generate a key pair and store in specified keystore + * + * @param ks path to keystore + * @param alias alias to use in new key pair + * @param storepass storepass + * @param keypass keypass + * @param keyAlg key algorithm + * @param sigAlg signature algorithm + * + * @throws Throwable + */ + public static void genKeyPair(String ks, String alias, String storepass, + String keypass, String keyAlg, String sigAlg) throws Throwable { + String keytool = jdk.test.lib.JDKToolFinder.getJDKTool("keytool"); + jdk.test.lib.process.ProcessTools.executeCommand(keytool, + "-J-Duser.language=en", + "-J-Duser.country=US", + "-genkeypair", + "-keyalg", keyAlg, + "-sigalg", sigAlg, + "-alias", alias, + "-keystore", ks, + "-keypass", keypass, + "-dname", "cn=sample", + "-storepass", storepass + ).shouldHaveExitValue(0); + } + private SecurityUtils() {} }