1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8177674 27 * @summary This test is used to verify the compatibility on jarsigner cross 28 * different JDK releases. If property strict is true, it also checks whether 29 * the default timestamp digest algorithm is SHA-256, and all of verification 30 * must pass even if the jarsigner in a JDK build doesn't support a specific 31 * algorithm. 32 * 33 * The test will generate a report, at JTwork/scratch/testReport, to display 34 * the key parameters for signing and the status of signing and verifying. 35 * 36 * Please note that, the test may output a great deal of logs if the jdk list 37 * is big, and that would lead to jtreg output overflow. So, it redirects 38 * the stdout and stderr to file JTwork/scratch/test.out. 39 * 40 * The testing JDK, which is specified by jtreg option "-jdk", should include 41 * the fix for JDK-8163304. Otherwise, the timestamp digest algorithm cannot 42 * be extracted from verification output. 43 * 44 * Usage: jtreg [-options] \ 45 * [-Dstrict=<true|false> \ 46 * -DproxyHost=<host> -DproxyPort=<port> \ 47 * -Dtsa=</url/to/TSA/service> \ 48 * -DjdkListFile=</path/to/jdkListFile> \ 49 * -DjdkList=</path/to/jdk1,/path/to/jdk2,/path/to/jdk3,...>] \ 50 * /path/to/Compatibility.java 51 * 52 * Properties: 53 * 1. strict=<true|false> 54 * If true, the test checks whether the default timestamp digest 55 * algorithm is SHA-256, and all of verification must pass even if 56 * the jarsigner in a JDK build doesn't support a specific algorithm. 57 * The default value is true. 58 * 59 * 2. proxyHost=<host> 60 * This property indicates proxy host. 61 * 62 * 3. proxyPort=<port> 63 * This property indicates proxy port. The default value is 80. 64 * 65 * 4. tsa=</url/to/TSA/service> 66 * This property indicates a TSA service. It is mandatory. 67 * 68 * 5. jdkListFile=</path/to/jdkListFile> 69 * This property indicates a local file, which contains a set of local 70 * JDK paths. The style of the file content looks like the below, 71 * /path/to/jdk1 72 * /path/to/jdk2 73 * /path/to/jdk3 74 * ... 75 * 76 * 6. jdkList=</path/to/jdk1,/path/to/jdk2,/path/to/jdk3,...> 77 * This property directly lists a set of local JDK paths in command. 78 * Note that, if both of jdkListFile and jdkList are specified, only 79 * property jdkListFile is selected. If neither of jdkListFile and 80 * jdkList is specified, the testing JDK, which is specified jtreg 81 * option -jdk will be used as the only one JDK in the JDK list. 82 * 83 * Report columns: 84 * 1. Signer JDK: The JDK version that signs jar. 85 * 2. Signature Algorithm: The signature algorithm used by signing. 86 * 3. TSA Digest Algorithm: The timestamp digest algorithm used by signing. 87 * 4. Cert Type: Certificate types. 88 * The types are the followings: 89 * [1] Valid, normal self-signed certificate. 90 * [2] Expired, expired self-signed certificate. 91 * 5. Status of Signing: Signing process result status. 92 * The status are the followings: 93 * [1]Normal, no any error and warning. 94 * [2]Warn, no any error but some warnings raise. 95 * [3]Error, some errors raise. 96 * 6. Verifier JDK: The JDK version that verifies signed jars. 97 * 7. Status of Verifying: Verifying process result status. The status 98 * are the same as those for "Status of Signing". 99 * 8. Failed: It highlights which case fails. The failed cases (rows) 100 * are marked with X. 101 * 102 * @modules java.base/sun.security.pkcs 103 * java.base/sun.security.timestamp 104 * java.base/sun.security.tools.keytool 105 * java.base/sun.security.util 106 * java.base/sun.security.x509 107 * @library /test/lib /lib/testlibrary ../warnings 108 * @run main/manual/othervm Compatibility 109 */ 110 111 import java.io.BufferedReader; 112 import java.io.File; 113 import java.io.FileOutputStream; 114 import java.io.FileReader; 115 import java.io.FileWriter; 116 import java.io.IOException; 117 import java.io.PrintStream; 118 import java.util.ArrayList; 119 import java.util.HashMap; 120 import java.util.HashSet; 121 import java.util.List; 122 import java.util.Map; 123 import java.util.Set; 124 import java.util.regex.Matcher; 125 import java.util.regex.Pattern; 126 127 import jdk.test.lib.process.OutputAnalyzer; 128 import jdk.test.lib.process.ProcessTools; 129 import jdk.test.lib.util.JarUtils; 130 131 public class Compatibility { 132 133 private static final String TEST_FILE_NAME = "test"; 134 private static final String TEST_JAR_NAME = "test.jar"; 135 136 private static final String TEST_SRC = System.getProperty("test.src"); 137 private static final String TEST_JDK = System.getProperty("test.jdk"); 138 private static final String TEST_JARSIGNER = jarsignerPath(TEST_JDK); 139 140 // If true, the test has to check whether the default timestamp digest 141 // algorithm is SHA-256. And it doesn't allow any verification failure even 142 // if the jarsigner doesn't support a specific algorithm. 143 private static final boolean STRICT = Boolean.parseBoolean( 144 System.getProperty("strict", "true")); 145 146 private static final String PROXY_HOST = System.getProperty("proxyHost"); 147 private static final String PROXY_PORT = System.getProperty("proxyPort", "80"); 148 private static final String TSA = System.getProperty("tsa"); 149 150 // An alternative security properties file, which only contains two lines: 151 // jdk.certpath.disabledAlgorithms=MD2, MD5 152 // jdk.jar.disabledAlgorithms=MD2, MD5 153 private static final String JAVA_SECURITY = TEST_SRC + "/java.security"; 154 155 private static final String ALIAS_PRE = "test"; 156 private static final String PASSWORD = "testpass"; 157 private static final String KEYSTORE = "testKeystore"; 158 159 private static final String RSA = "RSA"; 160 private static final String DSA = "DSA"; 161 private static final String DEFAULT = "DEFAULT"; 162 private static final String[] SIGNATURE_ALGORITHMS = new String[] { 163 "SHA1withRSA", "SHA256withRSA", "SHA1withDSA", "SHA256withDSA" }; 164 private static final String[] TSA_DIGEST_ALGORITHMS = new String[] { 165 DEFAULT, "SHA-1", "SHA-256" }; 166 167 private static final String VALID_CERT = "Valid"; 168 private static final String EXPIRED_CERT = "Expired"; 169 private static final String[] CERT_TYPES 170 = new String[] { VALID_CERT, EXPIRED_CERT }; 171 private static final Map<CertType, String> CERTS 172 = new HashMap<CertType, String>(); 173 174 private static final List<JdkInfo> JDK_INFOS = new ArrayList<JdkInfo>(); 175 private static final List<SignItem> SIGN_ITEMS = new ArrayList<SignItem>(); 176 177 // Create a jar file that contains an empty file. 178 private static void createJar() throws IOException { 179 new File(TEST_FILE_NAME).createNewFile(); 180 JarUtils.createJar(TEST_JAR_NAME, TEST_FILE_NAME); 181 } 182 183 // Create/Update a key store that adds a certificate with specific algorithm. 184 private static void createCertificate(String keyAlgorithm, 185 String certType) throws Throwable { 186 String nameSuffix = certType + keyAlgorithm; 187 String alias = ALIAS_PRE + nameSuffix; 188 OutputAnalyzer outputAnalyzer = exec( 189 TEST_JDK + "/bin/keytool", 190 "-J-Djava.security.properties=" + JAVA_SECURITY, 191 "-v", 192 "-storetype", "jks", 193 "-genkey", 194 "-keyalg", keyAlgorithm, 195 "-sigalg", "SHA1with" + keyAlgorithm, 196 "-keysize", "1024", 197 "-dname", "CN=Test" + nameSuffix, 198 "-alias", alias, 199 "-keypass", PASSWORD, 200 "-storepass", PASSWORD, 201 "-startdate", "-2d", 202 "-validity", certType == EXPIRED_CERT ? "1" : "3650", 203 "-keystore", KEYSTORE); 204 if (outputAnalyzer.getExitValue() == 0 205 && !outputAnalyzer.getOutput().matches("[Ee]xception")) { 206 CERTS.put(new CertType(keyAlgorithm, certType), alias); 207 } 208 } 209 210 public static void main(String[] args) throws Throwable { 211 if (TSA == null || TSA.isEmpty()) { 212 throw new RuntimeException("TSA service is mandatory."); 213 } 214 215 // Redirects the output to a file, named test.out. 216 PrintStream out = new PrintStream( 217 new FileOutputStream("test.out", true)); 218 System.setOut(out); 219 System.setErr(out); 220 221 createJar(); 222 223 // Create a key store that includes valid/expired RSA/DSA certificates. 224 createCertificate(RSA, VALID_CERT); 225 createCertificate(RSA, EXPIRED_CERT); 226 createCertificate(DSA, VALID_CERT); 227 createCertificate(DSA, EXPIRED_CERT); 228 229 String[] jdkPaths = jdkList(); 230 signJarByJDKs(jdkPaths); 231 List<ReportItem> reportItems = verifyJarByJDKs(jdkPaths); 232 boolean failed = generateReport(reportItems); 233 234 if (failed) { 235 throw new RuntimeException("Test failed. " 236 + "Please check the failed row(s) in testReport " 237 + "and more details in test.out."); 238 } 239 } 240 241 // Retrieves JDK paths from the file which is specified by property jdkListFile, 242 // or from property jdkList if jdkListFile is not available. 243 private static String[] jdkList() throws IOException { 244 String jdkListFile = System.getProperty("jdkListFile"); 245 if (jdkListFile != null) { 246 System.out.println("JDK List file: " + jdkListFile); 247 List<String> jdkPaths = new ArrayList<String>(); 248 BufferedReader reader = new BufferedReader( 249 new FileReader(jdkListFile)); 250 String line; 251 while ((line = reader.readLine()) != null) { 252 String jdkPath = line.trim(); 253 if (!jdkPath.isEmpty()) { 254 jdkPaths.add(jdkPath); 255 } 256 } 257 reader.close(); 258 return jdkPaths.toArray(new String[0]); 259 } 260 261 String jdkList = System.getProperty("jdkList", TEST_JDK); 262 System.out.println("JDK List:\n" + jdkList); 263 String[] jdkPaths = jdkList.split(","); 264 return jdkPaths; 265 } 266 267 private static void signJarByJDKs(String[] jdkPaths) throws Throwable { 268 for (String signerJdkPath : jdkPaths) { 269 JdkInfo jdkInfo = new JdkInfo(signerJdkPath); 270 JDK_INFOS.add(jdkInfo); 271 272 for (String sigAlg : SIGNATURE_ALGORITHMS) { 273 for (String tsaDigest : TSA_DIGEST_ALGORITHMS) { 274 // If the JDK doesn't support option -tsadigestalg, 275 // the associated cases just be ignored. 276 if (tsaDigest != DEFAULT && !jdkInfo.supportsTsadigestalg) { 277 continue; 278 } 279 280 for (String certType : CERT_TYPES) { 281 String alias = CERTS.get(new CertType( 282 sigAlg.contains(RSA) ? RSA : DSA, certType)); 283 String signedJarPath = jdkInfo.version + "_" + sigAlg 284 + "_" + tsaDigest + "_" + certType + ".jar"; 285 String tsadigestalg = default2Null(tsaDigest); 286 287 String signOutput = signJar(jdkInfo.jarsignerPath, 288 sigAlg, tsadigestalg, alias, signedJarPath); 289 STATUS status = signStatus(signOutput); 290 SignItem signItem = null; 291 if (status != STATUS.ERROR) { 292 signItem = SignItem.build() 293 .version(jdkInfo.version) 294 .signatureAlgorithm(sigAlg) 295 .tsaDigestAlgorithm(tsadigestalg) 296 .certType(certType).status(status) 297 .signedJarPath(signedJarPath); 298 SIGN_ITEMS.add(signItem); 299 300 // Using the testing JDK, which is specified by 301 // jtreg option "-jdk", to verify the signed jar 302 // and extract the timestamp digest algorithm. 303 String verifyOutput = verifyJar(TEST_JARSIGNER, 304 signedJarPath); 305 signItem.extractedTsaDigestAlgorithm( 306 extract(verifyOutput, 307 " *Timestamp digest algorithm.*", 308 ".*(: )| \\(.*")); 309 } else { 310 jdkInfo.addUnsupportedSigAlg(sigAlg); 311 } 312 } 313 } 314 } 315 } 316 } 317 318 private static List<ReportItem> verifyJarByJDKs(String[] jdkPaths) 319 throws Throwable { 320 List<ReportItem> reportItems = new ArrayList<ReportItem>(); 321 for (String verifierJdkPath : jdkPaths) { 322 JdkInfo verifierJdkInfo = JDK_INFOS.get( 323 JDK_INFOS.indexOf(new JdkInfo(verifierJdkPath))); 324 325 for (String sigalg : SIGNATURE_ALGORITHMS) { 326 for (String tsaDigest : TSA_DIGEST_ALGORITHMS) { 327 // If property strict is false and the JDK doesn't support 328 // the signature algorithm, then the case just be ignored. 329 if (!STRICT 330 && verifierJdkInfo.unsupportedSigAlgs.contains(sigalg)) { 331 continue; 332 } 333 334 for (String certType : CERT_TYPES) { 335 for (String signerJdkPath : jdkPaths) { 336 SignItem signItem = findSignItem(SIGN_ITEMS, 337 signerJdkPath, sigalg, tsaDigest, certType); 338 // If signItem is null, it means the signing process 339 // failed, so there is no associated signed jar. Then 340 // it cannot do verifying. 341 if (signItem != null) { 342 String verifyOutput = verifyJar( 343 verifierJdkInfo.jarsignerPath, 344 signItem.signedJarPath); 345 STATUS verifyStatus = verifyStatus( 346 verifyOutput); 347 if (verifyStatus != STATUS.ERROR) { 348 verifyStatus = (STRICT 349 && signItem.tsaDigestAlgorithm == DEFAULT 350 && signItem.extractedTsaDigestAlgorithm != null 351 && !signItem.extractedTsaDigestAlgorithm 352 .matches("SHA-?256")) 353 ? STATUS.ERROR 354 : verifyStatus; 355 } 356 VerifyItem verifyItem = VerifyItem.build() 357 .version(verifierJdkInfo.version) 358 .status(verifyStatus); 359 ReportItem reportItem = new ReportItem(signItem, 360 verifyItem); 361 reportItems.add(reportItem); 362 System.out.println("Result:\n" + reportItem); 363 } 364 } 365 } 366 } 367 } 368 } 369 370 return reportItems; 371 } 372 373 // Finds a SignItem by the specified JDK, algorithms and certificate. 374 private static SignItem findSignItem(List<SignItem> signItems, 375 String signerJDKPath, String sigalg, String tsaDigest, 376 String certType) throws Throwable { 377 SignItem dummySignItem = SignItem.build() 378 .version(javaVersion(signerJDKPath)) 379 .signatureAlgorithm(sigalg) 380 .tsaDigestAlgorithm(default2Null(tsaDigest)) 381 .certType(certType); 382 int index = signItems.indexOf(dummySignItem); 383 return index != -1 ? signItems.get(index) : null; 384 } 385 386 private static String default2Null(String tsaDigest) { 387 return !DEFAULT.equals(tsaDigest) ? tsaDigest : null; 388 } 389 390 // Determines the status of signing. 391 private static STATUS signStatus(String output) { 392 if (output.contains(Test.JAR_SIGNED)) { 393 if (output.contains(Test.HAS_EXPIRED_CERT_SIGNING_WARNING)) { 394 return STATUS.WARNING; 395 } else { 396 return STATUS.NORMAL; 397 } 398 } else { 399 return STATUS.ERROR; 400 } 401 } 402 403 // Determines the status of verifying. 404 private static STATUS verifyStatus(String output) { 405 if (output.contains(Test.JAR_VERIFIED)) { 406 if (output.contains(Test.WARNING)) { 407 return STATUS.WARNING; 408 } else { 409 return STATUS.NORMAL; 410 } 411 } else { 412 return STATUS.ERROR; 413 } 414 } 415 416 // Extracts string from text by specified patterns. 417 private static String extract(String text, String linePattern, 418 String replacePattern) { 419 Matcher lineMatcher = Pattern.compile(linePattern).matcher(text); 420 if (lineMatcher.find()) { 421 String line = lineMatcher.group(0); 422 return line.replaceAll(replacePattern, ""); 423 } else { 424 return null; 425 } 426 } 427 428 // Extracts build version from java version info. 429 private static String javaVersion(String jdkPath) throws Throwable { 430 OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand( 431 jdkPath + "/bin/java", 432 "-version"); 433 String version = extract(outputAnalyzer.getOutput(), 434 "Java\\(TM\\) SE Runtime Environment.*", 435 "(.*\\(.* )|\\)"); 436 return version != null ? version : "N/A"; 437 } 438 439 // Checks if the jarsigner supports option -tsadigestalg. 440 private static boolean supportsTsadigestalg(String jarsignerPath) 441 throws Throwable { 442 OutputAnalyzer outputAnalyzer = exec(jarsignerPath, "-help"); 443 return outputAnalyzer.getOutput().contains("-tsadigestalg"); 444 } 445 446 // Using specified jarsigner to sign the pre-created jar with specified 447 // algorithms. 448 private static String signJar(String jarsignerPath, String sigalg, 449 String tsadigestalg, String alias, String signedJarPath) 450 throws Throwable { 451 List<String> arguments = new ArrayList<String>(); 452 if (PROXY_HOST != null && PROXY_PORT != null) { 453 arguments.add("-J-Dhttp.proxyHost=" + PROXY_HOST); 454 arguments.add("-J-Dhttp.proxyPort=" + PROXY_PORT); 455 arguments.add("-J-Dhttps.proxyHost=" + PROXY_HOST); 456 arguments.add("-J-Dhttps.proxyPort=" + PROXY_PORT); 457 } 458 arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY); 459 arguments.add("-verbose"); 460 if (sigalg != null) { 461 arguments.add("-sigalg"); 462 arguments.add(sigalg); 463 } 464 arguments.add("-tsa"); 465 arguments.add(TSA); 466 if (tsadigestalg != null) { 467 arguments.add("-tsadigestalg"); 468 arguments.add(tsadigestalg); 469 } 470 arguments.add("-keystore"); 471 arguments.add(KEYSTORE); 472 arguments.add("-storepass"); 473 arguments.add(PASSWORD); 474 arguments.add("-signedjar"); 475 arguments.add(signedJarPath); 476 arguments.add(TEST_JAR_NAME); 477 arguments.add(alias); 478 479 OutputAnalyzer outputAnalyzer = exec( 480 jarsignerPath, 481 arguments.toArray(new String[arguments.size()])); 482 return outputAnalyzer.getOutput(); 483 } 484 485 // Using specified jarsigner to verify the signed jar. 486 private static String verifyJar(String jarsignerPath, String signedJarPath) 487 throws Throwable { 488 OutputAnalyzer outputAnalyzer = exec( 489 jarsignerPath, 490 "-J-Djava.security.properties=" + JAVA_SECURITY, 491 "-verbose", "-certs", 492 "-keystore", KEYSTORE, 493 "-verify", signedJarPath); 494 return outputAnalyzer.getOutput(); 495 } 496 497 // Generates the test result report. 498 private static boolean generateReport(List<ReportItem> reportItems) 499 throws IOException { 500 System.out.println("Report is being generated..."); 501 502 StringBuilder report = new StringBuilder(); 503 504 // Generates report headers. 505 report.append(ReportHeader.HEADERS).append("\n"); 506 507 boolean failed = false; 508 509 // Generates report rows. 510 for (ReportItem item : reportItems) { 511 failed = failed || item.verifyItem.status == STATUS.ERROR; 512 report.append(item).append("\n"); 513 } 514 515 FileWriter writer = new FileWriter(new File("testReport")); 516 writer.write(report.toString()); 517 writer.close(); 518 519 System.out.println("Report is generated."); 520 return failed; 521 } 522 523 private static String jarsignerPath(String jdkPath) { 524 return jdkPath + "/bin/jarsigner"; 525 } 526 527 // Executes command against the specified JDK tool, and ensures the output 528 // is US English. 529 private static OutputAnalyzer exec(String toolPath, String... args) 530 throws Throwable { 531 String[] cmd = new String[args.length + 3]; 532 cmd[0] = toolPath; 533 cmd[1] = "-J-Duser.language=en"; 534 cmd[2] = "-J-Duser.country=US"; 535 System.arraycopy(args, 0, cmd, 3, args.length); 536 return ProcessTools.executeCommand(cmd); 537 } 538 539 private static class JdkInfo { 540 541 private final String jdkPath; 542 private final String jarsignerPath; 543 private final String version; 544 private final boolean supportsTsadigestalg; 545 private Set<String> unsupportedSigAlgs = new HashSet<String>(); 546 547 private JdkInfo(String jdkPath) throws Throwable { 548 this.jdkPath = jdkPath; 549 version = javaVersion(jdkPath); 550 jarsignerPath = jarsignerPath(jdkPath); 551 supportsTsadigestalg = supportsTsadigestalg(jarsignerPath); 552 } 553 554 private void addUnsupportedSigAlg(String sigalg) { 555 unsupportedSigAlgs.add(sigalg); 556 } 557 558 @Override 559 public int hashCode() { 560 final int prime = 31; 561 int result = 1; 562 result = prime * result 563 + ((jdkPath == null) ? 0 : jdkPath.hashCode()); 564 return result; 565 } 566 567 @Override 568 public boolean equals(Object obj) { 569 if (this == obj) 570 return true; 571 if (obj == null) 572 return false; 573 if (getClass() != obj.getClass()) 574 return false; 575 JdkInfo other = (JdkInfo) obj; 576 if (jdkPath == null) { 577 if (other.jdkPath != null) 578 return false; 579 } else if (!jdkPath.equals(other.jdkPath)) 580 return false; 581 return true; 582 } 583 } 584 585 private static class CertType { 586 587 private final String keyAlgorithm; 588 private final String certType; 589 590 private CertType(String keyAlgorithm, String certType) { 591 this.keyAlgorithm = keyAlgorithm; 592 this.certType = certType; 593 } 594 595 @Override 596 public int hashCode() { 597 final int prime = 31; 598 int result = 1; 599 result = prime * result 600 + ((certType == null) ? 0 : certType.hashCode()); 601 result = prime * result 602 + ((keyAlgorithm == null) ? 0 : keyAlgorithm.hashCode()); 603 return result; 604 } 605 606 @Override 607 public boolean equals(Object obj) { 608 if (this == obj) 609 return true; 610 if (obj == null) 611 return false; 612 if (getClass() != obj.getClass()) 613 return false; 614 CertType other = (CertType) obj; 615 if (certType == null) { 616 if (other.certType != null) 617 return false; 618 } else if (!certType.equals(other.certType)) 619 return false; 620 if (keyAlgorithm == null) { 621 if (other.keyAlgorithm != null) 622 return false; 623 } else if (!keyAlgorithm.equals(other.keyAlgorithm)) 624 return false; 625 return true; 626 } 627 } 628 629 private static enum STATUS { 630 631 // Signing/Verifying with error 632 ERROR, 633 634 // jar is signed/verified with warning 635 WARNING, 636 637 // jar is signed/verified without any warning and error 638 NORMAL 639 } 640 641 private static class SignItem { 642 643 private String version; 644 private String signatureAlgorithm; 645 private String tsaDigestAlgorithm; 646 // tsadigestalg that is extracted from verification output (if possible) 647 private String extractedTsaDigestAlgorithm; 648 private String certType; 649 private STATUS status; 650 private String signedJarPath; 651 652 private static SignItem build() { 653 return new SignItem(); 654 } 655 656 private SignItem version(String version) { 657 this.version = version; 658 return this; 659 } 660 661 private SignItem signatureAlgorithm(String signatureAlgorithm) { 662 this.signatureAlgorithm = signatureAlgorithm; 663 return this; 664 } 665 666 private SignItem tsaDigestAlgorithm(String tsaDigestAlgorithm) { 667 this.tsaDigestAlgorithm = tsaDigestAlgorithm; 668 return this; 669 } 670 671 private SignItem extractedTsaDigestAlgorithm( 672 String extractedTsaDigestAlgorithm) { 673 this.extractedTsaDigestAlgorithm = extractedTsaDigestAlgorithm; 674 return this; 675 } 676 677 private SignItem certType(String certType) { 678 this.certType = certType; 679 return this; 680 } 681 682 private SignItem status(STATUS status) { 683 this.status = status; 684 return this; 685 } 686 687 private SignItem signedJarPath(String signedJarPath) { 688 this.signedJarPath = signedJarPath; 689 return this; 690 } 691 692 @Override 693 public int hashCode() { 694 final int prime = 31; 695 int result = 1; 696 result = prime * result 697 + ((certType == null) ? 0 : certType.hashCode()); 698 result = prime * result + ((signatureAlgorithm == null) ? 0 699 : signatureAlgorithm.hashCode()); 700 result = prime * result + ((tsaDigestAlgorithm == null) ? 0 701 : tsaDigestAlgorithm.hashCode()); 702 result = prime * result 703 + ((version == null) ? 0 : version.hashCode()); 704 return result; 705 } 706 707 @Override 708 public boolean equals(Object obj) { 709 if (this == obj) 710 return true; 711 if (obj == null) 712 return false; 713 if (getClass() != obj.getClass()) 714 return false; 715 SignItem other = (SignItem) obj; 716 if (certType == null) { 717 if (other.certType != null) 718 return false; 719 } else if (!certType.equals(other.certType)) 720 return false; 721 if (signatureAlgorithm == null) { 722 if (other.signatureAlgorithm != null) 723 return false; 724 } else if (!signatureAlgorithm.equals(other.signatureAlgorithm)) 725 return false; 726 if (tsaDigestAlgorithm == null) { 727 if (other.tsaDigestAlgorithm != null) 728 return false; 729 } else if (!tsaDigestAlgorithm.equals(other.tsaDigestAlgorithm)) 730 return false; 731 if (version == null) { 732 if (other.version != null) 733 return false; 734 } else if (!version.equals(other.version)) 735 return false; 736 return true; 737 } 738 } 739 740 private static class VerifyItem { 741 742 private String version; 743 private STATUS status; 744 745 private static VerifyItem build() { 746 return new VerifyItem(); 747 } 748 749 private VerifyItem version(String version) { 750 this.version = version; 751 return this; 752 } 753 754 private VerifyItem status(STATUS status) { 755 this.status = status; 756 return this; 757 } 758 } 759 760 private static class ReportHeader { 761 762 // Header names 763 private static final String SIGNER_VERSION = "[Signer JDK]"; 764 private static final String SIGNATURE_ALGORITHM = "[Signature Algorithm]"; 765 private static final String TSA_DIGEST_ALGORITHM = "[TSA Digest Algorithm]"; 766 private static final String CERT_TYPE = "[Cert Type]"; 767 private static final String SIGNE_STATUS = "[Status of Signing]"; 768 private static final String VERIFIER_VERSION = "[Verifier JDK]"; 769 private static final String VERIFY_STATUS = "[Status of Verifying]"; 770 private static final String FAILED = "[Failed]"; 771 772 // Header widths 773 private static final int W_SIGNER_VERSION = 16; 774 private static final int W_SIGNATURE_ALGORITHM = 23; 775 private static final int W_TSA_DIGEST_ALGORITHM 776 = TSA_DIGEST_ALGORITHM.length(); 777 private static final int W_CERT_TYPE = CERT_TYPE.length(); 778 private static final int W_SIGNED = SIGNE_STATUS.length(); 779 private static final int W_VERIFIER_VERSION = W_SIGNER_VERSION; 780 private static final int W_VERIFY_STATUS = VERIFY_STATUS.length(); 781 private static final int W_FAILED = FAILED.length(); 782 783 private static final String DELIMITER = " "; 784 private static final String FORMAT 785 = "%-" + W_SIGNER_VERSION + "s" + DELIMITER 786 + "%-" + W_SIGNATURE_ALGORITHM + "s" + DELIMITER 787 + "%-" + W_TSA_DIGEST_ALGORITHM + "s" + DELIMITER 788 + "%-" + W_CERT_TYPE + "s" + DELIMITER 789 + "%-" + W_SIGNED + "s" + DELIMITER 790 + "%-" + W_VERIFIER_VERSION + "s" + DELIMITER 791 + "%-" + W_VERIFY_STATUS + "s" + DELIMITER 792 + "%-" + W_FAILED + "s"; 793 794 private static final String HEADERS = String.format( 795 ReportHeader.FORMAT, 796 SIGNER_VERSION, 797 SIGNATURE_ALGORITHM, 798 TSA_DIGEST_ALGORITHM, 799 CERT_TYPE, 800 SIGNE_STATUS, 801 VERIFIER_VERSION, 802 VERIFY_STATUS, 803 FAILED); 804 805 } 806 807 private static class ReportItem { 808 809 private final SignItem signItem; 810 private final VerifyItem verifyItem; 811 812 private ReportItem(SignItem signItem, VerifyItem verifyItem) { 813 this.signItem = signItem; 814 this.verifyItem = verifyItem; 815 } 816 817 @Override 818 public String toString() { 819 return String.format(ReportHeader.FORMAT, 820 signItem.version, 821 signItem.signatureAlgorithm, 822 null2Default(signItem.tsaDigestAlgorithm, 823 signItem.extractedTsaDigestAlgorithm), 824 signItem.certType, 825 signItem.status, 826 verifyItem.version, 827 verifyItem.status, 828 verifyItem.status == STATUS.ERROR ? "X" : ""); 829 } 830 831 // If a value is null, then displays the default value or N/A. 832 private static String null2Default(String value, String defaultValue) { 833 return value == null 834 ? DEFAULT + "(" + (defaultValue == null 835 ? "N/A" 836 : defaultValue) + ")" 837 : value; 838 } 839 } 840 }