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