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