1 /* 2 * Copyright (c) 1997, 2015, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.tools.jarsigner; 27 28 import java.io.*; 29 import java.util.*; 30 import java.util.zip.*; 31 import java.util.jar.*; 32 import java.net.URI; 33 import java.text.Collator; 34 import java.text.MessageFormat; 35 import java.security.cert.Certificate; 36 import java.security.cert.X509Certificate; 37 import java.security.cert.CertificateException; 38 import java.security.*; 39 40 import java.net.SocketTimeoutException; 41 import java.net.URL; 42 import java.security.cert.CertPath; 43 import java.security.cert.CertPathValidator; 44 import java.security.cert.CertificateExpiredException; 45 import java.security.cert.CertificateFactory; 46 import java.security.cert.CertificateNotYetValidException; 47 import java.security.cert.PKIXParameters; 48 import java.security.cert.TrustAnchor; 49 import java.util.Map.Entry; 50 51 import jdk.security.jarsigner.JarSigner; 52 import jdk.security.jarsigner.JarSignerException; 53 import sun.security.tools.KeyStoreUtil; 54 import sun.security.x509.*; 55 import sun.security.util.*; 56 57 58 /** 59 * <p>The jarsigner utility. 60 * 61 * The exit codes for the main method are: 62 * 63 * 0: success 64 * 1: any error that the jar cannot be signed or verified, including: 65 * keystore loading error 66 * TSP communication error 67 * jarsigner command line error... 68 * otherwise: error codes from -strict 69 * 70 * @author Roland Schemers 71 * @author Jan Luehe 72 */ 73 public class Main { 74 75 // for i18n 76 private static final java.util.ResourceBundle rb = 77 java.util.ResourceBundle.getBundle 78 ("sun.security.tools.jarsigner.Resources"); 79 private static final Collator collator = Collator.getInstance(); 80 static { 81 // this is for case insensitive string comparisions 82 collator.setStrength(Collator.PRIMARY); 83 } 84 85 private static final String NONE = "NONE"; 86 private static final String P11KEYSTORE = "PKCS11"; 87 88 private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds 89 90 // Attention: 91 // This is the entry that get launched by the security tool jarsigner. 92 public static void main(String args[]) throws Exception { 93 Main js = new Main(); 94 js.run(args); 95 } 96 97 static final String VERSION = "1.0"; 98 99 static final int IN_KEYSTORE = 0x01; // signer is in keystore 100 static final int NOT_ALIAS = 0x04; // alias list is NOT empty and 101 // signer is not in alias list 102 static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list 103 104 X509Certificate[] certChain; // signer's cert chain (when composing) 105 PrivateKey privateKey; // private key 106 KeyStore store; // the keystore specified by -keystore 107 // or the default keystore, never null 108 109 String keystore; // key store file 110 boolean nullStream = false; // null keystore input stream (NONE) 111 boolean token = false; // token-based keystore 112 String jarfile; // jar files to sign or verify 113 String alias; // alias to sign jar with 114 List<String> ckaliases = new ArrayList<>(); // aliases in -verify 115 char[] storepass; // keystore password 116 boolean protectedPath; // protected authentication path 117 String storetype; // keystore type 118 String providerName; // provider name 119 List<String> providers = null; // list of provider names 120 List<String> providerClasses = null; // list of provider classes 121 // arguments for provider constructors 122 HashMap<String,String> providerArgs = new HashMap<>(); 123 char[] keypass; // private key password 124 String sigfile; // name of .SF file 125 String sigalg; // name of signature algorithm 126 String digestalg; // name of digest algorithm 127 String signedjar; // output filename 128 String tsaUrl; // location of the Timestamping Authority 129 String tsaAlias; // alias for the Timestamping Authority's certificate 130 String altCertChain; // file to read alternative cert chain from 131 String tSAPolicyID; 132 String tSADigestAlg; 133 boolean verify = false; // verify the jar 134 String verbose = null; // verbose output when signing/verifying 135 boolean showcerts = false; // show certs when verifying 136 boolean debug = false; // debug 137 boolean signManifest = true; // "sign" the whole manifest 138 boolean externalSF = true; // leave the .SF out of the PKCS7 block 139 boolean strict = false; // treat warnings as error 140 141 // read zip entry raw bytes 142 private String altSignerClass = null; 143 private String altSignerClasspath = null; 144 private ZipFile zipFile = null; 145 146 // Informational warnings 147 private boolean hasExpiringCert = false; 148 private boolean noTimestamp = false; 149 private Date expireDate = new Date(0L); // used in noTimestamp warning 150 151 // Severe warnings 152 private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg 153 private boolean hasExpiredCert = false; 154 private boolean notYetValidCert = false; 155 private boolean chainNotValidated = false; 156 private boolean notSignedByAlias = false; 157 private boolean aliasNotInStore = false; 158 private boolean hasUnsignedEntry = false; 159 private boolean badKeyUsage = false; 160 private boolean badExtendedKeyUsage = false; 161 private boolean badNetscapeCertType = false; 162 private boolean signerSelfSigned = false; 163 164 private Throwable chainNotValidatedReason = null; 165 166 CertificateFactory certificateFactory; 167 CertPathValidator validator; 168 PKIXParameters pkixParameters; 169 170 public void run(String args[]) { 171 try { 172 args = parseArgs(args); 173 174 // Try to load and install the specified providers 175 if (providers != null) { 176 for (String provName: providers) { 177 try { 178 KeyStoreUtil.loadProviderByName(provName, 179 providerArgs.get(provName)); 180 if (debug) { 181 System.out.println("loadProviderByName: " + provName); 182 } 183 } catch (IllegalArgumentException e) { 184 throw new Exception(String.format(rb.getString( 185 "provider.name.not.found"), provName)); 186 } 187 } 188 } 189 190 if (providerClasses != null) { 191 ClassLoader cl = ClassLoader.getSystemClassLoader(); 192 for (String provClass: providerClasses) { 193 try { 194 KeyStoreUtil.loadProviderByClass(provClass, 195 providerArgs.get(provClass), cl); 196 if (debug) { 197 System.out.println("loadProviderByClass: " + provClass); 198 } 199 } catch (ClassCastException cce) { 200 throw new Exception(String.format(rb.getString( 201 "provclass.not.a.provider"), provClass)); 202 } catch (IllegalArgumentException e) { 203 throw new Exception(String.format(rb.getString( 204 "provider.class.not.found"), provClass), e.getCause()); 205 } 206 } 207 } 208 209 if (verify) { 210 try { 211 loadKeyStore(keystore, false); 212 } catch (Exception e) { 213 if ((keystore != null) || (storepass != null)) { 214 System.out.println(rb.getString("jarsigner.error.") + 215 e.getMessage()); 216 if (debug) { 217 e.printStackTrace(); 218 } 219 System.exit(1); 220 } 221 } 222 /* if (debug) { 223 SignatureFileVerifier.setDebug(true); 224 ManifestEntryVerifier.setDebug(true); 225 } 226 */ 227 verifyJar(jarfile); 228 } else { 229 loadKeyStore(keystore, true); 230 getAliasInfo(alias); 231 232 signJar(jarfile, alias); 233 } 234 } catch (Exception e) { 235 System.out.println(rb.getString("jarsigner.error.") + e); 236 if (debug) { 237 e.printStackTrace(); 238 } 239 System.exit(1); 240 } finally { 241 // zero-out private key password 242 if (keypass != null) { 243 Arrays.fill(keypass, ' '); 244 keypass = null; 245 } 246 // zero-out keystore password 247 if (storepass != null) { 248 Arrays.fill(storepass, ' '); 249 storepass = null; 250 } 251 } 252 253 if (strict) { 254 int exitCode = 0; 255 if (weakAlg != 0 || chainNotValidated || hasExpiredCert || notYetValidCert || signerSelfSigned) { 256 exitCode |= 4; 257 } 258 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) { 259 exitCode |= 8; 260 } 261 if (hasUnsignedEntry) { 262 exitCode |= 16; 263 } 264 if (notSignedByAlias || aliasNotInStore) { 265 exitCode |= 32; 266 } 267 if (exitCode != 0) { 268 System.exit(exitCode); 269 } 270 } 271 } 272 273 /* 274 * Parse command line arguments. 275 */ 276 String[] parseArgs(String args[]) throws Exception { 277 /* parse flags */ 278 int n = 0; 279 280 if (args.length == 0) fullusage(); 281 282 String confFile = null; 283 String command = "-sign"; 284 for (n=0; n < args.length; n++) { 285 if (collator.compare(args[n], "-verify") == 0) { 286 command = "-verify"; 287 } else if (collator.compare(args[n], "-conf") == 0) { 288 if (n == args.length - 1) { 289 usageNoArg(); 290 } 291 confFile = args[++n]; 292 } 293 } 294 295 if (confFile != null) { 296 args = KeyStoreUtil.expandArgs( 297 "jarsigner", confFile, command, null, args); 298 } 299 300 debug = Arrays.stream(args).anyMatch( 301 x -> collator.compare(x, "-debug") == 0); 302 303 if (debug) { 304 // No need to localize debug output 305 System.out.println("Command line args: " + 306 Arrays.toString(args)); 307 } 308 309 for (n=0; n < args.length; n++) { 310 311 String flags = args[n]; 312 String modifier = null; 313 314 if (flags.startsWith("-")) { 315 int pos = flags.indexOf(':'); 316 if (pos > 0) { 317 modifier = flags.substring(pos+1); 318 flags = flags.substring(0, pos); 319 } 320 } 321 322 if (!flags.startsWith("-")) { 323 if (jarfile == null) { 324 jarfile = flags; 325 } else { 326 alias = flags; 327 ckaliases.add(alias); 328 } 329 } else if (collator.compare(flags, "-conf") == 0) { 330 if (++n == args.length) usageNoArg(); 331 } else if (collator.compare(flags, "-keystore") == 0) { 332 if (++n == args.length) usageNoArg(); 333 keystore = args[n]; 334 } else if (collator.compare(flags, "-storepass") ==0) { 335 if (++n == args.length) usageNoArg(); 336 storepass = getPass(modifier, args[n]); 337 } else if (collator.compare(flags, "-storetype") ==0) { 338 if (++n == args.length) usageNoArg(); 339 storetype = args[n]; 340 } else if (collator.compare(flags, "-providerName") ==0) { 341 if (++n == args.length) usageNoArg(); 342 providerName = args[n]; 343 } else if (collator.compare(flags, "-provider") == 0 || 344 collator.compare(flags, "-providerClass") == 0) { 345 if (++n == args.length) usageNoArg(); 346 if (providerClasses == null) { 347 providerClasses = new ArrayList<>(3); 348 } 349 providerClasses.add(args[n]); 350 351 if (args.length > (n+1)) { 352 flags = args[n+1]; 353 if (collator.compare(flags, "-providerArg") == 0) { 354 if (args.length == (n+2)) usageNoArg(); 355 providerArgs.put(args[n], args[n+2]); 356 n += 2; 357 } 358 } 359 } else if (collator.compare(flags, "-addprovider") == 0) { 360 if (++n == args.length) usageNoArg(); 361 if (providers == null) { 362 providers = new ArrayList<>(3); 363 } 364 providers.add(args[n]); 365 366 if (args.length > (n+1)) { 367 flags = args[n+1]; 368 if (collator.compare(flags, "-providerArg") == 0) { 369 if (args.length == (n+2)) usageNoArg(); 370 providerArgs.put(args[n], args[n+2]); 371 n += 2; 372 } 373 } 374 } else if (collator.compare(flags, "-protected") ==0) { 375 protectedPath = true; 376 } else if (collator.compare(flags, "-certchain") ==0) { 377 if (++n == args.length) usageNoArg(); 378 altCertChain = args[n]; 379 } else if (collator.compare(flags, "-tsapolicyid") ==0) { 380 if (++n == args.length) usageNoArg(); 381 tSAPolicyID = args[n]; 382 } else if (collator.compare(flags, "-tsadigestalg") ==0) { 383 if (++n == args.length) usageNoArg(); 384 tSADigestAlg = args[n]; 385 } else if (collator.compare(flags, "-debug") ==0) { 386 // Already processed 387 } else if (collator.compare(flags, "-keypass") ==0) { 388 if (++n == args.length) usageNoArg(); 389 keypass = getPass(modifier, args[n]); 390 } else if (collator.compare(flags, "-sigfile") ==0) { 391 if (++n == args.length) usageNoArg(); 392 sigfile = args[n]; 393 } else if (collator.compare(flags, "-signedjar") ==0) { 394 if (++n == args.length) usageNoArg(); 395 signedjar = args[n]; 396 } else if (collator.compare(flags, "-tsa") ==0) { 397 if (++n == args.length) usageNoArg(); 398 tsaUrl = args[n]; 399 } else if (collator.compare(flags, "-tsacert") ==0) { 400 if (++n == args.length) usageNoArg(); 401 tsaAlias = args[n]; 402 } else if (collator.compare(flags, "-altsigner") ==0) { 403 if (++n == args.length) usageNoArg(); 404 altSignerClass = args[n]; 405 System.err.println( 406 rb.getString("This.option.is.deprecated") + 407 "-altsigner"); 408 } else if (collator.compare(flags, "-altsignerpath") ==0) { 409 if (++n == args.length) usageNoArg(); 410 altSignerClasspath = args[n]; 411 System.err.println( 412 rb.getString("This.option.is.deprecated") + 413 "-altsignerpath"); 414 } else if (collator.compare(flags, "-sectionsonly") ==0) { 415 signManifest = false; 416 } else if (collator.compare(flags, "-internalsf") ==0) { 417 externalSF = false; 418 } else if (collator.compare(flags, "-verify") ==0) { 419 verify = true; 420 } else if (collator.compare(flags, "-verbose") ==0) { 421 verbose = (modifier != null) ? modifier : "all"; 422 } else if (collator.compare(flags, "-sigalg") ==0) { 423 if (++n == args.length) usageNoArg(); 424 sigalg = args[n]; 425 } else if (collator.compare(flags, "-digestalg") ==0) { 426 if (++n == args.length) usageNoArg(); 427 digestalg = args[n]; 428 } else if (collator.compare(flags, "-certs") ==0) { 429 showcerts = true; 430 } else if (collator.compare(flags, "-strict") ==0) { 431 strict = true; 432 } else if (collator.compare(flags, "-h") == 0 || 433 collator.compare(flags, "-help") == 0) { 434 fullusage(); 435 } else { 436 System.err.println( 437 rb.getString("Illegal.option.") + flags); 438 usage(); 439 } 440 } 441 442 // -certs must always be specified with -verbose 443 if (verbose == null) showcerts = false; 444 445 if (jarfile == null) { 446 System.err.println(rb.getString("Please.specify.jarfile.name")); 447 usage(); 448 } 449 if (!verify && alias == null) { 450 System.err.println(rb.getString("Please.specify.alias.name")); 451 usage(); 452 } 453 if (!verify && ckaliases.size() > 1) { 454 System.err.println(rb.getString("Only.one.alias.can.be.specified")); 455 usage(); 456 } 457 458 if (storetype == null) { 459 storetype = KeyStore.getDefaultType(); 460 } 461 storetype = KeyStoreUtil.niceStoreTypeName(storetype); 462 463 try { 464 if (signedjar != null && new File(signedjar).getCanonicalPath().equals( 465 new File(jarfile).getCanonicalPath())) { 466 signedjar = null; 467 } 468 } catch (IOException ioe) { 469 // File system error? 470 // Just ignore it. 471 } 472 473 if (P11KEYSTORE.equalsIgnoreCase(storetype) || 474 KeyStoreUtil.isWindowsKeyStore(storetype)) { 475 token = true; 476 if (keystore == null) { 477 keystore = NONE; 478 } 479 } 480 481 if (NONE.equals(keystore)) { 482 nullStream = true; 483 } 484 485 if (token && !nullStream) { 486 System.err.println(MessageFormat.format(rb.getString 487 (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype)); 488 usage(); 489 } 490 491 if (token && keypass != null) { 492 System.err.println(MessageFormat.format(rb.getString 493 (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype)); 494 usage(); 495 } 496 497 if (protectedPath) { 498 if (storepass != null || keypass != null) { 499 System.err.println(rb.getString 500 ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified")); 501 usage(); 502 } 503 } 504 if (KeyStoreUtil.isWindowsKeyStore(storetype)) { 505 if (storepass != null || keypass != null) { 506 System.err.println(rb.getString 507 ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified")); 508 usage(); 509 } 510 } 511 return args; 512 } 513 514 static char[] getPass(String modifier, String arg) { 515 char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb); 516 if (output != null) return output; 517 usage(); 518 return null; // Useless, usage() already exit 519 } 520 521 static void usageNoArg() { 522 System.out.println(rb.getString("Option.lacks.argument")); 523 usage(); 524 } 525 526 static void usage() { 527 System.out.println(); 528 System.out.println(rb.getString("Please.type.jarsigner.help.for.usage")); 529 System.exit(1); 530 } 531 532 static void fullusage() { 533 System.out.println(rb.getString 534 ("Usage.jarsigner.options.jar.file.alias")); 535 System.out.println(rb.getString 536 (".jarsigner.verify.options.jar.file.alias.")); 537 System.out.println(); 538 System.out.println(rb.getString 539 (".keystore.url.keystore.location")); 540 System.out.println(); 541 System.out.println(rb.getString 542 (".storepass.password.password.for.keystore.integrity")); 543 System.out.println(); 544 System.out.println(rb.getString 545 (".storetype.type.keystore.type")); 546 System.out.println(); 547 System.out.println(rb.getString 548 (".keypass.password.password.for.private.key.if.different.")); 549 System.out.println(); 550 System.out.println(rb.getString 551 (".certchain.file.name.of.alternative.certchain.file")); 552 System.out.println(); 553 System.out.println(rb.getString 554 (".sigfile.file.name.of.SF.DSA.file")); 555 System.out.println(); 556 System.out.println(rb.getString 557 (".signedjar.file.name.of.signed.JAR.file")); 558 System.out.println(); 559 System.out.println(rb.getString 560 (".digestalg.algorithm.name.of.digest.algorithm")); 561 System.out.println(); 562 System.out.println(rb.getString 563 (".sigalg.algorithm.name.of.signature.algorithm")); 564 System.out.println(); 565 System.out.println(rb.getString 566 (".verify.verify.a.signed.JAR.file")); 567 System.out.println(); 568 System.out.println(rb.getString 569 (".verbose.suboptions.verbose.output.when.signing.verifying.")); 570 System.out.println(rb.getString 571 (".suboptions.can.be.all.grouped.or.summary")); 572 System.out.println(); 573 System.out.println(rb.getString 574 (".certs.display.certificates.when.verbose.and.verifying")); 575 System.out.println(); 576 System.out.println(rb.getString 577 (".tsa.url.location.of.the.Timestamping.Authority")); 578 System.out.println(); 579 System.out.println(rb.getString 580 (".tsacert.alias.public.key.certificate.for.Timestamping.Authority")); 581 System.out.println(); 582 System.out.println(rb.getString 583 (".tsapolicyid.tsapolicyid.for.Timestamping.Authority")); 584 System.out.println(); 585 System.out.println(rb.getString 586 (".tsadigestalg.algorithm.of.digest.data.in.timestamping.request")); 587 System.out.println(); 588 System.out.println(rb.getString 589 (".altsigner.class.class.name.of.an.alternative.signing.mechanism")); 590 System.out.println(); 591 System.out.println(rb.getString 592 (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism")); 593 System.out.println(); 594 System.out.println(rb.getString 595 (".internalsf.include.the.SF.file.inside.the.signature.block")); 596 System.out.println(); 597 System.out.println(rb.getString 598 (".sectionsonly.don.t.compute.hash.of.entire.manifest")); 599 System.out.println(); 600 System.out.println(rb.getString 601 (".protected.keystore.has.protected.authentication.path")); 602 System.out.println(); 603 System.out.println(rb.getString 604 (".providerName.name.provider.name")); 605 System.out.println(); 606 System.out.println(rb.getString 607 (".add.provider.option")); 608 System.out.println(rb.getString 609 (".providerArg.option.1")); 610 System.out.println(); 611 System.out.println(rb.getString 612 (".providerClass.option")); 613 System.out.println(rb.getString 614 (".providerArg.option.2")); 615 System.out.println(); 616 System.out.println(rb.getString 617 (".strict.treat.warnings.as.errors")); 618 System.out.println(); 619 System.out.println(rb.getString 620 (".conf.url.specify.a.pre.configured.options.file")); 621 System.out.println(); 622 623 System.exit(0); 624 } 625 626 void verifyJar(String jarName) 627 throws Exception 628 { 629 boolean anySigned = false; // if there exists entry inside jar signed 630 JarFile jf = null; 631 632 try { 633 jf = new JarFile(jarName, true); 634 Vector<JarEntry> entriesVec = new Vector<>(); 635 byte[] buffer = new byte[8192]; 636 637 Enumeration<JarEntry> entries = jf.entries(); 638 while (entries.hasMoreElements()) { 639 JarEntry je = entries.nextElement(); 640 entriesVec.addElement(je); 641 InputStream is = null; 642 try { 643 is = jf.getInputStream(je); 644 while (is.read(buffer, 0, buffer.length) != -1) { 645 // we just read. this will throw a SecurityException 646 // if a signature/digest check fails. 647 } 648 } finally { 649 if (is != null) { 650 is.close(); 651 } 652 } 653 } 654 655 Manifest man = jf.getManifest(); 656 boolean hasSignature = false; 657 658 // The map to record display info, only used when -verbose provided 659 // key: signer info string 660 // value: the list of files with common key 661 Map<String,List<String>> output = new LinkedHashMap<>(); 662 663 if (man != null) { 664 if (verbose != null) System.out.println(); 665 Enumeration<JarEntry> e = entriesVec.elements(); 666 667 String tab = rb.getString("6SPACE"); 668 669 while (e.hasMoreElements()) { 670 JarEntry je = e.nextElement(); 671 String name = je.getName(); 672 673 hasSignature = hasSignature 674 || SignatureFileVerifier.isBlockOrSF(name); 675 676 CodeSigner[] signers = je.getCodeSigners(); 677 boolean isSigned = (signers != null); 678 anySigned |= isSigned; 679 hasUnsignedEntry |= !je.isDirectory() && !isSigned 680 && !signatureRelated(name); 681 682 int inStoreWithAlias = inKeyStore(signers); 683 684 boolean inStore = (inStoreWithAlias & IN_KEYSTORE) != 0; 685 686 notSignedByAlias |= (inStoreWithAlias & NOT_ALIAS) != 0; 687 if (keystore != null) { 688 aliasNotInStore |= isSigned && !inStore; 689 } 690 691 // Only used when -verbose provided 692 StringBuffer sb = null; 693 if (verbose != null) { 694 sb = new StringBuffer(); 695 boolean inManifest = 696 ((man.getAttributes(name) != null) || 697 (man.getAttributes("./"+name) != null) || 698 (man.getAttributes("/"+name) != null)); 699 sb.append(isSigned ? rb.getString("s") : rb.getString("SPACE")) 700 .append(inManifest ? rb.getString("m") : rb.getString("SPACE")) 701 .append(inStore ? rb.getString("k") : rb.getString("SPACE")) 702 .append((inStoreWithAlias & NOT_ALIAS) != 0 ? 'X' : ' ') 703 .append(rb.getString("SPACE")); 704 sb.append('|'); 705 } 706 707 // When -certs provided, display info has extra empty 708 // lines at the beginning and end. 709 if (isSigned) { 710 if (showcerts) sb.append('\n'); 711 for (CodeSigner signer: signers) { 712 // signerInfo() must be called even if -verbose 713 // not provided. The method updates various 714 // warning flags. 715 String si = signerInfo(signer, tab); 716 if (showcerts) { 717 sb.append(si); 718 sb.append('\n'); 719 } 720 } 721 } else if (showcerts && !verbose.equals("all")) { 722 // Print no info for unsigned entries when -verbose:all, 723 // to be consistent with old behavior. 724 if (signatureRelated(name)) { 725 sb.append('\n') 726 .append(tab) 727 .append(rb 728 .getString(".Signature.related.entries.")) 729 .append("\n\n"); 730 } else { 731 sb.append('\n').append(tab) 732 .append(rb.getString(".Unsigned.entries.")) 733 .append("\n\n"); 734 } 735 } 736 737 if (verbose != null) { 738 String label = sb.toString(); 739 if (signatureRelated(name)) { 740 // Entries inside META-INF and other unsigned 741 // entries are grouped separately. 742 label = "-" + label; 743 } 744 745 // The label finally contains 2 parts separated by '|': 746 // The legend displayed before the entry names, and 747 // the cert info (if -certs specified). 748 749 if (!output.containsKey(label)) { 750 output.put(label, new ArrayList<String>()); 751 } 752 753 StringBuilder fb = new StringBuilder(); 754 String s = Long.toString(je.getSize()); 755 for (int i = 6 - s.length(); i > 0; --i) { 756 fb.append(' '); 757 } 758 fb.append(s).append(' '). 759 append(new Date(je.getTime()).toString()); 760 fb.append(' ').append(name); 761 762 output.get(label).add(fb.toString()); 763 } 764 } 765 } 766 if (verbose != null) { 767 for (Entry<String,List<String>> s: output.entrySet()) { 768 List<String> files = s.getValue(); 769 String key = s.getKey(); 770 if (key.charAt(0) == '-') { // the signature-related group 771 key = key.substring(1); 772 } 773 int pipe = key.indexOf('|'); 774 if (verbose.equals("all")) { 775 for (String f: files) { 776 System.out.println(key.substring(0, pipe) + f); 777 System.out.printf(key.substring(pipe+1)); 778 } 779 } else { 780 if (verbose.equals("grouped")) { 781 for (String f: files) { 782 System.out.println(key.substring(0, pipe) + f); 783 } 784 } else if (verbose.equals("summary")) { 785 System.out.print(key.substring(0, pipe)); 786 if (files.size() > 1) { 787 System.out.println(files.get(0) + " " + 788 String.format(rb.getString( 789 ".and.d.more."), files.size()-1)); 790 } else { 791 System.out.println(files.get(0)); 792 } 793 } 794 System.out.printf(key.substring(pipe+1)); 795 } 796 } 797 System.out.println(); 798 System.out.println(rb.getString( 799 ".s.signature.was.verified.")); 800 System.out.println(rb.getString( 801 ".m.entry.is.listed.in.manifest")); 802 System.out.println(rb.getString( 803 ".k.at.least.one.certificate.was.found.in.keystore")); 804 if (ckaliases.size() > 0) { 805 System.out.println(rb.getString( 806 ".X.not.signed.by.specified.alias.es.")); 807 } 808 System.out.println(); 809 } 810 if (man == null) 811 System.out.println(rb.getString("no.manifest.")); 812 813 // If signer is a trusted cert or private entry in user's own 814 // keystore, it can be self-signed. 815 if (!aliasNotInStore) { 816 signerSelfSigned = false; 817 } 818 819 if (!anySigned) { 820 if (hasSignature) { 821 System.out.println(rb.getString("jar.treated.unsigned")); 822 } else { 823 System.out.println(rb.getString("jar.is.unsigned")); 824 } 825 } else { 826 boolean warningAppeared = false; 827 boolean errorAppeared = false; 828 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || 829 notYetValidCert || chainNotValidated || hasExpiredCert || 830 hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) || 831 aliasNotInStore || notSignedByAlias) { 832 833 if (strict) { 834 System.out.println(rb.getString("jar.verified.with.signer.errors.")); 835 System.out.println(); 836 System.out.println(rb.getString("Error.")); 837 errorAppeared = true; 838 } else { 839 System.out.println(rb.getString("jar.verified.")); 840 System.out.println(); 841 System.out.println(rb.getString("Warning.")); 842 warningAppeared = true; 843 } 844 845 if (weakAlg != 0) { 846 // In fact, jarsigner verification did not catch this 847 // since it has not read the JarFile content itself. 848 // Everything is done with JarFile API. 849 } 850 851 if (badKeyUsage) { 852 System.out.println( 853 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); 854 } 855 856 if (badExtendedKeyUsage) { 857 System.out.println( 858 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); 859 } 860 861 if (badNetscapeCertType) { 862 System.out.println( 863 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); 864 } 865 866 if (hasUnsignedEntry) { 867 System.out.println(rb.getString( 868 "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); 869 } 870 if (hasExpiredCert) { 871 System.out.println(rb.getString( 872 "This.jar.contains.entries.whose.signer.certificate.has.expired.")); 873 } 874 if (notYetValidCert) { 875 System.out.println(rb.getString( 876 "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); 877 } 878 879 if (chainNotValidated) { 880 System.out.println(String.format( 881 rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1"), 882 chainNotValidatedReason.getLocalizedMessage())); 883 } 884 885 if (notSignedByAlias) { 886 System.out.println( 887 rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); 888 } 889 890 if (aliasNotInStore) { 891 System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); 892 } 893 894 if (signerSelfSigned) { 895 System.out.println(rb.getString( 896 "This.jar.contains.entries.whose.signer.certificate.is.self.signed.")); 897 } 898 } else { 899 System.out.println(rb.getString("jar.verified.")); 900 } 901 if (hasExpiringCert || noTimestamp) { 902 if (!warningAppeared) { 903 System.out.println(); 904 System.out.println(rb.getString("Warning.")); 905 warningAppeared = true; 906 } 907 if (hasExpiringCert) { 908 System.out.println(rb.getString( 909 "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); 910 } 911 if (noTimestamp) { 912 System.out.println( 913 String.format(rb.getString("no.timestamp.verifying"), expireDate)); 914 } 915 } 916 if (warningAppeared || errorAppeared) { 917 if (! (verbose != null && showcerts)) { 918 System.out.println(); 919 System.out.println(rb.getString( 920 "Re.run.with.the.verbose.and.certs.options.for.more.details.")); 921 } 922 } 923 } 924 return; 925 } catch (Exception e) { 926 System.out.println(rb.getString("jarsigner.") + e); 927 if (debug) { 928 e.printStackTrace(); 929 } 930 } finally { // close the resource 931 if (jf != null) { 932 jf.close(); 933 } 934 } 935 936 System.exit(1); 937 } 938 939 private static MessageFormat validityTimeForm = null; 940 private static MessageFormat notYetTimeForm = null; 941 private static MessageFormat expiredTimeForm = null; 942 private static MessageFormat expiringTimeForm = null; 943 944 /* 945 * Display some details about a certificate: 946 * 947 * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"] 948 * [<validity-period> | <expiry-warning>] 949 * 950 * Note: no newline character at the end 951 */ 952 String printCert(String tab, Certificate c, boolean checkValidityPeriod, 953 Date timestamp, boolean checkUsage) { 954 955 StringBuilder certStr = new StringBuilder(); 956 String space = rb.getString("SPACE"); 957 X509Certificate x509Cert = null; 958 959 if (c instanceof X509Certificate) { 960 x509Cert = (X509Certificate) c; 961 certStr.append(tab).append(x509Cert.getType()) 962 .append(rb.getString("COMMA")) 963 .append(x509Cert.getSubjectDN().getName()); 964 } else { 965 certStr.append(tab).append(c.getType()); 966 } 967 968 String alias = storeHash.get(c); 969 if (alias != null) { 970 certStr.append(space).append(alias); 971 } 972 973 if (checkValidityPeriod && x509Cert != null) { 974 975 certStr.append("\n").append(tab).append("["); 976 Date notAfter = x509Cert.getNotAfter(); 977 try { 978 boolean printValidity = true; 979 if (timestamp == null) { 980 if (expireDate.getTime() == 0 || expireDate.after(notAfter)) { 981 expireDate = notAfter; 982 } 983 x509Cert.checkValidity(); 984 // test if cert will expire within six months 985 if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) { 986 hasExpiringCert = true; 987 if (expiringTimeForm == null) { 988 expiringTimeForm = new MessageFormat( 989 rb.getString("certificate.will.expire.on")); 990 } 991 Object[] source = { notAfter }; 992 certStr.append(expiringTimeForm.format(source)); 993 printValidity = false; 994 } 995 } else { 996 x509Cert.checkValidity(timestamp); 997 } 998 if (printValidity) { 999 if (validityTimeForm == null) { 1000 validityTimeForm = new MessageFormat( 1001 rb.getString("certificate.is.valid.from")); 1002 } 1003 Object[] source = { x509Cert.getNotBefore(), notAfter }; 1004 certStr.append(validityTimeForm.format(source)); 1005 } 1006 } catch (CertificateExpiredException cee) { 1007 hasExpiredCert = true; 1008 1009 if (expiredTimeForm == null) { 1010 expiredTimeForm = new MessageFormat( 1011 rb.getString("certificate.expired.on")); 1012 } 1013 Object[] source = { notAfter }; 1014 certStr.append(expiredTimeForm.format(source)); 1015 1016 } catch (CertificateNotYetValidException cnyve) { 1017 notYetValidCert = true; 1018 1019 if (notYetTimeForm == null) { 1020 notYetTimeForm = new MessageFormat( 1021 rb.getString("certificate.is.not.valid.until")); 1022 } 1023 Object[] source = { x509Cert.getNotBefore() }; 1024 certStr.append(notYetTimeForm.format(source)); 1025 } 1026 certStr.append("]"); 1027 1028 if (checkUsage) { 1029 boolean[] bad = new boolean[3]; 1030 checkCertUsage(x509Cert, bad); 1031 if (bad[0] || bad[1] || bad[2]) { 1032 String x = ""; 1033 if (bad[0]) { 1034 x ="KeyUsage"; 1035 } 1036 if (bad[1]) { 1037 if (x.length() > 0) x = x + ", "; 1038 x = x + "ExtendedKeyUsage"; 1039 } 1040 if (bad[2]) { 1041 if (x.length() > 0) x = x + ", "; 1042 x = x + "NetscapeCertType"; 1043 } 1044 certStr.append("\n").append(tab) 1045 .append(MessageFormat.format(rb.getString( 1046 ".{0}.extension.does.not.support.code.signing."), x)); 1047 } 1048 } 1049 } 1050 return certStr.toString(); 1051 } 1052 1053 private static MessageFormat signTimeForm = null; 1054 1055 private String printTimestamp(String tab, Timestamp timestamp) { 1056 1057 if (signTimeForm == null) { 1058 signTimeForm = 1059 new MessageFormat(rb.getString("entry.was.signed.on")); 1060 } 1061 Object[] source = { timestamp.getTimestamp() }; 1062 1063 return new StringBuilder().append(tab).append("[") 1064 .append(signTimeForm.format(source)).append("]").toString(); 1065 } 1066 1067 private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>(); 1068 1069 private int inKeyStoreForOneSigner(CodeSigner signer) { 1070 if (cacheForInKS.containsKey(signer)) { 1071 return cacheForInKS.get(signer); 1072 } 1073 1074 int result = 0; 1075 List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); 1076 for (Certificate c : certs) { 1077 String alias = storeHash.get(c); 1078 if (alias != null) { 1079 if (alias.startsWith("(")) { 1080 result |= IN_KEYSTORE; 1081 } 1082 if (ckaliases.contains(alias.substring(1, alias.length() - 1))) { 1083 result |= SIGNED_BY_ALIAS; 1084 } 1085 } else { 1086 if (store != null) { 1087 try { 1088 alias = store.getCertificateAlias(c); 1089 } catch (KeyStoreException kse) { 1090 // never happens, because keystore has been loaded 1091 } 1092 if (alias != null) { 1093 storeHash.put(c, "(" + alias + ")"); 1094 result |= IN_KEYSTORE; 1095 } 1096 } 1097 if (ckaliases.contains(alias)) { 1098 result |= SIGNED_BY_ALIAS; 1099 } 1100 } 1101 } 1102 cacheForInKS.put(signer, result); 1103 return result; 1104 } 1105 1106 Hashtable<Certificate, String> storeHash = new Hashtable<>(); 1107 1108 int inKeyStore(CodeSigner[] signers) { 1109 1110 if (signers == null) 1111 return 0; 1112 1113 int output = 0; 1114 1115 for (CodeSigner signer: signers) { 1116 int result = inKeyStoreForOneSigner(signer); 1117 output |= result; 1118 } 1119 if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) { 1120 output |= NOT_ALIAS; 1121 } 1122 return output; 1123 } 1124 1125 void signJar(String jarName, String alias) 1126 throws Exception { 1127 1128 DisabledAlgorithmConstraints dac = 1129 new DisabledAlgorithmConstraints( 1130 DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); 1131 1132 if (digestalg != null && !dac.permits( 1133 Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), digestalg, null)) { 1134 weakAlg |= 1; 1135 } 1136 if (tSADigestAlg != null && !dac.permits( 1137 Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), tSADigestAlg, null)) { 1138 weakAlg |= 4; 1139 } 1140 if (sigalg != null && !dac.permits( 1141 Collections.singleton(CryptoPrimitive.SIGNATURE), sigalg, null)) { 1142 weakAlg |= 2; 1143 } 1144 1145 boolean aliasUsed = false; 1146 X509Certificate tsaCert = null; 1147 1148 if (sigfile == null) { 1149 sigfile = alias; 1150 aliasUsed = true; 1151 } 1152 1153 if (sigfile.length() > 8) { 1154 sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH); 1155 } else { 1156 sigfile = sigfile.toUpperCase(Locale.ENGLISH); 1157 } 1158 1159 StringBuilder tmpSigFile = new StringBuilder(sigfile.length()); 1160 for (int j = 0; j < sigfile.length(); j++) { 1161 char c = sigfile.charAt(j); 1162 if (! 1163 ((c>= 'A' && c<= 'Z') || 1164 (c>= '0' && c<= '9') || 1165 (c == '-') || 1166 (c == '_'))) { 1167 if (aliasUsed) { 1168 // convert illegal characters from the alias to be _'s 1169 c = '_'; 1170 } else { 1171 throw new 1172 RuntimeException(rb.getString 1173 ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.")); 1174 } 1175 } 1176 tmpSigFile.append(c); 1177 } 1178 1179 sigfile = tmpSigFile.toString(); 1180 1181 String tmpJarName; 1182 if (signedjar == null) tmpJarName = jarName+".sig"; 1183 else tmpJarName = signedjar; 1184 1185 File jarFile = new File(jarName); 1186 File signedJarFile = new File(tmpJarName); 1187 1188 // Open the jar (zip) file 1189 try { 1190 zipFile = new ZipFile(jarName); 1191 } catch (IOException ioe) { 1192 error(rb.getString("unable.to.open.jar.file.")+jarName, ioe); 1193 } 1194 1195 FileOutputStream fos = null; 1196 try { 1197 fos = new FileOutputStream(signedJarFile); 1198 } catch (IOException ioe) { 1199 error(rb.getString("unable.to.create.")+tmpJarName, ioe); 1200 } 1201 1202 CertPath cp = CertificateFactory.getInstance("X.509") 1203 .generateCertPath(Arrays.asList(certChain)); 1204 JarSigner.Builder builder = new JarSigner.Builder(privateKey, cp); 1205 1206 if (verbose != null) { 1207 builder.eventHandler((action, file) -> { 1208 System.out.println(rb.getString("." + action + ".") + file); 1209 }); 1210 } 1211 1212 if (digestalg != null) { 1213 builder.digestAlgorithm(digestalg); 1214 } 1215 if (sigalg != null) { 1216 builder.signatureAlgorithm(sigalg); 1217 } 1218 1219 URI tsaURI = null; 1220 1221 if (tsaUrl != null) { 1222 tsaURI = new URI(tsaUrl); 1223 } else if (tsaAlias != null) { 1224 tsaCert = getTsaCert(tsaAlias); 1225 tsaURI = TimestampedSigner.getTimestampingURI(tsaCert); 1226 } 1227 1228 if (tsaURI != null) { 1229 if (verbose != null) { 1230 System.out.println( 1231 rb.getString("requesting.a.signature.timestamp")); 1232 if (tsaUrl != null) { 1233 System.out.println(rb.getString("TSA.location.") + tsaUrl); 1234 } else if (tsaCert != null) { 1235 System.out.println(rb.getString("TSA.certificate.") + 1236 printCert("", tsaCert, false, null, false)); 1237 } 1238 } 1239 builder.tsa(tsaURI); 1240 if (tSADigestAlg != null) { 1241 builder.setProperty("tsaDigestAlg", tSADigestAlg); 1242 } 1243 1244 if (tSAPolicyID != null) { 1245 builder.setProperty("tsaPolicyId", tSAPolicyID); 1246 } 1247 } else { 1248 noTimestamp = true; 1249 } 1250 1251 if (altSignerClass != null) { 1252 builder.setProperty("altSigner", altSignerClass); 1253 if (verbose != null) { 1254 System.out.println( 1255 rb.getString("using.an.alternative.signing.mechanism")); 1256 } 1257 } 1258 1259 if (altSignerClasspath != null) { 1260 builder.setProperty("altSignerPath", altSignerClasspath); 1261 } 1262 1263 builder.signerName(sigfile); 1264 1265 builder.setProperty("sectionsOnly", Boolean.toString(!signManifest)); 1266 builder.setProperty("internalSF", Boolean.toString(!externalSF)); 1267 1268 try { 1269 builder.build().sign(zipFile, fos); 1270 } catch (JarSignerException e) { 1271 Throwable cause = e.getCause(); 1272 if (cause != null && cause instanceof SocketTimeoutException) { 1273 // Provide a helpful message when TSA is beyond a firewall 1274 error(rb.getString("unable.to.sign.jar.") + 1275 rb.getString("no.response.from.the.Timestamping.Authority.") + 1276 "\n -J-Dhttp.proxyHost=<hostname>" + 1277 "\n -J-Dhttp.proxyPort=<portnumber>\n" + 1278 rb.getString("or") + 1279 "\n -J-Dhttps.proxyHost=<hostname> " + 1280 "\n -J-Dhttps.proxyPort=<portnumber> ", e); 1281 } else { 1282 error(rb.getString("unable.to.sign.jar.")+e.getCause(), e.getCause()); 1283 } 1284 } finally { 1285 // close the resouces 1286 if (zipFile != null) { 1287 zipFile.close(); 1288 zipFile = null; 1289 } 1290 1291 if (fos != null) { 1292 fos.close(); 1293 } 1294 } 1295 1296 // no IOException thrown in the follow try clause, so disable 1297 // the try clause. 1298 // try { 1299 if (signedjar == null) { 1300 // attempt an atomic rename. If that fails, 1301 // rename the original jar file, then the signed 1302 // one, then delete the original. 1303 if (!signedJarFile.renameTo(jarFile)) { 1304 File origJar = new File(jarName+".orig"); 1305 1306 if (jarFile.renameTo(origJar)) { 1307 if (signedJarFile.renameTo(jarFile)) { 1308 origJar.delete(); 1309 } else { 1310 MessageFormat form = new MessageFormat(rb.getString 1311 ("attempt.to.rename.signedJarFile.to.jarFile.failed")); 1312 Object[] source = {signedJarFile, jarFile}; 1313 error(form.format(source)); 1314 } 1315 } else { 1316 MessageFormat form = new MessageFormat(rb.getString 1317 ("attempt.to.rename.jarFile.to.origJar.failed")); 1318 Object[] source = {jarFile, origJar}; 1319 error(form.format(source)); 1320 } 1321 } 1322 } 1323 1324 boolean warningAppeared = false; 1325 if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || 1326 notYetValidCert || chainNotValidated || hasExpiredCert || signerSelfSigned) { 1327 if (strict) { 1328 System.out.println(rb.getString("jar.signed.with.signer.errors.")); 1329 System.out.println(); 1330 System.out.println(rb.getString("Error.")); 1331 } else { 1332 System.out.println(rb.getString("jar.signed.")); 1333 System.out.println(); 1334 System.out.println(rb.getString("Warning.")); 1335 warningAppeared = true; 1336 } 1337 1338 if (badKeyUsage) { 1339 System.out.println( 1340 rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); 1341 } 1342 1343 if (badExtendedKeyUsage) { 1344 System.out.println( 1345 rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); 1346 } 1347 1348 if (badNetscapeCertType) { 1349 System.out.println( 1350 rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); 1351 } 1352 1353 if (hasExpiredCert) { 1354 System.out.println( 1355 rb.getString("The.signer.certificate.has.expired.")); 1356 } else if (notYetValidCert) { 1357 System.out.println( 1358 rb.getString("The.signer.certificate.is.not.yet.valid.")); 1359 } 1360 1361 if (chainNotValidated) { 1362 System.out.println(String.format( 1363 rb.getString("The.signer.s.certificate.chain.is.not.validated.reason.1"), 1364 chainNotValidatedReason.getLocalizedMessage())); 1365 } 1366 1367 if (signerSelfSigned) { 1368 System.out.println( 1369 rb.getString("The.signer.s.certificate.is.self.signed.")); 1370 } 1371 1372 if ((weakAlg & 1) == 1) { 1373 System.out.println(String.format( 1374 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), 1375 digestalg, "-digestalg")); 1376 } 1377 1378 if ((weakAlg & 2) == 2) { 1379 System.out.println(String.format( 1380 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), 1381 sigalg, "-sigalg")); 1382 } 1383 if ((weakAlg & 4) == 4) { 1384 System.out.println(String.format( 1385 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), 1386 tSADigestAlg, "-tsadigestalg")); 1387 } 1388 } else { 1389 System.out.println(rb.getString("jar.signed.")); 1390 } 1391 if (hasExpiringCert || noTimestamp) { 1392 if (!warningAppeared) { 1393 System.out.println(); 1394 System.out.println(rb.getString("Warning.")); 1395 } 1396 1397 if (hasExpiringCert) { 1398 System.out.println( 1399 rb.getString("The.signer.certificate.will.expire.within.six.months.")); 1400 } 1401 1402 if (noTimestamp) { 1403 System.out.println( 1404 String.format(rb.getString("no.timestamp.signing"), expireDate)); 1405 } 1406 } 1407 1408 // no IOException thrown in the above try clause, so disable 1409 // the catch clause. 1410 // } catch(IOException ioe) { 1411 // error(rb.getString("unable.to.sign.jar.")+ioe, ioe); 1412 // } 1413 } 1414 1415 /** 1416 * signature-related files include: 1417 * . META-INF/MANIFEST.MF 1418 * . META-INF/SIG-* 1419 * . META-INF/*.SF 1420 * . META-INF/*.DSA 1421 * . META-INF/*.RSA 1422 * . META-INF/*.EC 1423 */ 1424 private boolean signatureRelated(String name) { 1425 return SignatureFileVerifier.isSigningRelated(name); 1426 } 1427 1428 Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>(); 1429 1430 /** 1431 * Returns a string of singer info, with a newline at the end 1432 */ 1433 private String signerInfo(CodeSigner signer, String tab) { 1434 if (cacheForSignerInfo.containsKey(signer)) { 1435 return cacheForSignerInfo.get(signer); 1436 } 1437 StringBuilder sb = new StringBuilder(); 1438 List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); 1439 // display the signature timestamp, if present 1440 Date timestamp; 1441 Timestamp ts = signer.getTimestamp(); 1442 if (ts != null) { 1443 sb.append(printTimestamp(tab, ts)); 1444 sb.append('\n'); 1445 timestamp = ts.getTimestamp(); 1446 } else { 1447 timestamp = null; 1448 noTimestamp = true; 1449 } 1450 // display the certificate(sb). The first one is end-entity cert and 1451 // its KeyUsage should be checked. 1452 boolean first = true; 1453 for (Certificate c : certs) { 1454 sb.append(printCert(tab, c, true, timestamp, first)); 1455 sb.append('\n'); 1456 first = false; 1457 } 1458 try { 1459 validateCertChain(certs); 1460 } catch (Exception e) { 1461 if (debug) { 1462 e.printStackTrace(); 1463 } 1464 if (e.getCause() != null && 1465 (e.getCause() instanceof CertificateExpiredException || 1466 e.getCause() instanceof CertificateNotYetValidException)) { 1467 // No more warning, we alreay have hasExpiredCert or notYetValidCert 1468 } else { 1469 chainNotValidated = true; 1470 chainNotValidatedReason = e; 1471 sb.append(tab).append(rb.getString(".CertPath.not.validated.")) 1472 .append(e.getLocalizedMessage()).append("]\n"); // TODO 1473 } 1474 } 1475 if (certs.size() == 1 1476 && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) { 1477 signerSelfSigned = true; 1478 } 1479 String result = sb.toString(); 1480 cacheForSignerInfo.put(signer, result); 1481 return result; 1482 } 1483 1484 void loadKeyStore(String keyStoreName, boolean prompt) { 1485 1486 if (!nullStream && keyStoreName == null) { 1487 keyStoreName = System.getProperty("user.home") + File.separator 1488 + ".keystore"; 1489 } 1490 1491 try { 1492 1493 certificateFactory = CertificateFactory.getInstance("X.509"); 1494 validator = CertPathValidator.getInstance("PKIX"); 1495 Set<TrustAnchor> tas = new HashSet<>(); 1496 try { 1497 KeyStore caks = KeyStoreUtil.getCacertsKeyStore(); 1498 if (caks != null) { 1499 Enumeration<String> aliases = caks.aliases(); 1500 while (aliases.hasMoreElements()) { 1501 String a = aliases.nextElement(); 1502 try { 1503 tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null)); 1504 } catch (Exception e2) { 1505 // ignore, when a SecretkeyEntry does not include a cert 1506 } 1507 } 1508 } 1509 } catch (Exception e) { 1510 // Ignore, if cacerts cannot be loaded 1511 } 1512 1513 if (providerName == null) { 1514 store = KeyStore.getInstance(storetype); 1515 } else { 1516 store = KeyStore.getInstance(storetype, providerName); 1517 } 1518 1519 // Get pass phrase 1520 // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z 1521 // and on NT call ?? 1522 if (token && storepass == null && !protectedPath 1523 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { 1524 storepass = getPass 1525 (rb.getString("Enter.Passphrase.for.keystore.")); 1526 } else if (!token && storepass == null && prompt) { 1527 storepass = getPass 1528 (rb.getString("Enter.Passphrase.for.keystore.")); 1529 } 1530 1531 try { 1532 if (nullStream) { 1533 store.load(null, storepass); 1534 } else { 1535 keyStoreName = keyStoreName.replace(File.separatorChar, '/'); 1536 URL url = null; 1537 try { 1538 url = new URL(keyStoreName); 1539 } catch (java.net.MalformedURLException e) { 1540 // try as file 1541 url = new File(keyStoreName).toURI().toURL(); 1542 } 1543 InputStream is = null; 1544 try { 1545 is = url.openStream(); 1546 store.load(is, storepass); 1547 } finally { 1548 if (is != null) { 1549 is.close(); 1550 } 1551 } 1552 } 1553 Enumeration<String> aliases = store.aliases(); 1554 while (aliases.hasMoreElements()) { 1555 String a = aliases.nextElement(); 1556 try { 1557 X509Certificate c = (X509Certificate)store.getCertificate(a); 1558 // Only add TrustedCertificateEntry and self-signed 1559 // PrivateKeyEntry 1560 if (store.isCertificateEntry(a) || 1561 c.getSubjectDN().equals(c.getIssuerDN())) { 1562 tas.add(new TrustAnchor(c, null)); 1563 } 1564 } catch (Exception e2) { 1565 // ignore, when a SecretkeyEntry does not include a cert 1566 } 1567 } 1568 } finally { 1569 try { 1570 pkixParameters = new PKIXParameters(tas); 1571 pkixParameters.setRevocationEnabled(false); 1572 } catch (InvalidAlgorithmParameterException ex) { 1573 // Only if tas is empty 1574 } 1575 } 1576 } catch (IOException ioe) { 1577 throw new RuntimeException(rb.getString("keystore.load.") + 1578 ioe.getMessage()); 1579 } catch (java.security.cert.CertificateException ce) { 1580 throw new RuntimeException(rb.getString("certificate.exception.") + 1581 ce.getMessage()); 1582 } catch (NoSuchProviderException pe) { 1583 throw new RuntimeException(rb.getString("keystore.load.") + 1584 pe.getMessage()); 1585 } catch (NoSuchAlgorithmException nsae) { 1586 throw new RuntimeException(rb.getString("keystore.load.") + 1587 nsae.getMessage()); 1588 } catch (KeyStoreException kse) { 1589 throw new RuntimeException 1590 (rb.getString("unable.to.instantiate.keystore.class.") + 1591 kse.getMessage()); 1592 } 1593 } 1594 1595 X509Certificate getTsaCert(String alias) { 1596 1597 java.security.cert.Certificate cs = null; 1598 1599 try { 1600 cs = store.getCertificate(alias); 1601 } catch (KeyStoreException kse) { 1602 // this never happens, because keystore has been loaded 1603 } 1604 if (cs == null || (!(cs instanceof X509Certificate))) { 1605 MessageFormat form = new MessageFormat(rb.getString 1606 ("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the")); 1607 Object[] source = {alias, alias}; 1608 error(form.format(source)); 1609 } 1610 return (X509Certificate) cs; 1611 } 1612 1613 /** 1614 * Check if userCert is designed to be a code signer 1615 * @param userCert the certificate to be examined 1616 * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage, 1617 * NetscapeCertType has codeSigning flag turned on. 1618 * If null, the class field badKeyUsage, badExtendedKeyUsage, 1619 * badNetscapeCertType will be set. 1620 */ 1621 void checkCertUsage(X509Certificate userCert, boolean[] bad) { 1622 1623 // Can act as a signer? 1624 // 1. if KeyUsage, then [0:digitalSignature] or 1625 // [1:nonRepudiation] should be true 1626 // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING 1627 // 3. if NetscapeCertType, then should contains OBJECT_SIGNING 1628 // 1,2,3 must be true 1629 1630 if (bad != null) { 1631 bad[0] = bad[1] = bad[2] = false; 1632 } 1633 1634 boolean[] keyUsage = userCert.getKeyUsage(); 1635 if (keyUsage != null) { 1636 keyUsage = Arrays.copyOf(keyUsage, 9); 1637 if (!keyUsage[0] && !keyUsage[1]) { 1638 if (bad != null) { 1639 bad[0] = true; 1640 badKeyUsage = true; 1641 } 1642 } 1643 } 1644 1645 try { 1646 List<String> xKeyUsage = userCert.getExtendedKeyUsage(); 1647 if (xKeyUsage != null) { 1648 if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage 1649 && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning 1650 if (bad != null) { 1651 bad[1] = true; 1652 badExtendedKeyUsage = true; 1653 } 1654 } 1655 } 1656 } catch (java.security.cert.CertificateParsingException e) { 1657 // shouldn't happen 1658 } 1659 1660 try { 1661 // OID_NETSCAPE_CERT_TYPE 1662 byte[] netscapeEx = userCert.getExtensionValue 1663 ("2.16.840.1.113730.1.1"); 1664 if (netscapeEx != null) { 1665 DerInputStream in = new DerInputStream(netscapeEx); 1666 byte[] encoded = in.getOctetString(); 1667 encoded = new DerValue(encoded).getUnalignedBitString() 1668 .toByteArray(); 1669 1670 NetscapeCertTypeExtension extn = 1671 new NetscapeCertTypeExtension(encoded); 1672 1673 Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING); 1674 if (!val) { 1675 if (bad != null) { 1676 bad[2] = true; 1677 badNetscapeCertType = true; 1678 } 1679 } 1680 } 1681 } catch (IOException e) { 1682 // 1683 } 1684 } 1685 1686 void getAliasInfo(String alias) { 1687 1688 Key key = null; 1689 1690 try { 1691 java.security.cert.Certificate[] cs = null; 1692 if (altCertChain != null) { 1693 try (FileInputStream fis = new FileInputStream(altCertChain)) { 1694 cs = CertificateFactory.getInstance("X.509"). 1695 generateCertificates(fis). 1696 toArray(new Certificate[0]); 1697 } catch (FileNotFoundException ex) { 1698 error(rb.getString("File.specified.by.certchain.does.not.exist")); 1699 } catch (CertificateException | IOException ex) { 1700 error(rb.getString("Cannot.restore.certchain.from.file.specified")); 1701 } 1702 } else { 1703 try { 1704 cs = store.getCertificateChain(alias); 1705 } catch (KeyStoreException kse) { 1706 // this never happens, because keystore has been loaded 1707 } 1708 } 1709 if (cs == null || cs.length == 0) { 1710 if (altCertChain != null) { 1711 error(rb.getString 1712 ("Certificate.chain.not.found.in.the.file.specified.")); 1713 } else { 1714 MessageFormat form = new MessageFormat(rb.getString 1715 ("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and")); 1716 Object[] source = {alias, alias}; 1717 error(form.format(source)); 1718 } 1719 } 1720 1721 certChain = new X509Certificate[cs.length]; 1722 for (int i=0; i<cs.length; i++) { 1723 if (!(cs[i] instanceof X509Certificate)) { 1724 error(rb.getString 1725 ("found.non.X.509.certificate.in.signer.s.chain")); 1726 } 1727 certChain[i] = (X509Certificate)cs[i]; 1728 } 1729 1730 // We don't meant to print anything, the next call 1731 // checks validity and keyUsage etc 1732 printCert("", certChain[0], true, null, true); 1733 1734 try { 1735 validateCertChain(Arrays.asList(certChain)); 1736 } catch (Exception e) { 1737 if (debug) { 1738 e.printStackTrace(); 1739 } 1740 if (e.getCause() != null && 1741 (e.getCause() instanceof CertificateExpiredException || 1742 e.getCause() instanceof CertificateNotYetValidException)) { 1743 // No more warning, we already have hasExpiredCert or notYetValidCert 1744 } else { 1745 chainNotValidated = true; 1746 chainNotValidatedReason = e; 1747 } 1748 } 1749 1750 if (KeyStoreUtil.isSelfSigned(certChain[0])) { 1751 signerSelfSigned = true; 1752 } 1753 1754 try { 1755 if (!token && keypass == null) 1756 key = store.getKey(alias, storepass); 1757 else 1758 key = store.getKey(alias, keypass); 1759 } catch (UnrecoverableKeyException e) { 1760 if (token) { 1761 throw e; 1762 } else if (keypass == null) { 1763 // Did not work out, so prompt user for key password 1764 MessageFormat form = new MessageFormat(rb.getString 1765 ("Enter.key.password.for.alias.")); 1766 Object[] source = {alias}; 1767 keypass = getPass(form.format(source)); 1768 key = store.getKey(alias, keypass); 1769 } 1770 } 1771 } catch (NoSuchAlgorithmException e) { 1772 error(e.getMessage()); 1773 } catch (UnrecoverableKeyException e) { 1774 error(rb.getString("unable.to.recover.key.from.keystore")); 1775 } catch (KeyStoreException kse) { 1776 // this never happens, because keystore has been loaded 1777 } 1778 1779 if (!(key instanceof PrivateKey)) { 1780 MessageFormat form = new MessageFormat(rb.getString 1781 ("key.associated.with.alias.not.a.private.key")); 1782 Object[] source = {alias}; 1783 error(form.format(source)); 1784 } else { 1785 privateKey = (PrivateKey)key; 1786 } 1787 } 1788 1789 void error(String message) { 1790 System.out.println(rb.getString("jarsigner.")+message); 1791 System.exit(1); 1792 } 1793 1794 1795 void error(String message, Throwable e) { 1796 System.out.println(rb.getString("jarsigner.")+message); 1797 if (debug) { 1798 e.printStackTrace(); 1799 } 1800 System.exit(1); 1801 } 1802 1803 void validateCertChain(List<? extends Certificate> certs) throws Exception { 1804 int cpLen = 0; 1805 out: for (; cpLen<certs.size(); cpLen++) { 1806 for (TrustAnchor ta: pkixParameters.getTrustAnchors()) { 1807 if (ta.getTrustedCert().equals(certs.get(cpLen))) { 1808 break out; 1809 } 1810 } 1811 } 1812 if (cpLen > 0) { 1813 CertPath cp = certificateFactory.generateCertPath( 1814 (cpLen == certs.size())? certs: certs.subList(0, cpLen)); 1815 validator.validate(cp, pkixParameters); 1816 } 1817 } 1818 1819 char[] getPass(String prompt) { 1820 System.err.print(prompt); 1821 System.err.flush(); 1822 try { 1823 char[] pass = Password.readPassword(System.in); 1824 1825 if (pass == null) { 1826 error(rb.getString("you.must.enter.key.password")); 1827 } else { 1828 return pass; 1829 } 1830 } catch (IOException ioe) { 1831 error(rb.getString("unable.to.read.password.")+ioe.getMessage()); 1832 } 1833 // this shouldn't happen 1834 return null; 1835 } 1836 }