Skip to content
This repository has been archived by the owner on Sep 19, 2023. It is now read-only.
/ jdk18 Public archive

8278851: Correct signer logic for jars signed with multiple digestalgs #32

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ public CodeSigner[] verify(Hashtable<String, CodeSigner[]> 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) {
Expand All @@ -218,11 +220,11 @@ public CodeSigner[] verify(Hashtable<String, CodeSigner[]> 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);
Expand All @@ -241,6 +243,11 @@ public CodeSigner[] verify(Hashtable<String, CodeSigner[]> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -133,53 +133,5 @@ private static void createJar(Path jar, String provider, List<String> 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<String> args = new ArrayList<>();
args.add("-verbose");
args.add(jarName);
args.add(ALIAS);

return jarsigner(args);
}

private static OutputAnalyzer jarsigner(List<String> 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());
}

}

Original file line number Diff line number Diff line change
@@ -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<JarEntry> 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");
}
}

100 changes: 100 additions & 0 deletions test/lib/jdk/test/lib/security/SecurityUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -104,5 +111,98 @@ private static boolean anyMatch(String value, List<String> 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<String> 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<String> 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() {}
}