--- /dev/null 2017-06-05 16:01:50.000000000 +0800 +++ new/test/sun/security/tools/jarsigner/compatibility/Compatibility.java 2017-06-05 16:01:50.000000000 +0800 @@ -0,0 +1,840 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8177674 + * @summary This test is used to verify the compatibility on jarsigner cross + * different JDK releases. If property strict is true, it also checks whether + * the default timestamp digest algorithm is SHA-256, and all of verification + * must pass even if the jarsigner in a JDK build doesn't support a specific + * algorithm. + * + * The test will generate a report, at JTwork/scratch/testReport, to display + * the key parameters for signing and the status of signing and verifying. + * + * Please note that, the test may output a great deal of logs if the jdk list + * is big, and that would lead to jtreg output overflow. So, it redirects + * the stdout and stderr to file JTwork/scratch/test.out. + * + * The testing JDK, which is specified by jtreg option "-jdk", should include + * the fix for JDK-8163304. Otherwise, the timestamp digest algorithm cannot + * be extracted from verification output. + * + * Usage: jtreg [-options] \ + * [-Dstrict= \ + * -DproxyHost= -DproxyPort= \ + * -Dtsa= \ + * -DjdkListFile= \ + * -DjdkList=] \ + * /path/to/Compatibility.java + * + * Properties: + * 1. strict= + * If true, the test checks whether the default timestamp digest + * algorithm is SHA-256, and all of verification must pass even if + * the jarsigner in a JDK build doesn't support a specific algorithm. + * The default value is true. + * + * 2. proxyHost= + * This property indicates proxy host. + * + * 3. proxyPort= + * This property indicates proxy port. The default value is 80. + * + * 4. tsa= + * This property indicates a TSA service. It is mandatory. + * + * 5. 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 + * ... + * + * 6. jdkList= + * This property directly lists a set of local JDK paths in command. + * Note that, if both of jdkListFile and jdkList are specified, only + * property jdkListFile is selected. If neither of jdkListFile and + * jdkList is specified, the testing JDK, which is specified jtreg + * option -jdk will be used as the only one JDK in the JDK list. + * + * Report columns: + * 1. Signer JDK: The JDK version that signs jar. + * 2. Signature Algorithm: The signature algorithm used by signing. + * 3. TSA Digest Algorithm: The timestamp digest algorithm used by signing. + * 4. Cert Type: Certificate types. + * The types are the followings: + * [1] Valid, normal self-signed certificate. + * [2] Expired, expired self-signed certificate. + * 5. Status of Signing: Signing process result status. + * The status are the followings: + * [1]Normal, no any error and warning. + * [2]Warn, no any error but some warnings raise. + * [3]Error, some errors raise. + * 6. Verifier JDK: The JDK version that verifies signed jars. + * 7. Status of Verifying: Verifying process result status. The status + * are the same as those for "Status of Signing". + * 8. Failed: It highlights which case fails. The failed cases (rows) + * are marked with X. + * + * @modules java.base/sun.security.pkcs + * java.base/sun.security.timestamp + * java.base/sun.security.tools.keytool + * java.base/sun.security.util + * java.base/sun.security.x509 + * @library /test/lib /lib/testlibrary ../warnings + * @run main/manual/othervm Compatibility + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.JarUtils; + +public class Compatibility { + + private static final String TEST_FILE_NAME = "test"; + private static final String TEST_JAR_NAME = "test.jar"; + + private static final String TEST_SRC = System.getProperty("test.src"); + private static final String TEST_JDK = System.getProperty("test.jdk"); + private static final String TEST_JARSIGNER = jarsignerPath(TEST_JDK); + + // If true, the test has to check whether the default timestamp digest + // algorithm is SHA-256. And it doesn't allow any verification failure even + // if the jarsigner doesn't support a specific algorithm. + private static final boolean STRICT = Boolean.parseBoolean( + System.getProperty("strict", "true")); + + private static final String PROXY_HOST = System.getProperty("proxyHost"); + private static final String PROXY_PORT = System.getProperty("proxyPort", "80"); + private static final String TSA = System.getProperty("tsa"); + + // An alternative security properties file, which only contains two lines: + // jdk.certpath.disabledAlgorithms=MD2, MD5 + // jdk.jar.disabledAlgorithms=MD2, MD5 + private static final String JAVA_SECURITY = TEST_SRC + "/java.security"; + + private static final String ALIAS_PRE = "test"; + private static final String PASSWORD = "testpass"; + private static final String KEYSTORE = "testKeystore"; + + private static final String RSA = "RSA"; + private static final String DSA = "DSA"; + private static final String DEFAULT = "DEFAULT"; + private static final String[] SIGNATURE_ALGORITHMS = new String[] { + "SHA1withRSA", "SHA256withRSA", "SHA1withDSA", "SHA256withDSA" }; + private static final String[] TSA_DIGEST_ALGORITHMS = new String[] { + DEFAULT, "SHA-1", "SHA-256" }; + + private static final String VALID_CERT = "Valid"; + private static final String EXPIRED_CERT = "Expired"; + private static final String[] CERT_TYPES + = new String[] { VALID_CERT, EXPIRED_CERT }; + private static final Map CERTS + = new HashMap(); + + private static final List JDK_INFOS = new ArrayList(); + private static final List SIGN_ITEMS = new ArrayList(); + + // Create a jar file that contains an empty file. + private static void createJar() throws IOException { + new File(TEST_FILE_NAME).createNewFile(); + JarUtils.createJar(TEST_JAR_NAME, TEST_FILE_NAME); + } + + // Create/Update a key store that adds a certificate with specific algorithm. + private static void createCertificate(String keyAlgorithm, + String certType) throws Throwable { + String nameSuffix = certType + keyAlgorithm; + String alias = ALIAS_PRE + nameSuffix; + OutputAnalyzer outputAnalyzer = exec( + TEST_JDK + "/bin/keytool", + "-J-Djava.security.properties=" + JAVA_SECURITY, + "-v", + "-storetype", "jks", + "-genkey", + "-keyalg", keyAlgorithm, + "-sigalg", "SHA1with" + keyAlgorithm, + "-keysize", "1024", + "-dname", "CN=Test" + nameSuffix, + "-alias", alias, + "-keypass", PASSWORD, + "-storepass", PASSWORD, + "-startdate", "-2d", + "-validity", certType == EXPIRED_CERT ? "1" : "3650", + "-keystore", KEYSTORE); + if (outputAnalyzer.getExitValue() == 0 + && !outputAnalyzer.getOutput().matches("[Ee]xception")) { + CERTS.put(new CertType(keyAlgorithm, certType), alias); + } + } + + public static void main(String[] args) throws Throwable { + if (TSA == null || TSA.isEmpty()) { + throw new RuntimeException("TSA service is mandatory."); + } + + // Redirects the output to a file, named test.out. + PrintStream out = new PrintStream( + new FileOutputStream("test.out", true)); + System.setOut(out); + System.setErr(out); + + createJar(); + + // Create a key store that includes valid/expired RSA/DSA certificates. + createCertificate(RSA, VALID_CERT); + createCertificate(RSA, EXPIRED_CERT); + createCertificate(DSA, VALID_CERT); + createCertificate(DSA, EXPIRED_CERT); + + String[] jdkPaths = jdkList(); + signJarByJDKs(jdkPaths); + List reportItems = verifyJarByJDKs(jdkPaths); + boolean failed = generateReport(reportItems); + + if (failed) { + throw new RuntimeException("Test failed. " + + "Please check the failed row(s) in testReport " + + "and more details in test.out."); + } + } + + // Retrieves JDK paths from the file which is specified by property jdkListFile, + // or from property jdkList if jdkListFile is not available. + private static String[] jdkList() throws IOException { + String jdkListFile = System.getProperty("jdkListFile"); + if (jdkListFile != null) { + System.out.println("JDK List file: " + jdkListFile); + List jdkPaths = new ArrayList(); + BufferedReader reader = new BufferedReader( + new FileReader(jdkListFile)); + String line; + while ((line = reader.readLine()) != null) { + String jdkPath = line.trim(); + if (!jdkPath.isEmpty()) { + jdkPaths.add(jdkPath); + } + } + reader.close(); + return jdkPaths.toArray(new String[0]); + } + + String jdkList = System.getProperty("jdkList", TEST_JDK); + System.out.println("JDK List:\n" + jdkList); + String[] jdkPaths = jdkList.split(","); + return jdkPaths; + } + + private static void signJarByJDKs(String[] jdkPaths) throws Throwable { + for (String signerJdkPath : jdkPaths) { + JdkInfo jdkInfo = new JdkInfo(signerJdkPath); + JDK_INFOS.add(jdkInfo); + + for (String sigAlg : SIGNATURE_ALGORITHMS) { + for (String tsaDigest : TSA_DIGEST_ALGORITHMS) { + // If the JDK doesn't support option -tsadigestalg, + // the associated cases just be ignored. + if (tsaDigest != DEFAULT && !jdkInfo.supportsTsadigestalg) { + continue; + } + + for (String certType : CERT_TYPES) { + String alias = CERTS.get(new CertType( + sigAlg.contains(RSA) ? RSA : DSA, certType)); + String signedJarPath = jdkInfo.version + "_" + sigAlg + + "_" + tsaDigest + "_" + certType + ".jar"; + String tsadigestalg = default2Null(tsaDigest); + + String signOutput = signJar(jdkInfo.jarsignerPath, + sigAlg, tsadigestalg, alias, signedJarPath); + STATUS status = signStatus(signOutput); + SignItem signItem = null; + if (status != STATUS.ERROR) { + signItem = SignItem.build() + .version(jdkInfo.version) + .signatureAlgorithm(sigAlg) + .tsaDigestAlgorithm(tsadigestalg) + .certType(certType).status(status) + .signedJarPath(signedJarPath); + SIGN_ITEMS.add(signItem); + + // Using the testing JDK, which is specified by + // jtreg option "-jdk", to verify the signed jar + // and extract the timestamp digest algorithm. + String verifyOutput = verifyJar(TEST_JARSIGNER, + signedJarPath); + signItem.extractedTsaDigestAlgorithm( + extract(verifyOutput, + " *Timestamp digest algorithm.*", + ".*(: )| \\(.*")); + } else { + jdkInfo.addUnsupportedSigAlg(sigAlg); + } + } + } + } + } + } + + private static List verifyJarByJDKs(String[] jdkPaths) + throws Throwable { + List reportItems = new ArrayList(); + for (String verifierJdkPath : jdkPaths) { + JdkInfo verifierJdkInfo = JDK_INFOS.get( + JDK_INFOS.indexOf(new JdkInfo(verifierJdkPath))); + + for (String sigalg : SIGNATURE_ALGORITHMS) { + for (String tsaDigest : TSA_DIGEST_ALGORITHMS) { + // If property strict is false and the JDK doesn't support + // the signature algorithm, then the case just be ignored. + if (!STRICT + && verifierJdkInfo.unsupportedSigAlgs.contains(sigalg)) { + continue; + } + + for (String certType : CERT_TYPES) { + for (String signerJdkPath : jdkPaths) { + SignItem signItem = findSignItem(SIGN_ITEMS, + signerJdkPath, sigalg, tsaDigest, certType); + // If signItem is null, it means the signing process + // failed, so there is no associated signed jar. Then + // it cannot do verifying. + if (signItem != null) { + String verifyOutput = verifyJar( + verifierJdkInfo.jarsignerPath, + signItem.signedJarPath); + STATUS verifyStatus = verifyStatus( + verifyOutput); + if (verifyStatus != STATUS.ERROR) { + verifyStatus = (STRICT + && signItem.tsaDigestAlgorithm == DEFAULT + && signItem.extractedTsaDigestAlgorithm != null + && !signItem.extractedTsaDigestAlgorithm + .matches("SHA-?256")) + ? STATUS.ERROR + : verifyStatus; + } + VerifyItem verifyItem = VerifyItem.build() + .version(verifierJdkInfo.version) + .status(verifyStatus); + ReportItem reportItem = new ReportItem(signItem, + verifyItem); + reportItems.add(reportItem); + System.out.println("Result:\n" + reportItem); + } + } + } + } + } + } + + return reportItems; + } + + // Finds a SignItem by the specified JDK, algorithms and certificate. + private static SignItem findSignItem(List signItems, + String signerJDKPath, String sigalg, String tsaDigest, + String certType) throws Throwable { + SignItem dummySignItem = SignItem.build() + .version(javaVersion(signerJDKPath)) + .signatureAlgorithm(sigalg) + .tsaDigestAlgorithm(default2Null(tsaDigest)) + .certType(certType); + int index = signItems.indexOf(dummySignItem); + return index != -1 ? signItems.get(index) : null; + } + + private static String default2Null(String tsaDigest) { + return !DEFAULT.equals(tsaDigest) ? tsaDigest : null; + } + + // Determines the status of signing. + private static STATUS signStatus(String output) { + if (output.contains(Test.JAR_SIGNED)) { + if (output.contains(Test.HAS_EXPIRED_CERT_SIGNING_WARNING)) { + return STATUS.WARNING; + } else { + return STATUS.NORMAL; + } + } else { + return STATUS.ERROR; + } + } + + // Determines the status of verifying. + private static STATUS verifyStatus(String output) { + if (output.contains(Test.JAR_VERIFIED)) { + if (output.contains(Test.WARNING)) { + return STATUS.WARNING; + } else { + return STATUS.NORMAL; + } + } else { + return STATUS.ERROR; + } + } + + // Extracts string from text by specified patterns. + private static String extract(String text, String linePattern, + String replacePattern) { + Matcher lineMatcher = Pattern.compile(linePattern).matcher(text); + if (lineMatcher.find()) { + String line = lineMatcher.group(0); + return line.replaceAll(replacePattern, ""); + } else { + return null; + } + } + + // Extracts build version from java version info. + private static String javaVersion(String jdkPath) throws Throwable { + OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand( + jdkPath + "/bin/java", + "-version"); + String version = extract(outputAnalyzer.getOutput(), + "Java\\(TM\\) SE Runtime Environment.*", + "(.*\\(.* )|\\)"); + return version != null ? version : "N/A"; + } + + // Checks if the jarsigner supports option -tsadigestalg. + private static boolean supportsTsadigestalg(String jarsignerPath) + throws Throwable { + OutputAnalyzer outputAnalyzer = exec(jarsignerPath, "-help"); + return outputAnalyzer.getOutput().contains("-tsadigestalg"); + } + + // Using specified jarsigner to sign the pre-created jar with specified + // algorithms. + private static String signJar(String jarsignerPath, String sigalg, + String tsadigestalg, String alias, String signedJarPath) + throws Throwable { + List arguments = new ArrayList(); + if (PROXY_HOST != null && PROXY_PORT != null) { + arguments.add("-J-Dhttp.proxyHost=" + PROXY_HOST); + arguments.add("-J-Dhttp.proxyPort=" + PROXY_PORT); + arguments.add("-J-Dhttps.proxyHost=" + PROXY_HOST); + arguments.add("-J-Dhttps.proxyPort=" + PROXY_PORT); + } + arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY); + arguments.add("-verbose"); + if (sigalg != null) { + arguments.add("-sigalg"); + arguments.add(sigalg); + } + arguments.add("-tsa"); + arguments.add(TSA); + if (tsadigestalg != null) { + arguments.add("-tsadigestalg"); + arguments.add(tsadigestalg); + } + arguments.add("-keystore"); + arguments.add(KEYSTORE); + arguments.add("-storepass"); + arguments.add(PASSWORD); + arguments.add("-signedjar"); + arguments.add(signedJarPath); + arguments.add(TEST_JAR_NAME); + arguments.add(alias); + + OutputAnalyzer outputAnalyzer = exec( + jarsignerPath, + arguments.toArray(new String[arguments.size()])); + return outputAnalyzer.getOutput(); + } + + // Using specified jarsigner to verify the signed jar. + private static String verifyJar(String jarsignerPath, String signedJarPath) + throws Throwable { + OutputAnalyzer outputAnalyzer = exec( + jarsignerPath, + "-J-Djava.security.properties=" + JAVA_SECURITY, + "-verbose", "-certs", + "-keystore", KEYSTORE, + "-verify", signedJarPath); + return outputAnalyzer.getOutput(); + } + + // Generates the test result report. + private static boolean generateReport(List reportItems) + throws IOException { + System.out.println("Report is being generated..."); + + StringBuilder report = new StringBuilder(); + + // Generates report headers. + report.append(ReportHeader.HEADERS).append("\n"); + + boolean failed = false; + + // Generates report rows. + for (ReportItem item : reportItems) { + failed = failed || item.verifyItem.status == STATUS.ERROR; + report.append(item).append("\n"); + } + + FileWriter writer = new FileWriter(new File("testReport")); + writer.write(report.toString()); + writer.close(); + + System.out.println("Report is generated."); + return failed; + } + + private static String jarsignerPath(String jdkPath) { + return jdkPath + "/bin/jarsigner"; + } + + // Executes command against the specified JDK tool, and ensures the output + // is US English. + private static OutputAnalyzer exec(String toolPath, String... args) + throws Throwable { + String[] cmd = new String[args.length + 3]; + cmd[0] = toolPath; + cmd[1] = "-J-Duser.language=en"; + cmd[2] = "-J-Duser.country=US"; + System.arraycopy(args, 0, cmd, 3, args.length); + return ProcessTools.executeCommand(cmd); + } + + private static class JdkInfo { + + private final String jdkPath; + private final String jarsignerPath; + private final String version; + private final boolean supportsTsadigestalg; + private Set unsupportedSigAlgs = new HashSet(); + + private JdkInfo(String jdkPath) throws Throwable { + this.jdkPath = jdkPath; + version = javaVersion(jdkPath); + jarsignerPath = jarsignerPath(jdkPath); + supportsTsadigestalg = supportsTsadigestalg(jarsignerPath); + } + + private void addUnsupportedSigAlg(String sigalg) { + unsupportedSigAlgs.add(sigalg); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((jdkPath == null) ? 0 : jdkPath.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + JdkInfo other = (JdkInfo) obj; + if (jdkPath == null) { + if (other.jdkPath != null) + return false; + } else if (!jdkPath.equals(other.jdkPath)) + return false; + return true; + } + } + + private static class CertType { + + private final String keyAlgorithm; + private final String certType; + + private CertType(String keyAlgorithm, String certType) { + this.keyAlgorithm = keyAlgorithm; + this.certType = certType; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((certType == null) ? 0 : certType.hashCode()); + result = prime * result + + ((keyAlgorithm == null) ? 0 : keyAlgorithm.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CertType other = (CertType) obj; + if (certType == null) { + if (other.certType != null) + return false; + } else if (!certType.equals(other.certType)) + return false; + if (keyAlgorithm == null) { + if (other.keyAlgorithm != null) + return false; + } else if (!keyAlgorithm.equals(other.keyAlgorithm)) + return false; + return true; + } + } + + private static enum STATUS { + + // Signing/Verifying with error + ERROR, + + // jar is signed/verified with warning + WARNING, + + // jar is signed/verified without any warning and error + NORMAL + } + + private static class SignItem { + + private String version; + private String signatureAlgorithm; + private String tsaDigestAlgorithm; + // tsadigestalg that is extracted from verification output (if possible) + private String extractedTsaDigestAlgorithm; + private String certType; + private STATUS status; + private String signedJarPath; + + private static SignItem build() { + return new SignItem(); + } + + private SignItem version(String version) { + this.version = version; + return this; + } + + private SignItem signatureAlgorithm(String signatureAlgorithm) { + this.signatureAlgorithm = signatureAlgorithm; + return this; + } + + private SignItem tsaDigestAlgorithm(String tsaDigestAlgorithm) { + this.tsaDigestAlgorithm = tsaDigestAlgorithm; + return this; + } + + private SignItem extractedTsaDigestAlgorithm( + String extractedTsaDigestAlgorithm) { + this.extractedTsaDigestAlgorithm = extractedTsaDigestAlgorithm; + return this; + } + + private SignItem certType(String certType) { + this.certType = certType; + return this; + } + + private SignItem status(STATUS status) { + this.status = status; + return this; + } + + private SignItem signedJarPath(String signedJarPath) { + this.signedJarPath = signedJarPath; + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((certType == null) ? 0 : certType.hashCode()); + result = prime * result + ((signatureAlgorithm == null) ? 0 + : signatureAlgorithm.hashCode()); + result = prime * result + ((tsaDigestAlgorithm == null) ? 0 + : tsaDigestAlgorithm.hashCode()); + result = prime * result + + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SignItem other = (SignItem) obj; + if (certType == null) { + if (other.certType != null) + return false; + } else if (!certType.equals(other.certType)) + return false; + if (signatureAlgorithm == null) { + if (other.signatureAlgorithm != null) + return false; + } else if (!signatureAlgorithm.equals(other.signatureAlgorithm)) + return false; + if (tsaDigestAlgorithm == null) { + if (other.tsaDigestAlgorithm != null) + return false; + } else if (!tsaDigestAlgorithm.equals(other.tsaDigestAlgorithm)) + return false; + if (version == null) { + if (other.version != null) + return false; + } else if (!version.equals(other.version)) + return false; + return true; + } + } + + private static class VerifyItem { + + private String version; + private STATUS status; + + private static VerifyItem build() { + return new VerifyItem(); + } + + private VerifyItem version(String version) { + this.version = version; + return this; + } + + private VerifyItem status(STATUS status) { + this.status = status; + return this; + } + } + + private static class ReportHeader { + + // Header names + private static final String SIGNER_VERSION = "[Signer JDK]"; + private static final String SIGNATURE_ALGORITHM = "[Signature Algorithm]"; + private static final String TSA_DIGEST_ALGORITHM = "[TSA Digest Algorithm]"; + private static final String CERT_TYPE = "[Cert Type]"; + private static final String SIGNE_STATUS = "[Status of Signing]"; + private static final String VERIFIER_VERSION = "[Verifier JDK]"; + private static final String VERIFY_STATUS = "[Status of Verifying]"; + private static final String FAILED = "[Failed]"; + + // Header widths + private static final int W_SIGNER_VERSION = 16; + private static final int W_SIGNATURE_ALGORITHM = 23; + private static final int W_TSA_DIGEST_ALGORITHM + = TSA_DIGEST_ALGORITHM.length(); + private static final int W_CERT_TYPE = CERT_TYPE.length(); + private static final int W_SIGNED = SIGNE_STATUS.length(); + private static final int W_VERIFIER_VERSION = W_SIGNER_VERSION; + private static final int W_VERIFY_STATUS = VERIFY_STATUS.length(); + private static final int W_FAILED = FAILED.length(); + + private static final String DELIMITER = " "; + private static final String FORMAT + = "%-" + W_SIGNER_VERSION + "s" + DELIMITER + + "%-" + W_SIGNATURE_ALGORITHM + "s" + DELIMITER + + "%-" + W_TSA_DIGEST_ALGORITHM + "s" + DELIMITER + + "%-" + W_CERT_TYPE + "s" + DELIMITER + + "%-" + W_SIGNED + "s" + DELIMITER + + "%-" + W_VERIFIER_VERSION + "s" + DELIMITER + + "%-" + W_VERIFY_STATUS + "s" + DELIMITER + + "%-" + W_FAILED + "s"; + + private static final String HEADERS = String.format( + ReportHeader.FORMAT, + SIGNER_VERSION, + SIGNATURE_ALGORITHM, + TSA_DIGEST_ALGORITHM, + CERT_TYPE, + SIGNE_STATUS, + VERIFIER_VERSION, + VERIFY_STATUS, + FAILED); + + } + + private static class ReportItem { + + private final SignItem signItem; + private final VerifyItem verifyItem; + + private ReportItem(SignItem signItem, VerifyItem verifyItem) { + this.signItem = signItem; + this.verifyItem = verifyItem; + } + + @Override + public String toString() { + return String.format(ReportHeader.FORMAT, + signItem.version, + signItem.signatureAlgorithm, + null2Default(signItem.tsaDigestAlgorithm, + signItem.extractedTsaDigestAlgorithm), + signItem.certType, + signItem.status, + verifyItem.version, + verifyItem.status, + verifyItem.status == STATUS.ERROR ? "X" : ""); + } + + // If a value is null, then displays the default value or N/A. + private static String null2Default(String value, String defaultValue) { + return value == null + ? DEFAULT + "(" + (defaultValue == null + ? "N/A" + : defaultValue) + ")" + : value; + } + } +} --- /dev/null 2017-06-05 16:01:50.000000000 +0800 +++ new/test/sun/security/tools/jarsigner/compatibility/java.security 2017-06-05 16:01:50.000000000 +0800 @@ -0,0 +1,2 @@ +jdk.certpath.disabledAlgorithms=MD2, MD5 +jdk.jar.disabledAlgorithms=MD2, MD5 \ No newline at end of file