8180289: jarsigner treats timestamped signed jar invalid after the signer cert expires
Reviewed-by: mullan
/*
* Copyright (c) 2015, 2017, 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 jdk.testlibrary.JDKToolLauncher;
import jdk.test.lib.util.JarUtils;
import jdk.testlibrary.OutputAnalyzer;
import jdk.testlibrary.ProcessTools;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
/**
* @test
* @bug 8024302 8026037 8130132
* @summary warnings, errors and -strict
* @library /lib/testlibrary /test/lib
* @build jdk.test.lib.util.JarUtils
* @run main Warning
*/
public class Warning {
public static void main(String[] args) throws Throwable {
Files.deleteIfExists(Paths.get("ks"));
newCert("ca", "-validity 365000");
recreateJar();
newCert("a");
run("jarsigner", "a.jar a")
.shouldContain("is self-signed");
run("jarsigner", "a.jar a -strict")
.shouldContain("is self-signed")
.shouldHaveExitValue(4);
// Trusted entry can be self-signed without a warning
run("jarsigner", "-verify a.jar")
.shouldNotContain("is self-signed")
.shouldNotContain("not signed by alias in this keystore");
run("keytool", "-delete -alias a");
// otherwise a warning will be shown
run("jarsigner", "-verify a.jar")
.shouldContain("is self-signed")
.shouldContain("not signed by alias in this keystore");
recreateJar();
newCert("b");
issueCert("b");
run("jarsigner", "a.jar b")
.shouldNotContain("is self-signed");
run("jarsigner", "-verify a.jar")
.shouldNotContain("is self-signed");
run("jarsigner", "a.jar b -digestalg MD5")
.shouldContain("-digestalg option is considered a security risk.");
run("jarsigner", "a.jar b -digestalg MD5 -strict")
.shouldHaveExitValue(4)
.shouldContain("-digestalg option is considered a security risk.");
run("jarsigner", "a.jar b -sigalg MD5withRSA")
.shouldContain("-sigalg option is considered a security risk");
issueCert("b", "-sigalg MD5withRSA");
run("jarsigner", "a.jar b")
.shouldMatch("chain is invalid. Reason:.*MD5withRSA");
recreateJar();
newCert("c", "-keysize 512");
issueCert("c");
run("jarsigner", "a.jar c")
.shouldContain("chain is invalid. " +
"Reason: Algorithm constraints check failed");
recreateJar();
newCert("s1"); issueCert("s1", "-startdate 2000/01/01 -validity 36525");
run("jarsigner", "a.jar s1")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp").shouldContain("2100-01-01")
.shouldNotContain("with signer errors");
run("jarsigner", "a.jar s1 -strict")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp").shouldContain("2100-01-01")
.shouldNotContain("with signer errors");
run("jarsigner", "a.jar s1 -verify")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp").shouldContain("2100-01-01")
.shouldNotContain("with signer errors");
run("jarsigner", "a.jar s1 -verify -strict")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp").shouldContain("2100-01-01")
.shouldNotContain("with signer errors");
recreateJar();
newCert("s2"); issueCert("s2", "-validity 100");
run("jarsigner", "a.jar s2")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp")
.shouldContain("will expire")
.shouldNotContain("with signer errors");
run("jarsigner", "a.jar s2 -strict")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp")
.shouldContain("will expire")
.shouldNotContain("with signer errors");
run("jarsigner", "a.jar s2 -verify")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp")
.shouldContain("will expire")
.shouldNotContain("with signer errors");
run("jarsigner", "a.jar s2 -verify -strict")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("Error:")
.shouldContain("timestamp")
.shouldContain("will expire")
.shouldNotContain("with signer errors");
recreateJar();
newCert("s3"); issueCert("s3", "-startdate -200d -validity 100");
run("jarsigner", "a.jar s3")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldContain("has expired")
.shouldNotContain("with signer errors")
.shouldNotContain("Error:");
run("jarsigner", "a.jar s3 -strict")
.shouldHaveExitValue(4)
.shouldContain("with signer errors")
.shouldMatch("(?s).*Error:.*has expired.*Warning:.*");
run("jarsigner", "a.jar s3 -verify")
.shouldHaveExitValue(0)
.shouldContain("Warning:")
.shouldNotContain("with signer errors")
.shouldNotContain("Error:");
run("jarsigner", "a.jar s3 -verify -strict")
.shouldHaveExitValue(4)
.shouldContain("with signer errors")
.shouldMatch("(?s).*Error:.*has expired.*Warning:.*");
}
// Creates a new jar without signature
static void recreateJar() throws Exception {
JarUtils.createJar("a.jar", "ks");
}
// Creates a self-signed cert for alias with zero or more -genkey options
static void newCert(String alias, String... more) throws Exception {
String args = "-genkeypair -alias " + alias + " -dname CN=" + alias;
for (String s: more) {
args += " " + s;
}
run("keytool", args).shouldHaveExitValue(0);
}
// Asks ca to issue a cert to alias with zero or more -gencert options
static void issueCert(String alias, String...more) throws Exception {
String req = run("keytool", "-certreq -alias " + alias)
.shouldHaveExitValue(0).getStdout();
String args = "-gencert -alias ca -rfc";
for (String s: more) {
args += " " + s;
}
String cert = run("keytool", args, req)
.shouldHaveExitValue(0).getStdout();
run("keytool", "-import -alias " + alias, cert).shouldHaveExitValue(0);
}
// Runs a java tool with command line arguments
static OutputAnalyzer run(String command, String args)
throws Exception {
return run(command, args, null);
}
// Runs a java tool with command line arguments and an optional input block
static OutputAnalyzer run(String command, String args, String input)
throws Exception {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK(command);
launcher.addVMArg("-Duser.language=en").addVMArg("-Duser.country=US");
switch (command) {
case "keytool":
for (String s: new String[] {
"-keystore", "ks", "-storepass", "changeit",
"-storetype", "jks",
"-keypass", "changeit", "-keyalg", "rsa", "-debug"}) {
launcher.addToolArg(s);
}
break;
case "jarsigner":
for (String s: new String[] {
"-keystore", "ks", "-storepass", "changeit",
"-storetype", "jks"}) {
launcher.addToolArg(s);
}
break;
}
for (String arg: args.split(" ")) {
launcher.addToolArg(arg);
}
String[] cmd = launcher.getCommand();
ProcessBuilder pb = new ProcessBuilder(cmd);
OutputAnalyzer out = ProcessTools.executeProcess(pb, input);
System.out.println("======================");
System.out.println(Arrays.toString(cmd));
String msg = " stdout: [" + out.getStdout() + "];\n"
+ " stderr: [" + out.getStderr() + "]\n"
+ " exitValue = " + out.getExitValue() + "\n";
System.out.println(msg);
return out;
}
}