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