8300272: Improve readability of the test JarWithOneNonDisabledDigestAlg

Reviewed-by: weijun
This commit is contained in:
Eirik Bjorsnos 2023-01-24 22:16:28 +00:00 committed by Weijun Wang
parent b678e70003
commit cf46004f27

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023, 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
@ -27,6 +27,7 @@
* @summary Check that jar entry with at least one non-disabled digest
* algorithm in manifest is treated as signed
* @modules java.base/sun.security.tools.keytool
* @modules java.base/sun.security.util
* @library /test/lib
* @build jdk.test.lib.util.JarUtils
* jdk.test.lib.security.SecurityUtils
@ -34,61 +35,94 @@
*/
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.CodeSigner;
import java.security.KeyStore;
import java.security.cert.CertPathValidatorException;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import jdk.security.jarsigner.JarSigner;
import jdk.test.lib.util.JarUtils;
import jdk.test.lib.security.SecurityUtils;
import sun.security.util.ConstraintsParameters;
import sun.security.util.DisabledAlgorithmConstraints;
import sun.security.util.JarConstraintsParameters;
public class JarWithOneNonDisabledDigestAlg {
private static final String PASS = "changeit";
private static final String TESTFILE1 = "testfile1";
private static final String TESTFILE2 = "testfile2";
private static final Path TESTFILE1 = Path.of("testfile1");
private static final Path TESTFILE2 = Path.of("testfile2");
private static final Path UNSIGNED_JAR = Path.of("unsigned.jar");
private static final Path SIGNED_JAR = Path.of("signed.jar");
private static final Path SIGNED_TWICE_JAR = Path.of("signed2.jar");
private static final Path MULTI_SIGNED_JAR = Path.of("multi-signed.jar");
private static final Path CURRENT_DIR = Path.of(".");
public static void main(String[] args) throws Exception {
SecurityUtils.removeFromDisabledAlgs("jdk.jar.disabledAlgorithms",
List.of("SHA1"));
Files.write(Path.of(TESTFILE1), TESTFILE1.getBytes());
JarUtils.createJarFile(Path.of("unsigned.jar"), Path.of("."),
Path.of(TESTFILE1));
// Sanity check: Assert that MD5 is disabled, SHA-256 enabled
checkDigestAlgorithmPermits();
genkeypair("-alias SHA1 -sigalg SHA1withRSA");
genkeypair("-alias SHA256 -sigalg SHA256withRSA");
// Create an unsigned JAR with a single file
Files.write(TESTFILE1, TESTFILE1.toString().getBytes());
JarUtils.createJarFile(UNSIGNED_JAR, CURRENT_DIR, TESTFILE1);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream fis = new FileInputStream("keystore")) {
ks.load(fis, PASS.toCharArray());
}
// Generate a keystore with two different signers
genkeypair("-alias SIGNER1");
genkeypair("-alias SIGNER2");
KeyStore ks = loadKeyStore();
// Sign JAR twice with same signer but different digest algorithms
// so that each entry in manifest file contains two digest values.
signJarFile(ks, "SHA1", "MD5", "unsigned.jar", "signed.jar");
signJarFile(ks, "SHA1", "SHA1", "signed.jar", "signed2.jar");
checkThatJarIsSigned("signed2.jar", false);
// Note that MD5 is a disabled digest algorithm, while SHA-256 is not
signJarFile(ks, "SIGNER1", "MD5", UNSIGNED_JAR, SIGNED_JAR);
signJarFile(ks, "SIGNER1", "SHA256", SIGNED_JAR, SIGNED_TWICE_JAR);
checkThatJarIsSigned(SIGNED_TWICE_JAR, Map.of(TESTFILE1.toString(), 1));
// add another file to the JAR
Files.write(Path.of(TESTFILE2), "testFile2".getBytes());
JarUtils.updateJarFile(Path.of("signed2.jar"), Path.of("."),
Path.of(TESTFILE2));
Files.write(TESTFILE2, TESTFILE2.toString().getBytes());
JarUtils.updateJarFile(SIGNED_TWICE_JAR, CURRENT_DIR, TESTFILE2);
// Sign again with different signer (SHA256) and SHA-1 digestalg.
// TESTFILE1 should have two signers and TESTFILE2 should have one
// signer.
signJarFile(ks, "SHA256", "SHA1", "signed2.jar", "multi-signed.jar");
// Sign the updated JAR, now with a different signer and with an enabled digest alg
signJarFile(ks, "SIGNER2", "SHA256", SIGNED_TWICE_JAR, MULTI_SIGNED_JAR);
checkThatJarIsSigned("multi-signed.jar", true);
// TESTFILE1 should have two signers and TESTFILE2 should have one signer.
checkThatJarIsSigned(MULTI_SIGNED_JAR,
Map.of(TESTFILE1.toString(), 2,
TESTFILE2.toString(), 1)
);
}
private static void checkDigestAlgorithmPermits() throws Exception {
ConstraintsParameters cp = new JarConstraintsParameters(Collections.emptyList(), new Date());
DisabledAlgorithmConstraints jarConstraints = DisabledAlgorithmConstraints.jarConstraints();
try {
jarConstraints.permits("MD5", cp, false);
throw new Exception("This test assumes that MD5 is disabled");
} catch (CertPathValidatorException e) {
// Ignore
}
try {
jarConstraints.permits("SHA256", cp, false);
} catch (CertPathValidatorException e) {
throw new Exception("This test assumes that SHA256 is enabled");
}
}
private static KeyStore loadKeyStore() throws Exception {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream fis = Files.newInputStream(Path.of("keystore"))) {
ks.load(fis, PASS.toCharArray());
}
return ks;
}
private static KeyStore.PrivateKeyEntry getEntry(KeyStore ks, String alias)
@ -101,12 +135,13 @@ public class JarWithOneNonDisabledDigestAlg {
private static void genkeypair(String cmd) throws Exception {
cmd = "-genkeypair -keystore keystore -storepass " + PASS +
" -keypass " + PASS + " -keyalg rsa -dname CN=Duke " + cmd;
" -keypass " + PASS + " -keyalg rsa -sigalg SHA256withRSA " +
"-dname CN=Duke " + cmd;
sun.security.tools.keytool.Main.main(cmd.split(" "));
}
private static void signJarFile(KeyStore ks, String alias,
String digestAlg, String inputFile, String outputFile)
String digestAlg, Path inputFile, Path outputFile)
throws Exception {
JarSigner signer = new JarSigner.Builder(getEntry(ks, alias))
@ -114,38 +149,36 @@ public class JarWithOneNonDisabledDigestAlg {
.signerName(alias)
.build();
try (ZipFile in = new ZipFile(inputFile);
FileOutputStream out = new FileOutputStream(outputFile)) {
try (ZipFile in = new ZipFile(inputFile.toFile());
OutputStream out = Files.newOutputStream(outputFile)) {
signer.sign(in, out);
}
}
private static void checkThatJarIsSigned(String jarFile, boolean multi)
private static void checkThatJarIsSigned(Path jarFile, Map<String, Integer> expected)
throws Exception {
try (JarFile jf = new JarFile(jarFile, true)) {
try (JarFile jf = new JarFile(jarFile.toFile(), 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);
try (InputStream is = jf.getInputStream(entry)) {
is.transferTo(OutputStream.nullOutputStream());
}
CodeSigner[] signers = entry.getCodeSigners();
if (signers == null) {
throw new Exception("JarEntry " + entry.getName() +
" is not signed");
} else if (multi) {
if (entry.getName().equals(TESTFILE1) &&
signers.length != 2) {
throw new Exception("Unexpected number of signers " +
"for " + entry.getName() + ": " + signers.length);
} else if (entry.getName().equals(TESTFILE2) &&
signers.length != 1) {
throw new Exception("Unexpected number of signers " +
"for " + entry.getName() + ": " + signers.length);
}
if (!expected.containsKey(entry.getName())) {
throw new Exception("Unexpected entry " + entry.getName());
}
int expectedSigners = expected.get(entry.getName());
int actualSigners = signers == null ? 0 : signers.length;
if (expectedSigners != actualSigners) {
throw new Exception("Unexpected number of signers " +
"for " + entry.getName() + ": " + actualSigners +
", expected " + expectedSigners);
}
}
}