8179614: Test for jarsigner on verifying jars that are signed and timestamped by other JDK releases

A test on checking the compatibility on jarsigner cross different JDK releases

Reviewed-by: mullan
This commit is contained in:
John Jiang 2017-08-15 19:19:50 -07:00 committed by Hamlin Li
parent f413915e19
commit 55fe55c4ff
7 changed files with 1804 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 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 java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* A custom output stream that redirects the testing outputs to a file, called
* details.out. It also calls another output stream to save some outputs to
* other files.
*/
public class DetailsOutputStream extends FileOutputStream {
private PhaseOutputStream phaseOutputStream = new PhaseOutputStream();
public DetailsOutputStream() throws FileNotFoundException {
super("details.out", true);
}
public void transferPhase() throws IOException {
if (phaseOutputStream.isCorePhase()) {
phaseOutputStream.write(HtmlHelper.endHtml());
phaseOutputStream.write(HtmlHelper.endPre());
}
phaseOutputStream.transfer();
if (phaseOutputStream.isCorePhase()) {
phaseOutputStream.write(HtmlHelper.startHtml());
phaseOutputStream.write(HtmlHelper.startPre());
}
}
@Override
public void write(byte[] b) throws IOException {
super.write(b);
phaseOutputStream.write(b);
}
@Override
public void write(int b) throws IOException {
super.write(b);
phaseOutputStream.write(b);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
super.write(b, off, len);
phaseOutputStream.write(b, off, len);
}
public void writeAnchorName(String name, String text) throws IOException {
super.write((text).getBytes());
super.write('\n');
phaseOutputStream.write(HtmlHelper.anchorName(name, text));
phaseOutputStream.write('\n');
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 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.
*/
/*
* A helper that is used for creating HTML elements.
*/
public class HtmlHelper {
private static final String STYLE
= "style=\"font-family: Courier New; "
+ "font-size: 12px; "
+ "white-space: pre-wrap\"";
public static String htmlRow(String... values) {
StringBuilder row = new StringBuilder();
row.append(startTr());
for (String value : values) {
row.append(startTd());
row.append(value);
row.append(endTd());
}
row.append(endTr());
return row.toString();
}
public static String startHtml() {
return startTag("html");
}
public static String endHtml() {
return endTag("html");
}
public static String startPre() {
return startTag("pre " + STYLE);
}
public static String endPre() {
return endTag("pre");
}
public static String startTable() {
return startTag("table " + STYLE);
}
public static String endTable() {
return endTag("table");
}
public static String startTr() {
return startTag("tr");
}
public static String endTr() {
return endTag("tr");
}
public static String startTd() {
return startTag("td");
}
public static String endTd() {
return endTag("td");
}
public static String startTag(String tag) {
return "<" + tag + ">";
}
public static String endTag(String tag) {
return "</" + tag + ">";
}
public static String anchorName(String name, String text) {
return "<a name=" + name + ">" + text + "</a>";
}
public static String anchorLink(String file, String anchorName,
String text) {
return "<a href=" + file + "#" + anchorName + ">" + text + "</a>";
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 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 java.security.NoSuchAlgorithmException;
import java.security.Signature;
/*
* This class is used for returning some specific JDK information.
*/
public class JdkUtils {
static final String M_JAVA_RUNTIME_VERSION = "javaRuntimeVersion";
static final String M_IS_SUPPORTED_SIGALG = "isSupportedSigalg";
// Returns the JDK build version.
static String javaRuntimeVersion() {
return System.getProperty("java.runtime.version");
}
// Checks if the specified signature algorithm is supported by the JDK.
static boolean isSupportedSigalg(String sigalg) {
boolean isSupported = false;
try {
isSupported = Signature.getInstance(sigalg) != null;
} catch (NoSuchAlgorithmException e) { }
if (!isSupported) {
System.out.println(sigalg + " is not supported yet.");
}
return isSupported;
}
public static void main(String[] args) {
if (M_JAVA_RUNTIME_VERSION.equals(args[0])) {
System.out.print(javaRuntimeVersion());
} else if (M_IS_SUPPORTED_SIGALG.equals(args[0])) {
System.out.print(isSupportedSigalg(args[1]));
}
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 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 java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/*
* A custom output stream that saves the testing details to different files
* according to the current testing phase.
*/
public class PhaseOutputStream extends OutputStream {
public enum Phase {
PRE_SIGNING, // before jar signing
SIGNING, // jar signing
VERIFYING, // jar verifying
DELAY_VERIFYING, // jar verifying after certificates expire
POST_VERIFYING; // after jar verifying
}
private OutputStream signingOut = null;
private OutputStream verifyingOut = null;
private OutputStream delayVerifyingOut = null;
private Phase currentPhase = Phase.PRE_SIGNING;
public void transfer() {
switch (currentPhase) {
case PRE_SIGNING:
currentPhase = Phase.SIGNING;
break;
case SIGNING:
currentPhase = Phase.VERIFYING;
break;
case VERIFYING:
currentPhase = Compatibility.DELAY_VERIFY
? Phase.DELAY_VERIFYING
: Phase.POST_VERIFYING;
break;
case DELAY_VERIFYING:
currentPhase = Phase.POST_VERIFYING;
break;
case POST_VERIFYING:
currentPhase = Phase.POST_VERIFYING;
break;
}
}
// The core phases are SIGNING, VERIFYING and DELAY_VERIFYING.
public boolean isCorePhase() {
return currentPhase != PhaseOutputStream.Phase.PRE_SIGNING
&& currentPhase != PhaseOutputStream.Phase.POST_VERIFYING;
}
public Phase currentPhase() {
return currentPhase;
}
@Override
public void write(int b) throws IOException {
OutputStream output = phaseOut();
if (output != null) {
output.write(b);
}
}
@Override
public void write(byte[] b) throws IOException {
OutputStream output = phaseOut();
if (output != null) {
output.write(b);
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
OutputStream output = phaseOut();
if (output != null) {
output.write(b, off, len);
}
}
public void write(String str) throws IOException {
write(str.getBytes());
}
private OutputStream phaseOut() throws FileNotFoundException {
switch (currentPhase) {
case SIGNING:
return signingOut == null
? signingOut = createOutput(Phase.SIGNING)
: signingOut;
case VERIFYING:
return verifyingOut == null
? verifyingOut = createOutput(Phase.VERIFYING)
: verifyingOut;
case DELAY_VERIFYING:
return delayVerifyingOut == null
? delayVerifyingOut = createOutput(Phase.DELAY_VERIFYING)
: delayVerifyingOut;
default:
return null;
}
}
@Override
public void flush() throws IOException {
flush(signingOut);
flush(verifyingOut);
flush(delayVerifyingOut);
}
private void flush(OutputStream output) throws IOException {
if (output != null) {
output.flush();
}
}
@Override
public void close() throws IOException {
close(signingOut);
close(verifyingOut);
close(delayVerifyingOut);
}
private void close(OutputStream output) throws IOException {
if (output != null) {
output.close();
}
}
private static OutputStream createOutput(Phase phase)
throws FileNotFoundException {
return new FileOutputStream(fileName(phase), true);
}
public static String fileName(Phase phase) {
return phase.name() + ".html";
}
}

View File

@ -0,0 +1,215 @@
# Copyright (c) 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.
##### Summary #####
This test is used to verify the compatibility on jarsigner cross different JDK
releases. It also can be used to check jar signing (w/ and w/o TSA) and verifying
on some specific key algorithms and digest algorithms.
##### Output #####
The test will generate a report, at JTwork/scratch/report.html, to display the
key parameters for signing and the status of signing and verifying. And it will
generate another report, at JTwork/scratch/failedReport.html, to collect all of
failed cases.
Please note that, the test may output a great deal of logs if the jdk list and
TSA list are big, and that would lead to jtreg output overflow. So, it redirects
stdout and stderr to file JTwork/scratch/details.out.
##### Report Columns #####
Certificate
Certificate identifier. The identifier consists of specific attributes of
the certificate. Generally, the naming convention is:
KeyAlgorithm_DigestAlgorithm_[KeySize][_Expired]
Signer JDK
The JDK version that signs jar.
Signature Algorithm
The signature algorithm used by signing.
TSA Digest
The timestamp digest algorithm used by signing.
TSA
TSA URL index. All of TSA URLs and their indices can be found at the top
of this report.
Signing Status
Signing process result status. The status are the followings:
[1]NONE, no action.
[2]NORMAL, no any error and warning.
[3]WARNING, no any error but some warnings raise.
[4]ERROR, some errors raise.
Verifier JDK
The JDK version that verifies signed jars.
Verifying Status
Verifying process result status. The status are the same as those for
"Status of Signing".
Delay Verifying Status
Delay verifying process result status. The status are the same as those
for "Status of Signing".
Failed
It highlights which case fails. The failed cases (rows) are marked with
letter X.
##### Usages #####
jtreg [-options] \
-jdk:<path/to/testing/JDK>
[-DproxyHost=<host> \
-DproxyPort=<port> \
-DtsaListFile=</url/to/tsaListFile> \
-DtsaList=</path/to/tsa1#/path/to/tsa2#/path/to/tsa3#...> \
-DjdkListFile=</path/to/jdkListFile> \
-DjdkList=</path/to/jdk1#/path/to/jdk2#/path/to/jdk3#...> \
-DjavaSecurityFile=</path/to/java/security/properties/file> \
-DdelayVerify=<true|false> \
-DcertValidity=<[1, 1440]>] \
<JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
Besides the common jtreg options, like -jdk, this test introduces a set of
properties for receiving users' inputs and making the test more flexible. These
properties are:
proxyHost=<host>
This property indicates proxy host.
proxyPort=<port>
This property indicates proxy port. The default value is 80.
tsaListFile=</path/to/tsaListFile>
This property indicates a local file, which contains a set of TSA URLs and
the supported digest algorithms (by optional parameter digests). The format
of the file content looks like the below,
http://path/to/tsa1
http://path/to/tsa2;digests=SHA-1,SHA-256
https://path/to/tsa3
...
If a TSA line does not list the supported digest algorithms, that means
the TSA supports SHA-1, SHA-256 and SHA-512. Because the test only focus
on SHA-1, SHA-256 and SHA-512. So, if other digest algorithms, like SHA-224
and SHA-384, are listed, they just be ignored.
tsaList=</path/to/tsa1#/path/to/tsa2;digests=SHA-1,SHA-256#...>
This property directly lists a set of TSAs in command. "#" is the delimiter.
Note that, if both of tsaListFile and tsaList are specified, only property
jdkListFile is selected. If neither of tsaListFile and tsaList is specified,
the test will fails immediately.
jdkListFile=</path/to/jdkListFile>
This property indicates a local file, which contains a set of local JDK
paths. The style of the file content looks like the below,
/path/to/jdk1
/path/to/jdk2
/path/to/jdk3
...
jdkList=</path/to/jdk1#/path/to/jdk2#/path/to/jdk3#...>
This property directly lists a set of local JDK paths in command. "#" is
the delimiter.
Note that, if both of jdkListFile and jdkList are specified, only property
jdkListFile is selected. If neither of jdkListFile nor jdkList is specified,
the testing JDK, which is specified by jtreg option -jdk will be used as
the only one JDK in the JDK list.
javaSecurityFile=</path/to/java/security/properties/file>
This property indicates an alternative java security properties file. The
default file is the path of file java.scurity that is distributed with
this test.
delayVerify=<true|false>
This property indicates if doing an additional verifying after all of valid
certificates expire. The default value is false.
certValidity=<[1, 1440]>
This property indicates the remaining validity period in minutes for valid
certificates. The value range is [1, 1440]. The default value is 1440.
Note that, if delayVerify is false, this property doesn't take effect.
The testing JDK, which is specified by jtreg option "-jdk", should include the
fix for JDK-8163304. Otherwise, the signature algorithm and timestamp digest
algorithm cannot be extracted from verification output. And this JDK should
support as many as possible signature algorithms. Anyway the latest JDK build
is always recommended.
##### Examples #####
$ cat /path/to/jdkList
/path/to/jdk6u171-b05
/path/to/jdk7u161-b05
/path/to/jdk8u144-b01
/path/to/jdk9-179
$ cat /path/to/tsaList
http://timestamp.comodoca.com/rfc3161
http://sha256timestamp.ws.symantec.com/sha256/timestamp
http://tsa.starfieldtech.com
http://timestamp.entrust.net/TSS/RFC3161sha1TS;digests=SHA-1,SHA-256
http://timestamp.entrust.net/TSS/RFC3161sha2TS;digests=SHA-1,SHA-256
http://rfc3161timestamp.globalsign.com/advanced;digests=SHA-256,SHA-512
http://rfc3161timestamp.globalsign.com/standard
http://timestamp.globalsign.com/scripts/timstamp.dll
http://timestamp.globalsign.com/?signature=sha2;digests=SHA-256,SHA-512
http://timestamp.digicert.com
http://time.certum.pl
http://tsa.swisssign.net
http://zeitstempel.dfn.de
https://tsp.iaik.tugraz.at/tsp/TspRequest
$ jtreg -va -nr -timeout:100 \
-jdk:/path/to/latest/jdk \
-DproxyHost=<proxy> -DproxyPort=<port> \
-DjdkListFile=/path/to/jdkList \
-DtsaListFile=/path/to/tsaList \
-DdelayVerify=true -DcertValidity=60 \
<JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
The above is a comprehensive usage example. File "jdkList" lists the paths of
testing JDK builds, and file "tsaList" lists the URLs of TSA services. Some TSAs,
like http://timestamp.entrust.net/TSS/RFC3161sha1TS, specify the supported digest
algorithms. Other TSAs, which don't specify parameter digests, are regarded to
support SHA-1, SHA-256 and SHA-512. The test uses a proxy to access TSA services.
And it enables delay verifying and set the certificate validity period to 60
minutes. So, after the first verification is done, the test will wait for all
of valid certificates expire and then does verification again.
If don't want to provide such JDK list and TSA list files, the test allows to
specify JDKs and TSAs (via properties jdkList and tsaList respectively) in the
command directly, like the below style,
$ jtreg -va -nr -timeout:100 \
-jdk:/path/to/latest/jdk \
-DproxyHost=<proxy> -DproxyPort=<port> \
-DjdkList=/path/to/jdk6u171-b05#/path/to/jdk7u161-b05#/path/to/jdk8u144-b01#/path/to/jdk9-179 \
-DtsaList=http://timestamp.comodoca.com/rfc3161#http://timestamp.entrust.net/TSS/RFC3161sha1TS;digests=SHA-1,SHA-256 \
-DdelayVerify=true -DcertValidity=60 \
<JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java
Furthermore, here introduces one of the simplest usages. It doesn't specify any
JDK list, so the testing JDK, which is specified by jtreg option "-jdk", will
be tested. And it doesn't apply delay verifying, and no proxy is used, and use
only one TSA. Now, the command is pretty simple and looks like the followings,
$ jtreg -va -nr -timeout:100 \
-jdk:/path/to/latest/jdk \
-DtsaList=http://timestamp.comodoca.com/rfc3161 \
<JDK_REPO>/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java

View File

@ -0,0 +1,2 @@
jdk.certpath.disabledAlgorithms=MD2, MD5
jdk.jar.disabledAlgorithms=MD2, MD5