1 /* 2 * Copyright (c) 1997, 2013, 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.math.BigInteger; 33 import java.net.URI; 34 import java.net.URISyntaxException; 35 import java.text.Collator; 36 import java.text.MessageFormat; 37 import java.security.cert.Certificate; 38 import java.security.cert.X509Certificate; 39 import java.security.cert.CertificateException; 40 import java.security.*; 41 import java.lang.reflect.Constructor; 42 43 import com.sun.jarsigner.ContentSigner; 44 import com.sun.jarsigner.ContentSignerParameters; 45 import java.net.SocketTimeoutException; 46 import java.net.URL; 47 import java.net.URLClassLoader; 48 import java.security.cert.CertPath; 49 import java.security.cert.CertPathValidator; 50 import java.security.cert.CertificateExpiredException; 51 import java.security.cert.CertificateFactory; 52 import java.security.cert.CertificateNotYetValidException; 53 import java.security.cert.PKIXParameters; 54 import java.security.cert.TrustAnchor; 55 import java.util.Map.Entry; 56 import sun.security.pkcs.PKCS7; 57 import sun.security.pkcs.SignerInfo; 58 import sun.security.timestamp.TimestampToken; 59 import sun.security.tools.KeyStoreUtil; 60 import sun.security.tools.PathList; 61 import sun.security.x509.*; 62 import sun.security.util.*; 63 import java.util.Base64; 64 65 66 /** 67 * <p>The jarsigner utility. 68 * 69 * The exit codes for the main method are: 70 * 71 * 0: success 72 * 1: any error that the jar cannot be signed or verified, including: 73 * keystore loading error 74 * TSP communication error 75 * jarsigner command line error... 76 * otherwise: error codes from -strict 77 * 78 * @author Roland Schemers 79 * @author Jan Luehe 80 */ 81 82 public class Main { 83 84 // for i18n 85 private static final java.util.ResourceBundle rb = 86 java.util.ResourceBundle.getBundle 87 ("sun.security.tools.jarsigner.Resources"); 88 private static final Collator collator = Collator.getInstance(); 89 static { 90 // this is for case insensitive string comparisions 91 collator.setStrength(Collator.PRIMARY); 92 } 93 94 private static final String META_INF = "META-INF/"; 95 96 private static final Class<?>[] PARAM_STRING = { String.class }; 97 98 private static final String NONE = "NONE"; 99 private static final String P11KEYSTORE = "PKCS11"; 100 101 private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds 102 103 private static final DisabledAlgorithmConstraints DISABLED_CHECK = 104 new DisabledAlgorithmConstraints( 105 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); 106 107 private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections 108 .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); 109 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections 110 .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 111 112 // Attention: 113 // This is the entry that get launched by the security tool jarsigner. 114 public static void main(String args[]) throws Exception { 115 Main js = new Main(); 116 js.run(args); 117 } 118 119 static final String VERSION = "1.0"; 120 121 static final int IN_KEYSTORE = 0x01; // signer is in keystore 122 static final int IN_SCOPE = 0x02; 123 static final int NOT_ALIAS = 0x04; // alias list is NOT empty and 124 // signer is not in alias list 125 static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list 126 127 X509Certificate[] certChain; // signer's cert chain (when composing) 128 PrivateKey privateKey; // private key 129 KeyStore store; // the keystore specified by -keystore 130 // or the default keystore, never null 131 132 String keystore; // key store file 133 boolean nullStream = false; // null keystore input stream (NONE) 134 boolean token = false; // token-based keystore 135 String jarfile; // jar files to sign or verify 136 String alias; // alias to sign jar with 137 List<String> ckaliases = new ArrayList<>(); // aliases in -verify 138 char[] storepass; // keystore password 139 boolean protectedPath; // protected authentication path 140 String storetype; // keystore type 141 String providerName; // provider name 142 Vector<String> providers = null; // list of providers 143 // arguments for provider constructors 144 HashMap<String,String> providerArgs = new HashMap<>(); 145 char[] keypass; // private key password 146 String sigfile; // name of .SF file 147 String sigalg; // name of signature algorithm 148 String digestalg = "SHA-256"; // name of digest algorithm 149 String signedjar; // output filename 150 String tsaUrl; // location of the Timestamping Authority 151 String tsaAlias; // alias for the Timestamping Authority's certificate 152 String altCertChain; // file to read alternative cert chain from 153 String tSAPolicyID; 154 String tSADigestAlg = "SHA-256"; 155 boolean verify = false; // verify the jar 156 String verbose = null; // verbose output when signing/verifying 157 boolean showcerts = false; // show certs when verifying 158 boolean debug = false; // debug 159 boolean signManifest = true; // "sign" the whole manifest 160 boolean externalSF = true; // leave the .SF out of the PKCS7 block 161 boolean strict = false; // treat warnings as error 162 163 // read zip entry raw bytes 164 private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); 165 private byte[] buffer = new byte[8192]; 166 private ContentSigner signingMechanism = null; 167 private String altSignerClass = null; 168 private String altSignerClasspath = null; 169 private ZipFile zipFile = null; 170 171 // Informational warnings 172 private boolean hasExpiringCert = false; 173 private boolean noTimestamp = false; 174 private Date expireDate = new Date(0L); // used in noTimestamp warning 175 176 // Severe warnings 177 private boolean hasExpiredCert = false; 178 private boolean notYetValidCert = false; 179 private boolean chainNotValidated = false; 180 private boolean notSignedByAlias = false; 181 private boolean aliasNotInStore = false; 182 private boolean hasUnsignedEntry = false; 183 private boolean badKeyUsage = false; 184 private boolean badExtendedKeyUsage = false; 185 private boolean badNetscapeCertType = false; 186 187 private boolean seeWeak = false; 188 189 CertificateFactory certificateFactory; 190 CertPathValidator validator; 191 PKIXParameters pkixParameters; 192 193 public void run(String args[]) { 194 try { 195 parseArgs(args); 196 197 // Try to load and install the specified providers 198 if (providers != null) { 199 ClassLoader cl = ClassLoader.getSystemClassLoader(); 200 Enumeration<String> e = providers.elements(); 201 while (e.hasMoreElements()) { 202 String provName = e.nextElement(); 203 Class<?> provClass; 204 if (cl != null) { 205 provClass = cl.loadClass(provName); 206 } else { 207 provClass = Class.forName(provName); 208 } 209 210 String provArg = providerArgs.get(provName); 211 Object obj; 212 if (provArg == null) { 213 obj = provClass.newInstance(); 214 } else { 215 Constructor<?> c = 216 provClass.getConstructor(PARAM_STRING); 217 obj = c.newInstance(provArg); 218 } 219 220 if (!(obj instanceof Provider)) { 221 MessageFormat form = new MessageFormat(rb.getString 222 ("provName.not.a.provider")); 223 Object[] source = {provName}; 224 throw new Exception(form.format(source)); 225 } 226 Security.addProvider((Provider)obj); 227 } 228 } 229 230 if (verify) { 231 try { 232 loadKeyStore(keystore, false); 233 } catch (Exception e) { 234 if ((keystore != null) || (storepass != null)) { 235 System.out.println(rb.getString("jarsigner.error.") + 236 e.getMessage()); 237 System.exit(1); 238 } 239 } 240 /* if (debug) { 241 SignatureFileVerifier.setDebug(true); 242 ManifestEntryVerifier.setDebug(true); 243 } 244 */ 245 verifyJar(jarfile); 246 } else { 247 loadKeyStore(keystore, true); 248 getAliasInfo(alias); 249 250 // load the alternative signing mechanism 251 if (altSignerClass != null) { 252 signingMechanism = loadSigningMechanism(altSignerClass, 253 altSignerClasspath); 254 } 255 signJar(jarfile, alias, args); 256 } 257 } catch (Exception e) { 258 System.out.println(rb.getString("jarsigner.error.") + e); 259 if (debug) { 260 e.printStackTrace(); 261 } 262 System.exit(1); 263 } finally { 264 // zero-out private key password 265 if (keypass != null) { 266 Arrays.fill(keypass, ' '); 267 keypass = null; 268 } 269 // zero-out keystore password 270 if (storepass != null) { 271 Arrays.fill(storepass, ' '); 272 storepass = null; 273 } 274 } 275 276 if (strict) { 277 int exitCode = 0; 278 if (chainNotValidated || hasExpiredCert || notYetValidCert) { 279 exitCode |= 4; 280 } 281 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) { 282 exitCode |= 8; 283 } 284 if (hasUnsignedEntry) { 285 exitCode |= 16; 286 } 287 if (notSignedByAlias || aliasNotInStore) { 288 exitCode |= 32; 289 } 290 if (exitCode != 0) { 291 System.exit(exitCode); 292 } 293 } 294 } 295 296 /* 297 * Parse command line arguments. 298 */ 299 void parseArgs(String args[]) { 300 /* parse flags */ 301 int n = 0; 302 303 if (args.length == 0) fullusage(); 304 for (n=0; n < args.length; n++) { 305 306 String flags = args[n]; 307 String modifier = null; 308 309 if (flags.startsWith("-")) { 310 int pos = flags.indexOf(':'); 311 if (pos > 0) { 312 modifier = flags.substring(pos+1); 313 flags = flags.substring(0, pos); 314 } 315 } 316 317 if (!flags.startsWith("-")) { 318 if (jarfile == null) { 319 jarfile = flags; 320 } else { 321 alias = flags; 322 ckaliases.add(alias); 323 } 324 } else if (collator.compare(flags, "-keystore") == 0) { 325 if (++n == args.length) usageNoArg(); 326 keystore = args[n]; 327 } else if (collator.compare(flags, "-storepass") ==0) { 328 if (++n == args.length) usageNoArg(); 329 storepass = getPass(modifier, args[n]); 330 } else if (collator.compare(flags, "-storetype") ==0) { 331 if (++n == args.length) usageNoArg(); 332 storetype = args[n]; 333 } else if (collator.compare(flags, "-providerName") ==0) { 334 if (++n == args.length) usageNoArg(); 335 providerName = args[n]; 336 } else if ((collator.compare(flags, "-provider") == 0) || 337 (collator.compare(flags, "-providerClass") == 0)) { 338 if (++n == args.length) usageNoArg(); 339 if (providers == null) { 340 providers = new Vector<String>(3); 341 } 342 providers.add(args[n]); 343 344 if (args.length > (n+1)) { 345 flags = args[n+1]; 346 if (collator.compare(flags, "-providerArg") == 0) { 347 if (args.length == (n+2)) usageNoArg(); 348 providerArgs.put(args[n], args[n+2]); 349 n += 2; 350 } 351 } 352 } else if (collator.compare(flags, "-protected") ==0) { 353 protectedPath = true; 354 } else if (collator.compare(flags, "-certchain") ==0) { 355 if (++n == args.length) usageNoArg(); 356 altCertChain = args[n]; 357 } else if (collator.compare(flags, "-tsapolicyid") ==0) { 358 if (++n == args.length) usageNoArg(); 359 tSAPolicyID = args[n]; 360 } else if (collator.compare(flags, "-tsadigestalg") ==0) { 361 if (++n == args.length) usageNoArg(); 362 tSADigestAlg = args[n]; 363 } else if (collator.compare(flags, "-debug") ==0) { 364 debug = true; 365 } else if (collator.compare(flags, "-keypass") ==0) { 366 if (++n == args.length) usageNoArg(); 367 keypass = getPass(modifier, args[n]); 368 } else if (collator.compare(flags, "-sigfile") ==0) { 369 if (++n == args.length) usageNoArg(); 370 sigfile = args[n]; 371 } else if (collator.compare(flags, "-signedjar") ==0) { 372 if (++n == args.length) usageNoArg(); 373 signedjar = args[n]; 374 } else if (collator.compare(flags, "-tsa") ==0) { 375 if (++n == args.length) usageNoArg(); 376 tsaUrl = args[n]; 377 } else if (collator.compare(flags, "-tsacert") ==0) { 378 if (++n == args.length) usageNoArg(); 379 tsaAlias = args[n]; 380 } else if (collator.compare(flags, "-altsigner") ==0) { 381 if (++n == args.length) usageNoArg(); 382 altSignerClass = args[n]; 383 } else if (collator.compare(flags, "-altsignerpath") ==0) { 384 if (++n == args.length) usageNoArg(); 385 altSignerClasspath = args[n]; 386 } else if (collator.compare(flags, "-sectionsonly") ==0) { 387 signManifest = false; 388 } else if (collator.compare(flags, "-internalsf") ==0) { 389 externalSF = false; 390 } else if (collator.compare(flags, "-verify") ==0) { 391 verify = true; 392 } else if (collator.compare(flags, "-verbose") ==0) { 393 verbose = (modifier != null) ? modifier : "all"; 394 } else if (collator.compare(flags, "-sigalg") ==0) { 395 if (++n == args.length) usageNoArg(); 396 sigalg = args[n]; 397 } else if (collator.compare(flags, "-digestalg") ==0) { 398 if (++n == args.length) usageNoArg(); 399 digestalg = args[n]; 400 } else if (collator.compare(flags, "-certs") ==0) { 401 showcerts = true; 402 } else if (collator.compare(flags, "-strict") ==0) { 403 strict = true; 404 } else if (collator.compare(flags, "-h") == 0 || 405 collator.compare(flags, "-help") == 0) { 406 fullusage(); 407 } else { 408 System.err.println( 409 rb.getString("Illegal.option.") + flags); 410 usage(); 411 } 412 } 413 414 // -certs must always be specified with -verbose 415 if (verbose == null) showcerts = false; 416 417 if (jarfile == null) { 418 System.err.println(rb.getString("Please.specify.jarfile.name")); 419 usage(); 420 } 421 if (!verify && alias == null) { 422 System.err.println(rb.getString("Please.specify.alias.name")); 423 usage(); 424 } 425 if (!verify && ckaliases.size() > 1) { 426 System.err.println(rb.getString("Only.one.alias.can.be.specified")); 427 usage(); 428 } 429 430 if (storetype == null) { 431 storetype = KeyStore.getDefaultType(); 432 } 433 storetype = KeyStoreUtil.niceStoreTypeName(storetype); 434 435 try { 436 if (signedjar != null && new File(signedjar).getCanonicalPath().equals( 437 new File(jarfile).getCanonicalPath())) { 438 signedjar = null; 439 } 440 } catch (IOException ioe) { 441 // File system error? 442 // Just ignore it. 443 } 444 445 if (P11KEYSTORE.equalsIgnoreCase(storetype) || 446 KeyStoreUtil.isWindowsKeyStore(storetype)) { 447 token = true; 448 if (keystore == null) { 449 keystore = NONE; 450 } 451 } 452 453 if (NONE.equals(keystore)) { 454 nullStream = true; 455 } 456 457 if (token && !nullStream) { 458 System.err.println(MessageFormat.format(rb.getString 459 (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype)); 460 usage(); 461 } 462 463 if (token && keypass != null) { 464 System.err.println(MessageFormat.format(rb.getString 465 (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype)); 466 usage(); 467 } 468 469 if (protectedPath) { 470 if (storepass != null || keypass != null) { 471 System.err.println(rb.getString 472 ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified")); 473 usage(); 474 } 475 } 476 if (KeyStoreUtil.isWindowsKeyStore(storetype)) { 477 if (storepass != null || keypass != null) { 478 System.err.println(rb.getString 479 ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified")); 480 usage(); 481 } 482 } 483 } 484 485 static char[] getPass(String modifier, String arg) { 486 char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb); 487 if (output != null) return output; 488 usage(); 489 return null; // Useless, usage() already exit 490 } 491 492 static void usageNoArg() { 493 System.out.println(rb.getString("Option.lacks.argument")); 494 usage(); 495 } 496 497 static void usage() { 498 System.out.println(); 499 System.out.println(rb.getString("Please.type.jarsigner.help.for.usage")); 500 System.exit(1); 501 } 502 503 static void fullusage() { 504 System.out.println(rb.getString 505 ("Usage.jarsigner.options.jar.file.alias")); 506 System.out.println(rb.getString 507 (".jarsigner.verify.options.jar.file.alias.")); 508 System.out.println(); 509 System.out.println(rb.getString 510 (".keystore.url.keystore.location")); 511 System.out.println(); 512 System.out.println(rb.getString 513 (".storepass.password.password.for.keystore.integrity")); 514 System.out.println(); 515 System.out.println(rb.getString 516 (".storetype.type.keystore.type")); 517 System.out.println(); 518 System.out.println(rb.getString 519 (".keypass.password.password.for.private.key.if.different.")); 520 System.out.println(); 521 System.out.println(rb.getString 522 (".certchain.file.name.of.alternative.certchain.file")); 523 System.out.println(); 524 System.out.println(rb.getString 525 (".sigfile.file.name.of.SF.DSA.file")); 526 System.out.println(); 527 System.out.println(rb.getString 528 (".signedjar.file.name.of.signed.JAR.file")); 529 System.out.println(); 530 System.out.println(rb.getString 531 (".digestalg.algorithm.name.of.digest.algorithm")); 532 System.out.println(); 533 System.out.println(rb.getString 534 (".sigalg.algorithm.name.of.signature.algorithm")); 535 System.out.println(); 536 System.out.println(rb.getString 537 (".verify.verify.a.signed.JAR.file")); 538 System.out.println(); 539 System.out.println(rb.getString 540 (".verbose.suboptions.verbose.output.when.signing.verifying.")); 541 System.out.println(rb.getString 542 (".suboptions.can.be.all.grouped.or.summary")); 543 System.out.println(); 544 System.out.println(rb.getString 545 (".certs.display.certificates.when.verbose.and.verifying")); 546 System.out.println(); 547 System.out.println(rb.getString 548 (".tsa.url.location.of.the.Timestamping.Authority")); 549 System.out.println(); 550 System.out.println(rb.getString 551 (".tsacert.alias.public.key.certificate.for.Timestamping.Authority")); 552 System.out.println(); 553 System.out.println(rb.getString 554 (".tsapolicyid.tsapolicyid.for.Timestamping.Authority")); 555 System.out.println(); 556 System.out.println(rb.getString 557 (".tsadigestalg.algorithm.of.digest.data.in.timestamping.request")); 558 System.out.println(); 559 System.out.println(rb.getString 560 (".altsigner.class.class.name.of.an.alternative.signing.mechanism")); 561 System.out.println(); 562 System.out.println(rb.getString 563 (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism")); 564 System.out.println(); 565 System.out.println(rb.getString 566 (".internalsf.include.the.SF.file.inside.the.signature.block")); 567 System.out.println(); 568 System.out.println(rb.getString 569 (".sectionsonly.don.t.compute.hash.of.entire.manifest")); 570 System.out.println(); 571 System.out.println(rb.getString 572 (".protected.keystore.has.protected.authentication.path")); 573 System.out.println(); 574 System.out.println(rb.getString 575 (".providerName.name.provider.name")); 576 System.out.println(); 577 System.out.println(rb.getString 578 (".providerClass.class.name.of.cryptographic.service.provider.s")); 579 System.out.println(rb.getString 580 (".providerArg.arg.master.class.file.and.constructor.argument")); 581 System.out.println(); 582 System.out.println(rb.getString 583 (".strict.treat.warnings.as.errors")); 584 System.out.println(); 585 586 System.exit(0); 587 } 588 589 void verifyJar(String jarName) 590 throws Exception 591 { 592 boolean anySigned = false; // if there exists entry inside jar signed 593 JarFile jf = null; 594 Map<String,String> digestMap = new HashMap<>(); 595 Map<String,PKCS7> sigMap = new HashMap<>(); 596 Map<String,String> sigNameMap = new HashMap<>(); 597 Map<String,String> unparsableSignatures = new HashMap<>(); 598 599 try { 600 jf = new JarFile(jarName, true); 601 Vector<JarEntry> entriesVec = new Vector<>(); 602 byte[] buffer = new byte[8192]; 603 604 Enumeration<JarEntry> entries = jf.entries(); 605 while (entries.hasMoreElements()) { 606 JarEntry je = entries.nextElement(); 607 entriesVec.addElement(je); 608 try (InputStream is = jf.getInputStream(je)) { 609 String name = je.getName(); 610 if (signatureRelated(name) 611 && SignatureFileVerifier.isBlockOrSF(name)) { 612 String alias = name.substring(name.lastIndexOf('/') + 1, 613 name.lastIndexOf('.')); 614 try { 615 if (name.endsWith(".SF")) { 616 Manifest sf = new Manifest(is); 617 boolean found = false; 618 for (Object obj : sf.getMainAttributes().keySet()) { 619 String key = obj.toString(); 620 if (key.endsWith("-Digest-Manifest")) { 621 digestMap.put(alias, 622 key.substring(0, key.length() - 16)); 623 found = true; 624 break; 625 } 626 } 627 if (!found) { 628 unparsableSignatures.putIfAbsent(alias, 629 String.format( 630 rb.getString("history.unparsable"), 631 name)); 632 } 633 } else { 634 sigNameMap.put(alias, name); 635 sigMap.put(alias, new PKCS7(is)); 636 } 637 } catch (IOException ioe) { 638 unparsableSignatures.putIfAbsent(alias, String.format( 639 rb.getString("history.unparsable"), name)); 640 } 641 } else { 642 while (is.read(buffer, 0, buffer.length) != -1) { 643 // we just read. this will throw a SecurityException 644 // if a signature/digest check fails. 645 } 646 } 647 } 648 } 649 650 Manifest man = jf.getManifest(); 651 boolean hasSignature = false; 652 653 // The map to record display info, only used when -verbose provided 654 // key: signer info string 655 // value: the list of files with common key 656 Map<String,List<String>> output = new LinkedHashMap<>(); 657 658 if (man != null) { 659 if (verbose != null) System.out.println(); 660 Enumeration<JarEntry> e = entriesVec.elements(); 661 662 String tab = rb.getString("6SPACE"); 663 664 while (e.hasMoreElements()) { 665 JarEntry je = e.nextElement(); 666 String name = je.getName(); 667 668 hasSignature = hasSignature 669 || SignatureFileVerifier.isBlockOrSF(name); 670 671 CodeSigner[] signers = je.getCodeSigners(); 672 boolean isSigned = (signers != null); 673 anySigned |= isSigned; 674 hasUnsignedEntry |= !je.isDirectory() && !isSigned 675 && !signatureRelated(name); 676 677 int inStoreOrScope = inKeyStore(signers); 678 679 boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0; 680 boolean inScope = (inStoreOrScope & IN_SCOPE) != 0; 681 682 notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0; 683 if (keystore != null) { 684 aliasNotInStore |= isSigned && (!inStore && !inScope); 685 } 686 687 // Only used when -verbose provided 688 StringBuffer sb = null; 689 if (verbose != null) { 690 sb = new StringBuffer(); 691 boolean inManifest = 692 ((man.getAttributes(name) != null) || 693 (man.getAttributes("./"+name) != null) || 694 (man.getAttributes("/"+name) != null)); 695 sb.append( 696 (isSigned ? rb.getString("s") : rb.getString("SPACE")) + 697 (inManifest ? rb.getString("m") : rb.getString("SPACE")) + 698 (inStore ? rb.getString("k") : rb.getString("SPACE")) + 699 (inScope ? rb.getString("i") : rb.getString("SPACE")) + 700 ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") + 701 rb.getString("SPACE")); 702 sb.append("|"); 703 } 704 705 // When -certs provided, display info has extra empty 706 // lines at the beginning and end. 707 if (isSigned) { 708 if (showcerts) sb.append('\n'); 709 for (CodeSigner signer: signers) { 710 // signerInfo() must be called even if -verbose 711 // not provided. The method updates various 712 // warning flags. 713 String si = signerInfo(signer, tab); 714 if (showcerts) { 715 sb.append(si); 716 sb.append('\n'); 717 } 718 } 719 } else if (showcerts && !verbose.equals("all")) { 720 // Print no info for unsigned entries when -verbose:all, 721 // to be consistent with old behavior. 722 if (signatureRelated(name)) { 723 sb.append("\n" + tab + rb.getString( 724 ".Signature.related.entries.") + "\n\n"); 725 } else { 726 sb.append("\n" + tab + rb.getString( 727 ".Unsigned.entries.") + "\n\n"); 728 } 729 } 730 731 if (verbose != null) { 732 String label = sb.toString(); 733 if (signatureRelated(name)) { 734 // Entries inside META-INF and other unsigned 735 // entries are grouped separately. 736 label = "-" + label; 737 } 738 739 // The label finally contains 2 parts separated by '|': 740 // The legend displayed before the entry names, and 741 // the cert info (if -certs specified). 742 743 if (!output.containsKey(label)) { 744 output.put(label, new ArrayList<String>()); 745 } 746 747 StringBuffer fb = new StringBuffer(); 748 String s = Long.toString(je.getSize()); 749 for (int i = 6 - s.length(); i > 0; --i) { 750 fb.append(' '); 751 } 752 fb.append(s).append(' '). 753 append(new Date(je.getTime()).toString()); 754 fb.append(' ').append(name); 755 756 output.get(label).add(fb.toString()); 757 } 758 } 759 } 760 if (verbose != null) { 761 for (Entry<String,List<String>> s: output.entrySet()) { 762 List<String> files = s.getValue(); 763 String key = s.getKey(); 764 if (key.charAt(0) == '-') { // the signature-related group 765 key = key.substring(1); 766 } 767 int pipe = key.indexOf('|'); 768 if (verbose.equals("all")) { 769 for (String f: files) { 770 System.out.println(key.substring(0, pipe) + f); 771 System.out.printf(key.substring(pipe+1)); 772 } 773 } else { 774 if (verbose.equals("grouped")) { 775 for (String f: files) { 776 System.out.println(key.substring(0, pipe) + f); 777 } 778 } else if (verbose.equals("summary")) { 779 System.out.print(key.substring(0, pipe)); 780 if (files.size() > 1) { 781 System.out.println(files.get(0) + " " + 782 String.format(rb.getString( 783 ".and.d.more."), files.size()-1)); 784 } else { 785 System.out.println(files.get(0)); 786 } 787 } 788 System.out.printf(key.substring(pipe+1)); 789 } 790 } 791 System.out.println(); 792 System.out.println(rb.getString( 793 ".s.signature.was.verified.")); 794 System.out.println(rb.getString( 795 ".m.entry.is.listed.in.manifest")); 796 System.out.println(rb.getString( 797 ".k.at.least.one.certificate.was.found.in.keystore")); 798 System.out.println(rb.getString( 799 ".i.at.least.one.certificate.was.found.in.identity.scope")); 800 if (ckaliases.size() > 0) { 801 System.out.println(rb.getString( 802 ".X.not.signed.by.specified.alias.es.")); 803 } 804 } 805 if (man == null) { 806 System.out.println(); 807 System.out.println(rb.getString("no.manifest.")); 808 } 809 810 // Even if the verbose option is not specified, all out strings 811 // must be generated so seeWeak can be updated. 812 if (!digestMap.isEmpty() 813 || !sigMap.isEmpty() 814 || !unparsableSignatures.isEmpty()) { 815 if (verbose != null) { 816 System.out.println(); 817 } 818 for (String s : sigMap.keySet()) { 819 if (!digestMap.containsKey(s)) { 820 unparsableSignatures.putIfAbsent(s, String.format( 821 rb.getString("history.nosf"), s)); 822 } 823 } 824 for (String s : digestMap.keySet()) { 825 PKCS7 p7 = sigMap.get(s); 826 if (p7 != null) { 827 String history; 828 try { 829 SignerInfo si = p7.getSignerInfos()[0]; 830 X509Certificate signer = si.getCertificate(p7); 831 String digestAlg = digestMap.get(s); 832 String sigAlg = AlgorithmId.makeSigAlg( 833 si.getDigestAlgorithmId().getName(), 834 si.getDigestEncryptionAlgorithmId().getName()); 835 PublicKey key = signer.getPublicKey(); 836 PKCS7 tsToken = si.getTsToken(); 837 if (tsToken != null) { 838 SignerInfo tsSi = tsToken.getSignerInfos()[0]; 839 X509Certificate tsSigner = tsSi.getCertificate(tsToken); 840 byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); 841 TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); 842 PublicKey tsKey = tsSigner.getPublicKey(); 843 String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName(); 844 String tsSigAlg = AlgorithmId.makeSigAlg( 845 tsSi.getDigestAlgorithmId().getName(), 846 tsSi.getDigestEncryptionAlgorithmId().getName()); 847 Calendar c = Calendar.getInstance( 848 TimeZone.getTimeZone("UTC"), 849 Locale.getDefault(Locale.Category.FORMAT)); 850 c.setTime(tsTokenInfo.getDate()); 851 history = String.format( 852 rb.getString("history.with.ts"), 853 signer.getSubjectX500Principal(), 854 withWeak(digestAlg, DIGEST_PRIMITIVE_SET), 855 withWeak(sigAlg, SIG_PRIMITIVE_SET), 856 withWeak(key), 857 c, 858 tsSigner.getSubjectX500Principal(), 859 withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET), 860 withWeak(tsSigAlg, SIG_PRIMITIVE_SET), 861 withWeak(tsKey)); 862 } else { 863 history = String.format( 864 rb.getString("history.without.ts"), 865 signer.getSubjectX500Principal(), 866 withWeak(digestAlg, DIGEST_PRIMITIVE_SET), 867 withWeak(sigAlg, SIG_PRIMITIVE_SET), 868 withWeak(key)); 869 } 870 } catch (Exception e) { 871 // The only usage of sigNameMap, remember the name 872 // of the block file if it's invalid. 873 history = String.format( 874 rb.getString("history.unparsable"), 875 sigNameMap.get(s)); 876 } 877 if (verbose != null) { 878 System.out.println(history); 879 } 880 } else { 881 unparsableSignatures.putIfAbsent(s, String.format( 882 rb.getString("history.nobk"), s)); 883 } 884 } 885 if (verbose != null) { 886 for (String s : unparsableSignatures.keySet()) { 887 System.out.println(unparsableSignatures.get(s)); 888 } 889 } 890 } 891 System.out.println(); 892 893 if (!anySigned) { 894 if (seeWeak) { 895 if (verbose != null) { 896 System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose")); 897 System.out.println("\n " + 898 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS + 899 "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS)); 900 } else { 901 System.out.println(rb.getString("jar.treated.unsigned.see.weak")); 902 } 903 } else if (hasSignature) { 904 System.out.println(rb.getString("jar.treated.unsigned")); 905 } else { 906 System.out.println(rb.getString("jar.is.unsigned")); 907 } 908 } else { 909 boolean warningAppeared = false; 910 boolean errorAppeared = false; 911 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || 912 notYetValidCert || chainNotValidated || hasExpiredCert || 913 hasUnsignedEntry || 914 aliasNotInStore || notSignedByAlias) { 915 916 if (strict) { 917 System.out.println(rb.getString("jar.verified.with.signer.errors.")); 918 System.out.println(); 919 System.out.println(rb.getString("Error.")); 920 errorAppeared = true; 921 } else { 922 System.out.println(rb.getString("jar.verified.")); 923 System.out.println(); 924 System.out.println(rb.getString("Warning.")); 925 warningAppeared = true; 926 } 927 928 if (badKeyUsage) { 929 System.out.println( 930 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); 931 } 932 933 if (badExtendedKeyUsage) { 934 System.out.println( 935 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); 936 } 937 938 if (badNetscapeCertType) { 939 System.out.println( 940 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); 941 } 942 943 if (hasUnsignedEntry) { 944 System.out.println(rb.getString( 945 "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); 946 } 947 if (hasExpiredCert) { 948 System.out.println(rb.getString( 949 "This.jar.contains.entries.whose.signer.certificate.has.expired.")); 950 } 951 if (notYetValidCert) { 952 System.out.println(rb.getString( 953 "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); 954 } 955 956 if (chainNotValidated) { 957 System.out.println( 958 rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated.")); 959 } 960 961 if (notSignedByAlias) { 962 System.out.println( 963 rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); 964 } 965 966 if (aliasNotInStore) { 967 System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); 968 } 969 } else { 970 System.out.println(rb.getString("jar.verified.")); 971 } 972 if (hasExpiringCert || noTimestamp) { 973 if (!warningAppeared) { 974 System.out.println(); 975 System.out.println(rb.getString("Warning.")); 976 warningAppeared = true; 977 } 978 if (hasExpiringCert) { 979 System.out.println(rb.getString( 980 "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); 981 } 982 if (noTimestamp) { 983 System.out.println( 984 String.format(rb.getString("no.timestamp.verifying"), expireDate)); 985 } 986 } 987 if (warningAppeared || errorAppeared) { 988 if (! (verbose != null && showcerts)) { 989 System.out.println(); 990 System.out.println(rb.getString( 991 "Re.run.with.the.verbose.and.certs.options.for.more.details.")); 992 } 993 } 994 } 995 return; 996 } catch (Exception e) { 997 System.out.println(rb.getString("jarsigner.") + e); 998 if (debug) { 999 e.printStackTrace(); 1000 } 1001 } finally { // close the resource 1002 if (jf != null) { 1003 jf.close(); 1004 } 1005 } 1006 1007 System.exit(1); 1008 } 1009 1010 private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) { 1011 if (DISABLED_CHECK.permits(primitiveSet, alg, null)) { 1012 return alg; 1013 } else { 1014 seeWeak = true; 1015 return String.format(rb.getString("with.weak"), alg); 1016 } 1017 } 1018 1019 private String withWeak(PublicKey key) { 1020 if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 1021 return String.format( 1022 rb.getString("key.bit"), KeyUtil.getKeySize(key)); 1023 } else { 1024 seeWeak = true; 1025 return String.format( 1026 rb.getString("key.bit.weak"), KeyUtil.getKeySize(key)); 1027 } 1028 } 1029 1030 private static MessageFormat validityTimeForm = null; 1031 private static MessageFormat notYetTimeForm = null; 1032 private static MessageFormat expiredTimeForm = null; 1033 private static MessageFormat expiringTimeForm = null; 1034 1035 /* 1036 * Display some details about a certificate: 1037 * 1038 * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"] 1039 * [<validity-period> | <expiry-warning>] 1040 * 1041 * Note: no newline character at the end 1042 */ 1043 String printCert(String tab, Certificate c, boolean checkValidityPeriod, 1044 Date timestamp, boolean checkUsage) { 1045 1046 StringBuilder certStr = new StringBuilder(); 1047 String space = rb.getString("SPACE"); 1048 X509Certificate x509Cert = null; 1049 1050 if (c instanceof X509Certificate) { 1051 x509Cert = (X509Certificate) c; 1052 certStr.append(tab).append(x509Cert.getType()) 1053 .append(rb.getString("COMMA")) 1054 .append(x509Cert.getSubjectDN().getName()); 1055 } else { 1056 certStr.append(tab).append(c.getType()); 1057 } 1058 1059 String alias = storeHash.get(c); 1060 if (alias != null) { 1061 certStr.append(space).append(alias); 1062 } 1063 1064 if (checkValidityPeriod && x509Cert != null) { 1065 1066 certStr.append("\n").append(tab).append("["); 1067 Date notAfter = x509Cert.getNotAfter(); 1068 try { 1069 boolean printValidity = true; 1070 if (timestamp == null) { 1071 if (expireDate.getTime() == 0 || expireDate.after(notAfter)) { 1072 expireDate = notAfter; 1073 } 1074 x509Cert.checkValidity(); 1075 // test if cert will expire within six months 1076 if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) { 1077 hasExpiringCert = true; 1078 if (expiringTimeForm == null) { 1079 expiringTimeForm = new MessageFormat( 1080 rb.getString("certificate.will.expire.on")); 1081 } 1082 Object[] source = { notAfter }; 1083 certStr.append(expiringTimeForm.format(source)); 1084 printValidity = false; 1085 } 1086 } else { 1087 x509Cert.checkValidity(timestamp); 1088 } 1089 if (printValidity) { 1090 if (validityTimeForm == null) { 1091 validityTimeForm = new MessageFormat( 1092 rb.getString("certificate.is.valid.from")); 1093 } 1094 Object[] source = { x509Cert.getNotBefore(), notAfter }; 1095 certStr.append(validityTimeForm.format(source)); 1096 } 1097 } catch (CertificateExpiredException cee) { 1098 hasExpiredCert = true; 1099 1100 if (expiredTimeForm == null) { 1101 expiredTimeForm = new MessageFormat( 1102 rb.getString("certificate.expired.on")); 1103 } 1104 Object[] source = { notAfter }; 1105 certStr.append(expiredTimeForm.format(source)); 1106 1107 } catch (CertificateNotYetValidException cnyve) { 1108 notYetValidCert = true; 1109 1110 if (notYetTimeForm == null) { 1111 notYetTimeForm = new MessageFormat( 1112 rb.getString("certificate.is.not.valid.until")); 1113 } 1114 Object[] source = { x509Cert.getNotBefore() }; 1115 certStr.append(notYetTimeForm.format(source)); 1116 } 1117 certStr.append("]"); 1118 1119 if (checkUsage) { 1120 boolean[] bad = new boolean[3]; 1121 checkCertUsage(x509Cert, bad); 1122 if (bad[0] || bad[1] || bad[2]) { 1123 String x = ""; 1124 if (bad[0]) { 1125 x ="KeyUsage"; 1126 } 1127 if (bad[1]) { 1128 if (x.length() > 0) x = x + ", "; 1129 x = x + "ExtendedKeyUsage"; 1130 } 1131 if (bad[2]) { 1132 if (x.length() > 0) x = x + ", "; 1133 x = x + "NetscapeCertType"; 1134 } 1135 certStr.append("\n").append(tab) 1136 .append(MessageFormat.format(rb.getString( 1137 ".{0}.extension.does.not.support.code.signing."), x)); 1138 } 1139 } 1140 } 1141 return certStr.toString(); 1142 } 1143 1144 private static MessageFormat signTimeForm = null; 1145 1146 private String printTimestamp(String tab, Timestamp timestamp) { 1147 1148 if (signTimeForm == null) { 1149 signTimeForm = 1150 new MessageFormat(rb.getString("entry.was.signed.on")); 1151 } 1152 Object[] source = { timestamp.getTimestamp() }; 1153 1154 return new StringBuilder().append(tab).append("[") 1155 .append(signTimeForm.format(source)).append("]").toString(); 1156 } 1157 1158 private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>(); 1159 1160 private int inKeyStoreForOneSigner(CodeSigner signer) { 1161 if (cacheForInKS.containsKey(signer)) { 1162 return cacheForInKS.get(signer); 1163 } 1164 1165 boolean found = false; 1166 int result = 0; 1167 List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); 1168 for (Certificate c : certs) { 1169 String alias = storeHash.get(c); 1170 if (alias != null) { 1171 if (alias.startsWith("(")) { 1172 result |= IN_KEYSTORE; 1173 } else if (alias.startsWith("[")) { 1174 result |= IN_SCOPE; 1175 } 1176 if (ckaliases.contains(alias.substring(1, alias.length() - 1))) { 1177 result |= SIGNED_BY_ALIAS; 1178 } 1179 } else { 1180 if (store != null) { 1181 try { 1182 alias = store.getCertificateAlias(c); 1183 } catch (KeyStoreException kse) { 1184 // never happens, because keystore has been loaded 1185 } 1186 if (alias != null) { 1187 storeHash.put(c, "(" + alias + ")"); 1188 found = true; 1189 result |= IN_KEYSTORE; 1190 } 1191 } 1192 if (ckaliases.contains(alias)) { 1193 result |= SIGNED_BY_ALIAS; 1194 } 1195 } 1196 } 1197 cacheForInKS.put(signer, result); 1198 return result; 1199 } 1200 1201 Hashtable<Certificate, String> storeHash = new Hashtable<>(); 1202 1203 int inKeyStore(CodeSigner[] signers) { 1204 1205 if (signers == null) 1206 return 0; 1207 1208 int output = 0; 1209 1210 for (CodeSigner signer: signers) { 1211 int result = inKeyStoreForOneSigner(signer); 1212 output |= result; 1213 } 1214 if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) { 1215 output |= NOT_ALIAS; 1216 } 1217 return output; 1218 } 1219 1220 void signJar(String jarName, String alias, String[] args) 1221 throws Exception { 1222 boolean aliasUsed = false; 1223 X509Certificate tsaCert = null; 1224 1225 if (sigfile == null) { 1226 sigfile = alias; 1227 aliasUsed = true; 1228 } 1229 1230 if (sigfile.length() > 8) { 1231 sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH); 1232 } else { 1233 sigfile = sigfile.toUpperCase(Locale.ENGLISH); 1234 } 1235 1236 StringBuilder tmpSigFile = new StringBuilder(sigfile.length()); 1237 for (int j = 0; j < sigfile.length(); j++) { 1238 char c = sigfile.charAt(j); 1239 if (! 1240 ((c>= 'A' && c<= 'Z') || 1241 (c>= '0' && c<= '9') || 1242 (c == '-') || 1243 (c == '_'))) { 1244 if (aliasUsed) { 1245 // convert illegal characters from the alias to be _'s 1246 c = '_'; 1247 } else { 1248 throw new 1249 RuntimeException(rb.getString 1250 ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.")); 1251 } 1252 } 1253 tmpSigFile.append(c); 1254 } 1255 1256 sigfile = tmpSigFile.toString(); 1257 1258 String tmpJarName; 1259 if (signedjar == null) tmpJarName = jarName+".sig"; 1260 else tmpJarName = signedjar; 1261 1262 File jarFile = new File(jarName); 1263 File signedJarFile = new File(tmpJarName); 1264 1265 // Open the jar (zip) file 1266 try { 1267 zipFile = new ZipFile(jarName); 1268 } catch (IOException ioe) { 1269 error(rb.getString("unable.to.open.jar.file.")+jarName, ioe); 1270 } 1271 1272 FileOutputStream fos = null; 1273 try { 1274 fos = new FileOutputStream(signedJarFile); 1275 } catch (IOException ioe) { 1276 error(rb.getString("unable.to.create.")+tmpJarName, ioe); 1277 } 1278 1279 PrintStream ps = new PrintStream(fos); 1280 ZipOutputStream zos = new ZipOutputStream(ps); 1281 1282 /* First guess at what they might be - we don't xclude RSA ones. */ 1283 String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH); 1284 String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH); 1285 1286 Manifest manifest = new Manifest(); 1287 Map<String,Attributes> mfEntries = manifest.getEntries(); 1288 1289 // The Attributes of manifest before updating 1290 Attributes oldAttr = null; 1291 1292 boolean mfModified = false; 1293 boolean mfCreated = false; 1294 byte[] mfRawBytes = null; 1295 1296 try { 1297 MessageDigest digests[] = { MessageDigest.getInstance(digestalg) }; 1298 1299 // Check if manifest exists 1300 ZipEntry mfFile; 1301 if ((mfFile = getManifestFile(zipFile)) != null) { 1302 // Manifest exists. Read its raw bytes. 1303 mfRawBytes = getBytes(zipFile, mfFile); 1304 manifest.read(new ByteArrayInputStream(mfRawBytes)); 1305 oldAttr = (Attributes)(manifest.getMainAttributes().clone()); 1306 } else { 1307 // Create new manifest 1308 Attributes mattr = manifest.getMainAttributes(); 1309 mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(), 1310 "1.0"); 1311 String javaVendor = System.getProperty("java.vendor"); 1312 String jdkVersion = System.getProperty("java.version"); 1313 mattr.putValue("Created-By", jdkVersion + " (" +javaVendor 1314 + ")"); 1315 mfFile = new ZipEntry(JarFile.MANIFEST_NAME); 1316 mfCreated = true; 1317 } 1318 1319 /* 1320 * For each entry in jar 1321 * (except for signature-related META-INF entries), 1322 * do the following: 1323 * 1324 * - if entry is not contained in manifest, add it to manifest; 1325 * - if entry is contained in manifest, calculate its hash and 1326 * compare it with the one in the manifest; if they are 1327 * different, replace the hash in the manifest with the newly 1328 * generated one. (This may invalidate existing signatures!) 1329 */ 1330 Vector<ZipEntry> mfFiles = new Vector<>(); 1331 1332 boolean wasSigned = false; 1333 1334 for (Enumeration<? extends ZipEntry> enum_=zipFile.entries(); 1335 enum_.hasMoreElements();) { 1336 ZipEntry ze = enum_.nextElement(); 1337 1338 if (ze.getName().startsWith(META_INF)) { 1339 // Store META-INF files in vector, so they can be written 1340 // out first 1341 mfFiles.addElement(ze); 1342 1343 if (SignatureFileVerifier.isBlockOrSF( 1344 ze.getName().toUpperCase(Locale.ENGLISH))) { 1345 wasSigned = true; 1346 } 1347 1348 if (signatureRelated(ze.getName())) { 1349 // ignore signature-related and manifest files 1350 continue; 1351 } 1352 } 1353 1354 if (manifest.getAttributes(ze.getName()) != null) { 1355 // jar entry is contained in manifest, check and 1356 // possibly update its digest attributes 1357 if (updateDigests(ze, zipFile, digests, 1358 manifest) == true) { 1359 mfModified = true; 1360 } 1361 } else if (!ze.isDirectory()) { 1362 // Add entry to manifest 1363 Attributes attrs = getDigestAttributes(ze, zipFile, 1364 digests); 1365 mfEntries.put(ze.getName(), attrs); 1366 mfModified = true; 1367 } 1368 } 1369 1370 // Recalculate the manifest raw bytes if necessary 1371 if (mfModified) { 1372 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1373 manifest.write(baos); 1374 if (wasSigned) { 1375 byte[] newBytes = baos.toByteArray(); 1376 if (mfRawBytes != null 1377 && oldAttr.equals(manifest.getMainAttributes())) { 1378 1379 /* 1380 * Note: 1381 * 1382 * The Attributes object is based on HashMap and can handle 1383 * continuation columns. Therefore, even if the contents are 1384 * not changed (in a Map view), the bytes that it write() 1385 * may be different from the original bytes that it read() 1386 * from. Since the signature on the main attributes is based 1387 * on raw bytes, we must retain the exact bytes. 1388 */ 1389 1390 int newPos = findHeaderEnd(newBytes); 1391 int oldPos = findHeaderEnd(mfRawBytes); 1392 1393 if (newPos == oldPos) { 1394 System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos); 1395 } else { 1396 // cat oldHead newTail > newBytes 1397 byte[] lastBytes = new byte[oldPos + 1398 newBytes.length - newPos]; 1399 System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos); 1400 System.arraycopy(newBytes, newPos, lastBytes, oldPos, 1401 newBytes.length - newPos); 1402 newBytes = lastBytes; 1403 } 1404 } 1405 mfRawBytes = newBytes; 1406 } else { 1407 mfRawBytes = baos.toByteArray(); 1408 } 1409 } 1410 1411 // Write out the manifest 1412 if (mfModified) { 1413 // manifest file has new length 1414 mfFile = new ZipEntry(JarFile.MANIFEST_NAME); 1415 } 1416 if (verbose != null) { 1417 if (mfCreated) { 1418 System.out.println(rb.getString(".adding.") + 1419 mfFile.getName()); 1420 } else if (mfModified) { 1421 System.out.println(rb.getString(".updating.") + 1422 mfFile.getName()); 1423 } 1424 } 1425 zos.putNextEntry(mfFile); 1426 zos.write(mfRawBytes); 1427 1428 // Calculate SignatureFile (".SF") and SignatureBlockFile 1429 ManifestDigester manDig = new ManifestDigester(mfRawBytes); 1430 SignatureFile sf = new SignatureFile(digests, manifest, manDig, 1431 sigfile, signManifest); 1432 1433 if (tsaAlias != null) { 1434 tsaCert = getTsaCert(tsaAlias); 1435 } 1436 1437 if (tsaUrl == null && tsaCert == null) { 1438 noTimestamp = true; 1439 } 1440 1441 SignatureFile.Block block = null; 1442 1443 try { 1444 block = 1445 sf.generateBlock(privateKey, sigalg, certChain, 1446 externalSF, tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg, 1447 signingMechanism, args, zipFile); 1448 } catch (SocketTimeoutException e) { 1449 // Provide a helpful message when TSA is beyond a firewall 1450 error(rb.getString("unable.to.sign.jar.") + 1451 rb.getString("no.response.from.the.Timestamping.Authority.") + 1452 "\n -J-Dhttp.proxyHost=<hostname>" + 1453 "\n -J-Dhttp.proxyPort=<portnumber>\n" + 1454 rb.getString("or") + 1455 "\n -J-Dhttps.proxyHost=<hostname> " + 1456 "\n -J-Dhttps.proxyPort=<portnumber> ", e); 1457 } 1458 1459 sfFilename = sf.getMetaName(); 1460 bkFilename = block.getMetaName(); 1461 1462 ZipEntry sfFile = new ZipEntry(sfFilename); 1463 ZipEntry bkFile = new ZipEntry(bkFilename); 1464 1465 long time = System.currentTimeMillis(); 1466 sfFile.setTime(time); 1467 bkFile.setTime(time); 1468 1469 // signature file 1470 zos.putNextEntry(sfFile); 1471 sf.write(zos); 1472 if (verbose != null) { 1473 if (zipFile.getEntry(sfFilename) != null) { 1474 System.out.println(rb.getString(".updating.") + 1475 sfFilename); 1476 } else { 1477 System.out.println(rb.getString(".adding.") + 1478 sfFilename); 1479 } 1480 } 1481 1482 if (verbose != null) { 1483 if (tsaUrl != null || tsaCert != null) { 1484 System.out.println( 1485 rb.getString("requesting.a.signature.timestamp")); 1486 } 1487 if (tsaUrl != null) { 1488 System.out.println(rb.getString("TSA.location.") + tsaUrl); 1489 } 1490 if (tsaCert != null) { 1491 URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert); 1492 if (tsaURI != null) { 1493 System.out.println(rb.getString("TSA.location.") + 1494 tsaURI); 1495 } 1496 System.out.println(rb.getString("TSA.certificate.") + 1497 printCert("", tsaCert, false, null, false)); 1498 } 1499 if (signingMechanism != null) { 1500 System.out.println( 1501 rb.getString("using.an.alternative.signing.mechanism")); 1502 } 1503 } 1504 1505 // signature block file 1506 zos.putNextEntry(bkFile); 1507 block.write(zos); 1508 if (verbose != null) { 1509 if (zipFile.getEntry(bkFilename) != null) { 1510 System.out.println(rb.getString(".updating.") + 1511 bkFilename); 1512 } else { 1513 System.out.println(rb.getString(".adding.") + 1514 bkFilename); 1515 } 1516 } 1517 1518 // Write out all other META-INF files that we stored in the 1519 // vector 1520 for (int i=0; i<mfFiles.size(); i++) { 1521 ZipEntry ze = mfFiles.elementAt(i); 1522 if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME) 1523 && !ze.getName().equalsIgnoreCase(sfFilename) 1524 && !ze.getName().equalsIgnoreCase(bkFilename)) { 1525 writeEntry(zipFile, zos, ze); 1526 } 1527 } 1528 1529 // Write out all other files 1530 for (Enumeration<? extends ZipEntry> enum_=zipFile.entries(); 1531 enum_.hasMoreElements();) { 1532 ZipEntry ze = enum_.nextElement(); 1533 1534 if (!ze.getName().startsWith(META_INF)) { 1535 if (verbose != null) { 1536 if (manifest.getAttributes(ze.getName()) != null) 1537 System.out.println(rb.getString(".signing.") + 1538 ze.getName()); 1539 else 1540 System.out.println(rb.getString(".adding.") + 1541 ze.getName()); 1542 } 1543 writeEntry(zipFile, zos, ze); 1544 } 1545 } 1546 } catch(IOException ioe) { 1547 error(rb.getString("unable.to.sign.jar.")+ioe, ioe); 1548 } finally { 1549 // close the resouces 1550 if (zipFile != null) { 1551 zipFile.close(); 1552 zipFile = null; 1553 } 1554 1555 if (zos != null) { 1556 zos.close(); 1557 } 1558 } 1559 1560 // no IOException thrown in the follow try clause, so disable 1561 // the try clause. 1562 // try { 1563 if (signedjar == null) { 1564 // attempt an atomic rename. If that fails, 1565 // rename the original jar file, then the signed 1566 // one, then delete the original. 1567 if (!signedJarFile.renameTo(jarFile)) { 1568 File origJar = new File(jarName+".orig"); 1569 1570 if (jarFile.renameTo(origJar)) { 1571 if (signedJarFile.renameTo(jarFile)) { 1572 origJar.delete(); 1573 } else { 1574 MessageFormat form = new MessageFormat(rb.getString 1575 ("attempt.to.rename.signedJarFile.to.jarFile.failed")); 1576 Object[] source = {signedJarFile, jarFile}; 1577 error(form.format(source)); 1578 } 1579 } else { 1580 MessageFormat form = new MessageFormat(rb.getString 1581 ("attempt.to.rename.jarFile.to.origJar.failed")); 1582 Object[] source = {jarFile, origJar}; 1583 error(form.format(source)); 1584 } 1585 } 1586 } 1587 1588 boolean warningAppeared = false; 1589 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || 1590 notYetValidCert || chainNotValidated || hasExpiredCert) { 1591 if (strict) { 1592 System.out.println(rb.getString("jar.signed.with.signer.errors.")); 1593 System.out.println(); 1594 System.out.println(rb.getString("Error.")); 1595 } else { 1596 System.out.println(rb.getString("jar.signed.")); 1597 System.out.println(); 1598 System.out.println(rb.getString("Warning.")); 1599 warningAppeared = true; 1600 } 1601 1602 if (badKeyUsage) { 1603 System.out.println( 1604 rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); 1605 } 1606 1607 if (badExtendedKeyUsage) { 1608 System.out.println( 1609 rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); 1610 } 1611 1612 if (badNetscapeCertType) { 1613 System.out.println( 1614 rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); 1615 } 1616 1617 if (hasExpiredCert) { 1618 System.out.println( 1619 rb.getString("The.signer.certificate.has.expired.")); 1620 } else if (notYetValidCert) { 1621 System.out.println( 1622 rb.getString("The.signer.certificate.is.not.yet.valid.")); 1623 } 1624 1625 if (chainNotValidated) { 1626 System.out.println( 1627 rb.getString("The.signer.s.certificate.chain.is.not.validated.")); 1628 } 1629 } else { 1630 System.out.println(rb.getString("jar.signed.")); 1631 } 1632 if (hasExpiringCert || noTimestamp) { 1633 if (!warningAppeared) { 1634 System.out.println(); 1635 System.out.println(rb.getString("Warning.")); 1636 } 1637 1638 if (hasExpiringCert) { 1639 System.out.println( 1640 rb.getString("The.signer.certificate.will.expire.within.six.months.")); 1641 } 1642 1643 if (noTimestamp) { 1644 System.out.println( 1645 String.format(rb.getString("no.timestamp.signing"), expireDate)); 1646 } 1647 } 1648 1649 // no IOException thrown in the above try clause, so disable 1650 // the catch clause. 1651 // } catch(IOException ioe) { 1652 // error(rb.getString("unable.to.sign.jar.")+ioe, ioe); 1653 // } 1654 } 1655 1656 /** 1657 * Find the length of header inside bs. The header is a multiple (>=0) 1658 * lines of attributes plus an empty line. The empty line is included 1659 * in the header. 1660 */ 1661 @SuppressWarnings("fallthrough") 1662 private int findHeaderEnd(byte[] bs) { 1663 // Initial state true to deal with empty header 1664 boolean newline = true; // just met a newline 1665 int len = bs.length; 1666 for (int i=0; i<len; i++) { 1667 switch (bs[i]) { 1668 case '\r': 1669 if (i < len - 1 && bs[i+1] == '\n') i++; 1670 // fallthrough 1671 case '\n': 1672 if (newline) return i+1; //+1 to get length 1673 newline = true; 1674 break; 1675 default: 1676 newline = false; 1677 } 1678 } 1679 // If header end is not found, it means the MANIFEST.MF has only 1680 // the main attributes section and it does not end with 2 newlines. 1681 // Returns the whole length so that it can be completely replaced. 1682 return len; 1683 } 1684 1685 /** 1686 * signature-related files include: 1687 * . META-INF/MANIFEST.MF 1688 * . META-INF/SIG-* 1689 * . META-INF/*.SF 1690 * . META-INF/*.DSA 1691 * . META-INF/*.RSA 1692 * . META-INF/*.EC 1693 */ 1694 private boolean signatureRelated(String name) { 1695 return SignatureFileVerifier.isSigningRelated(name); 1696 } 1697 1698 Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>(); 1699 1700 /** 1701 * Returns a string of singer info, with a newline at the end 1702 */ 1703 private String signerInfo(CodeSigner signer, String tab) { 1704 if (cacheForSignerInfo.containsKey(signer)) { 1705 return cacheForSignerInfo.get(signer); 1706 } 1707 StringBuffer s = new StringBuffer(); 1708 List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); 1709 // display the signature timestamp, if present 1710 Date timestamp; 1711 Timestamp ts = signer.getTimestamp(); 1712 if (ts != null) { 1713 s.append(printTimestamp(tab, ts)); 1714 s.append('\n'); 1715 timestamp = ts.getTimestamp(); 1716 } else { 1717 timestamp = null; 1718 noTimestamp = true; 1719 } 1720 // display the certificate(s). The first one is end-entity cert and 1721 // its KeyUsage should be checked. 1722 boolean first = true; 1723 for (Certificate c : certs) { 1724 s.append(printCert(tab, c, true, timestamp, first)); 1725 s.append('\n'); 1726 first = false; 1727 } 1728 try { 1729 validateCertChain(certs); 1730 } catch (Exception e) { 1731 if (debug) { 1732 e.printStackTrace(); 1733 } 1734 if (e.getCause() != null && 1735 (e.getCause() instanceof CertificateExpiredException || 1736 e.getCause() instanceof CertificateNotYetValidException)) { 1737 // No more warning, we alreay have hasExpiredCert or notYetValidCert 1738 } else { 1739 chainNotValidated = true; 1740 s.append(tab + rb.getString(".CertPath.not.validated.") + 1741 e.getLocalizedMessage() + "]\n"); // TODO 1742 } 1743 } 1744 String result = s.toString(); 1745 cacheForSignerInfo.put(signer, result); 1746 return result; 1747 } 1748 1749 private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze) 1750 throws IOException 1751 { 1752 ZipEntry ze2 = new ZipEntry(ze.getName()); 1753 ze2.setMethod(ze.getMethod()); 1754 ze2.setTime(ze.getTime()); 1755 ze2.setComment(ze.getComment()); 1756 ze2.setExtra(ze.getExtra()); 1757 if (ze.getMethod() == ZipEntry.STORED) { 1758 ze2.setSize(ze.getSize()); 1759 ze2.setCrc(ze.getCrc()); 1760 } 1761 os.putNextEntry(ze2); 1762 writeBytes(zf, ze, os); 1763 } 1764 1765 /** 1766 * Writes all the bytes for a given entry to the specified output stream. 1767 */ 1768 private synchronized void writeBytes 1769 (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException { 1770 int n; 1771 1772 InputStream is = null; 1773 try { 1774 is = zf.getInputStream(ze); 1775 long left = ze.getSize(); 1776 1777 while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) { 1778 os.write(buffer, 0, n); 1779 left -= n; 1780 } 1781 } finally { 1782 if (is != null) { 1783 is.close(); 1784 } 1785 } 1786 } 1787 1788 void loadKeyStore(String keyStoreName, boolean prompt) { 1789 1790 if (!nullStream && keyStoreName == null) { 1791 keyStoreName = System.getProperty("user.home") + File.separator 1792 + ".keystore"; 1793 } 1794 1795 try { 1796 1797 certificateFactory = CertificateFactory.getInstance("X.509"); 1798 validator = CertPathValidator.getInstance("PKIX"); 1799 Set<TrustAnchor> tas = new HashSet<>(); 1800 try { 1801 KeyStore caks = KeyStoreUtil.getCacertsKeyStore(); 1802 if (caks != null) { 1803 Enumeration<String> aliases = caks.aliases(); 1804 while (aliases.hasMoreElements()) { 1805 String a = aliases.nextElement(); 1806 try { 1807 tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null)); 1808 } catch (Exception e2) { 1809 // ignore, when a SecretkeyEntry does not include a cert 1810 } 1811 } 1812 } 1813 } catch (Exception e) { 1814 // Ignore, if cacerts cannot be loaded 1815 } 1816 1817 if (providerName == null) { 1818 store = KeyStore.getInstance(storetype); 1819 } else { 1820 store = KeyStore.getInstance(storetype, providerName); 1821 } 1822 1823 // Get pass phrase 1824 // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z 1825 // and on NT call ?? 1826 if (token && storepass == null && !protectedPath 1827 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { 1828 storepass = getPass 1829 (rb.getString("Enter.Passphrase.for.keystore.")); 1830 } else if (!token && storepass == null && prompt) { 1831 storepass = getPass 1832 (rb.getString("Enter.Passphrase.for.keystore.")); 1833 } 1834 1835 try { 1836 if (nullStream) { 1837 store.load(null, storepass); 1838 } else { 1839 keyStoreName = keyStoreName.replace(File.separatorChar, '/'); 1840 URL url = null; 1841 try { 1842 url = new URL(keyStoreName); 1843 } catch (java.net.MalformedURLException e) { 1844 // try as file 1845 url = new File(keyStoreName).toURI().toURL(); 1846 } 1847 InputStream is = null; 1848 try { 1849 is = url.openStream(); 1850 store.load(is, storepass); 1851 } finally { 1852 if (is != null) { 1853 is.close(); 1854 } 1855 } 1856 } 1857 Enumeration<String> aliases = store.aliases(); 1858 while (aliases.hasMoreElements()) { 1859 String a = aliases.nextElement(); 1860 try { 1861 X509Certificate c = (X509Certificate)store.getCertificate(a); 1862 // Only add TrustedCertificateEntry and self-signed 1863 // PrivateKeyEntry 1864 if (store.isCertificateEntry(a) || 1865 c.getSubjectDN().equals(c.getIssuerDN())) { 1866 tas.add(new TrustAnchor(c, null)); 1867 } 1868 } catch (Exception e2) { 1869 // ignore, when a SecretkeyEntry does not include a cert 1870 } 1871 } 1872 } finally { 1873 try { 1874 pkixParameters = new PKIXParameters(tas); 1875 pkixParameters.setRevocationEnabled(false); 1876 } catch (InvalidAlgorithmParameterException ex) { 1877 // Only if tas is empty 1878 } 1879 } 1880 } catch (IOException ioe) { 1881 throw new RuntimeException(rb.getString("keystore.load.") + 1882 ioe.getMessage()); 1883 } catch (java.security.cert.CertificateException ce) { 1884 throw new RuntimeException(rb.getString("certificate.exception.") + 1885 ce.getMessage()); 1886 } catch (NoSuchProviderException pe) { 1887 throw new RuntimeException(rb.getString("keystore.load.") + 1888 pe.getMessage()); 1889 } catch (NoSuchAlgorithmException nsae) { 1890 throw new RuntimeException(rb.getString("keystore.load.") + 1891 nsae.getMessage()); 1892 } catch (KeyStoreException kse) { 1893 throw new RuntimeException 1894 (rb.getString("unable.to.instantiate.keystore.class.") + 1895 kse.getMessage()); 1896 } 1897 } 1898 1899 X509Certificate getTsaCert(String alias) { 1900 1901 java.security.cert.Certificate cs = null; 1902 1903 try { 1904 cs = store.getCertificate(alias); 1905 } catch (KeyStoreException kse) { 1906 // this never happens, because keystore has been loaded 1907 } 1908 if (cs == null || (!(cs instanceof X509Certificate))) { 1909 MessageFormat form = new MessageFormat(rb.getString 1910 ("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the")); 1911 Object[] source = {alias, alias}; 1912 error(form.format(source)); 1913 } 1914 return (X509Certificate) cs; 1915 } 1916 1917 /** 1918 * Check if userCert is designed to be a code signer 1919 * @param userCert the certificate to be examined 1920 * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage, 1921 * NetscapeCertType has codeSigning flag turned on. 1922 * If null, the class field badKeyUsage, badExtendedKeyUsage, 1923 * badNetscapeCertType will be set. 1924 */ 1925 void checkCertUsage(X509Certificate userCert, boolean[] bad) { 1926 1927 // Can act as a signer? 1928 // 1. if KeyUsage, then [0:digitalSignature] or 1929 // [1:nonRepudiation] should be true 1930 // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING 1931 // 3. if NetscapeCertType, then should contains OBJECT_SIGNING 1932 // 1,2,3 must be true 1933 1934 if (bad != null) { 1935 bad[0] = bad[1] = bad[2] = false; 1936 } 1937 1938 boolean[] keyUsage = userCert.getKeyUsage(); 1939 if (keyUsage != null) { 1940 keyUsage = Arrays.copyOf(keyUsage, 9); 1941 if (!keyUsage[0] && !keyUsage[1]) { 1942 if (bad != null) { 1943 bad[0] = true; 1944 badKeyUsage = true; 1945 } 1946 } 1947 } 1948 1949 try { 1950 List<String> xKeyUsage = userCert.getExtendedKeyUsage(); 1951 if (xKeyUsage != null) { 1952 if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage 1953 && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning 1954 if (bad != null) { 1955 bad[1] = true; 1956 badExtendedKeyUsage = true; 1957 } 1958 } 1959 } 1960 } catch (java.security.cert.CertificateParsingException e) { 1961 // shouldn't happen 1962 } 1963 1964 try { 1965 // OID_NETSCAPE_CERT_TYPE 1966 byte[] netscapeEx = userCert.getExtensionValue 1967 ("2.16.840.1.113730.1.1"); 1968 if (netscapeEx != null) { 1969 DerInputStream in = new DerInputStream(netscapeEx); 1970 byte[] encoded = in.getOctetString(); 1971 encoded = new DerValue(encoded).getUnalignedBitString() 1972 .toByteArray(); 1973 1974 NetscapeCertTypeExtension extn = 1975 new NetscapeCertTypeExtension(encoded); 1976 1977 Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING); 1978 if (!val) { 1979 if (bad != null) { 1980 bad[2] = true; 1981 badNetscapeCertType = true; 1982 } 1983 } 1984 } 1985 } catch (IOException e) { 1986 // 1987 } 1988 } 1989 1990 void getAliasInfo(String alias) { 1991 1992 Key key = null; 1993 1994 try { 1995 java.security.cert.Certificate[] cs = null; 1996 if (altCertChain != null) { 1997 try (FileInputStream fis = new FileInputStream(altCertChain)) { 1998 cs = CertificateFactory.getInstance("X.509"). 1999 generateCertificates(fis). 2000 toArray(new Certificate[0]); 2001 } catch (FileNotFoundException ex) { 2002 error(rb.getString("File.specified.by.certchain.does.not.exist")); 2003 } catch (CertificateException | IOException ex) { 2004 error(rb.getString("Cannot.restore.certchain.from.file.specified")); 2005 } 2006 } else { 2007 try { 2008 cs = store.getCertificateChain(alias); 2009 } catch (KeyStoreException kse) { 2010 // this never happens, because keystore has been loaded 2011 } 2012 } 2013 if (cs == null || cs.length == 0) { 2014 if (altCertChain != null) { 2015 error(rb.getString 2016 ("Certificate.chain.not.found.in.the.file.specified.")); 2017 } else { 2018 MessageFormat form = new MessageFormat(rb.getString 2019 ("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and")); 2020 Object[] source = {alias, alias}; 2021 error(form.format(source)); 2022 } 2023 } 2024 2025 certChain = new X509Certificate[cs.length]; 2026 for (int i=0; i<cs.length; i++) { 2027 if (!(cs[i] instanceof X509Certificate)) { 2028 error(rb.getString 2029 ("found.non.X.509.certificate.in.signer.s.chain")); 2030 } 2031 certChain[i] = (X509Certificate)cs[i]; 2032 } 2033 2034 // We don't meant to print anything, the next call 2035 // checks validity and keyUsage etc 2036 printCert("", certChain[0], true, null, true); 2037 2038 try { 2039 validateCertChain(Arrays.asList(certChain)); 2040 } catch (Exception e) { 2041 if (debug) { 2042 e.printStackTrace(); 2043 } 2044 if (e.getCause() != null && 2045 (e.getCause() instanceof CertificateExpiredException || 2046 e.getCause() instanceof CertificateNotYetValidException)) { 2047 // No more warning, we alreay have hasExpiredCert or notYetValidCert 2048 } else { 2049 chainNotValidated = true; 2050 } 2051 } 2052 2053 try { 2054 if (!token && keypass == null) 2055 key = store.getKey(alias, storepass); 2056 else 2057 key = store.getKey(alias, keypass); 2058 } catch (UnrecoverableKeyException e) { 2059 if (token) { 2060 throw e; 2061 } else if (keypass == null) { 2062 // Did not work out, so prompt user for key password 2063 MessageFormat form = new MessageFormat(rb.getString 2064 ("Enter.key.password.for.alias.")); 2065 Object[] source = {alias}; 2066 keypass = getPass(form.format(source)); 2067 key = store.getKey(alias, keypass); 2068 } 2069 } 2070 } catch (NoSuchAlgorithmException e) { 2071 error(e.getMessage()); 2072 } catch (UnrecoverableKeyException e) { 2073 error(rb.getString("unable.to.recover.key.from.keystore")); 2074 } catch (KeyStoreException kse) { 2075 // this never happens, because keystore has been loaded 2076 } 2077 2078 if (!(key instanceof PrivateKey)) { 2079 MessageFormat form = new MessageFormat(rb.getString 2080 ("key.associated.with.alias.not.a.private.key")); 2081 Object[] source = {alias}; 2082 error(form.format(source)); 2083 } else { 2084 privateKey = (PrivateKey)key; 2085 } 2086 } 2087 2088 void error(String message) 2089 { 2090 System.out.println(rb.getString("jarsigner.")+message); 2091 System.exit(1); 2092 } 2093 2094 2095 void error(String message, Exception e) 2096 { 2097 System.out.println(rb.getString("jarsigner.")+message); 2098 if (debug) { 2099 e.printStackTrace(); 2100 } 2101 System.exit(1); 2102 } 2103 2104 void validateCertChain(List<? extends Certificate> certs) throws Exception { 2105 int cpLen = 0; 2106 out: for (; cpLen<certs.size(); cpLen++) { 2107 for (TrustAnchor ta: pkixParameters.getTrustAnchors()) { 2108 if (ta.getTrustedCert().equals(certs.get(cpLen))) { 2109 break out; 2110 } 2111 } 2112 } 2113 if (cpLen > 0) { 2114 CertPath cp = certificateFactory.generateCertPath( 2115 (cpLen == certs.size())? certs: certs.subList(0, cpLen)); 2116 validator.validate(cp, pkixParameters); 2117 } 2118 } 2119 2120 char[] getPass(String prompt) 2121 { 2122 System.err.print(prompt); 2123 System.err.flush(); 2124 try { 2125 char[] pass = Password.readPassword(System.in); 2126 2127 if (pass == null) { 2128 error(rb.getString("you.must.enter.key.password")); 2129 } else { 2130 return pass; 2131 } 2132 } catch (IOException ioe) { 2133 error(rb.getString("unable.to.read.password.")+ioe.getMessage()); 2134 } 2135 // this shouldn't happen 2136 return null; 2137 } 2138 2139 /* 2140 * Reads all the bytes for a given zip entry. 2141 */ 2142 private synchronized byte[] getBytes(ZipFile zf, 2143 ZipEntry ze) throws IOException { 2144 int n; 2145 2146 InputStream is = null; 2147 try { 2148 is = zf.getInputStream(ze); 2149 baos.reset(); 2150 long left = ze.getSize(); 2151 2152 while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) { 2153 baos.write(buffer, 0, n); 2154 left -= n; 2155 } 2156 } finally { 2157 if (is != null) { 2158 is.close(); 2159 } 2160 } 2161 2162 return baos.toByteArray(); 2163 } 2164 2165 /* 2166 * Returns manifest entry from given jar file, or null if given jar file 2167 * does not have a manifest entry. 2168 */ 2169 private ZipEntry getManifestFile(ZipFile zf) { 2170 ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME); 2171 if (ze == null) { 2172 // Check all entries for matching name 2173 Enumeration<? extends ZipEntry> enum_ = zf.entries(); 2174 while (enum_.hasMoreElements() && ze == null) { 2175 ze = enum_.nextElement(); 2176 if (!JarFile.MANIFEST_NAME.equalsIgnoreCase 2177 (ze.getName())) { 2178 ze = null; 2179 } 2180 } 2181 } 2182 return ze; 2183 } 2184 2185 /* 2186 * Computes the digests of a zip entry, and returns them as an array 2187 * of base64-encoded strings. 2188 */ 2189 private synchronized String[] getDigests(ZipEntry ze, ZipFile zf, 2190 MessageDigest[] digests) 2191 throws IOException { 2192 2193 int n, i; 2194 InputStream is = null; 2195 try { 2196 is = zf.getInputStream(ze); 2197 long left = ze.getSize(); 2198 while((left > 0) 2199 && (n = is.read(buffer, 0, buffer.length)) != -1) { 2200 for (i=0; i<digests.length; i++) { 2201 digests[i].update(buffer, 0, n); 2202 } 2203 left -= n; 2204 } 2205 } finally { 2206 if (is != null) { 2207 is.close(); 2208 } 2209 } 2210 2211 // complete the digests 2212 String[] base64Digests = new String[digests.length]; 2213 for (i=0; i<digests.length; i++) { 2214 base64Digests[i] = Base64.getEncoder().encodeToString(digests[i].digest()); 2215 } 2216 return base64Digests; 2217 } 2218 2219 /* 2220 * Computes the digests of a zip entry, and returns them as a list of 2221 * attributes 2222 */ 2223 private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf, 2224 MessageDigest[] digests) 2225 throws IOException { 2226 2227 String[] base64Digests = getDigests(ze, zf, digests); 2228 Attributes attrs = new Attributes(); 2229 2230 for (int i=0; i<digests.length; i++) { 2231 attrs.putValue(digests[i].getAlgorithm()+"-Digest", 2232 base64Digests[i]); 2233 } 2234 return attrs; 2235 } 2236 2237 /* 2238 * Updates the digest attributes of a manifest entry, by adding or 2239 * replacing digest values. 2240 * A digest value is added if the manifest entry does not contain a digest 2241 * for that particular algorithm. 2242 * A digest value is replaced if it is obsolete. 2243 * 2244 * Returns true if the manifest entry has been changed, and false 2245 * otherwise. 2246 */ 2247 private boolean updateDigests(ZipEntry ze, ZipFile zf, 2248 MessageDigest[] digests, 2249 Manifest mf) throws IOException { 2250 boolean update = false; 2251 2252 Attributes attrs = mf.getAttributes(ze.getName()); 2253 String[] base64Digests = getDigests(ze, zf, digests); 2254 2255 for (int i=0; i<digests.length; i++) { 2256 // The entry name to be written into attrs 2257 String name = null; 2258 try { 2259 // Find if the digest already exists 2260 AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm()); 2261 for (Object key: attrs.keySet()) { 2262 if (key instanceof Attributes.Name) { 2263 String n = ((Attributes.Name)key).toString(); 2264 if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { 2265 String tmp = n.substring(0, n.length() - 7); 2266 if (AlgorithmId.get(tmp).equals(aid)) { 2267 name = n; 2268 break; 2269 } 2270 } 2271 } 2272 } 2273 } catch (NoSuchAlgorithmException nsae) { 2274 // Ignored. Writing new digest entry. 2275 } 2276 2277 if (name == null) { 2278 name = digests[i].getAlgorithm()+"-Digest"; 2279 attrs.putValue(name, base64Digests[i]); 2280 update=true; 2281 } else { 2282 // compare digests, and replace the one in the manifest 2283 // if they are different 2284 String mfDigest = attrs.getValue(name); 2285 if (!mfDigest.equalsIgnoreCase(base64Digests[i])) { 2286 attrs.putValue(name, base64Digests[i]); 2287 update=true; 2288 } 2289 } 2290 } 2291 return update; 2292 } 2293 2294 /* 2295 * Try to load the specified signing mechanism. 2296 * The URL class loader is used. 2297 */ 2298 private ContentSigner loadSigningMechanism(String signerClassName, 2299 String signerClassPath) throws Exception { 2300 2301 // construct class loader 2302 String cpString = null; // make sure env.class.path defaults to dot 2303 2304 // do prepends to get correct ordering 2305 cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString); 2306 cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString); 2307 cpString = PathList.appendPath(signerClassPath, cpString); 2308 URL[] urls = PathList.pathToURLs(cpString); 2309 ClassLoader appClassLoader = new URLClassLoader(urls); 2310 2311 // attempt to find signer 2312 Class<?> signerClass = appClassLoader.loadClass(signerClassName); 2313 2314 // Check that it implements ContentSigner 2315 Object signer = signerClass.newInstance(); 2316 if (!(signer instanceof ContentSigner)) { 2317 MessageFormat form = new MessageFormat( 2318 rb.getString("signerClass.is.not.a.signing.mechanism")); 2319 Object[] source = {signerClass.getName()}; 2320 throw new IllegalArgumentException(form.format(source)); 2321 } 2322 return (ContentSigner)signer; 2323 } 2324 } 2325 2326 class SignatureFile { 2327 2328 /** SignatureFile */ 2329 Manifest sf; 2330 2331 /** .SF base name */ 2332 String baseName; 2333 2334 public SignatureFile(MessageDigest digests[], 2335 Manifest mf, 2336 ManifestDigester md, 2337 String baseName, 2338 boolean signManifest) 2339 2340 { 2341 this.baseName = baseName; 2342 2343 String version = System.getProperty("java.version"); 2344 String javaVendor = System.getProperty("java.vendor"); 2345 2346 sf = new Manifest(); 2347 Attributes mattr = sf.getMainAttributes(); 2348 2349 mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0"); 2350 mattr.putValue("Created-By", version + " (" + javaVendor + ")"); 2351 2352 if (signManifest) { 2353 // sign the whole manifest 2354 for (int i=0; i < digests.length; i++) { 2355 mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest", 2356 Base64.getEncoder().encodeToString(md.manifestDigest(digests[i]))); 2357 } 2358 } 2359 2360 // create digest of the manifest main attributes 2361 ManifestDigester.Entry mde = 2362 md.get(ManifestDigester.MF_MAIN_ATTRS, false); 2363 if (mde != null) { 2364 for (int i=0; i < digests.length; i++) { 2365 mattr.putValue(digests[i].getAlgorithm() + 2366 "-Digest-" + ManifestDigester.MF_MAIN_ATTRS, 2367 Base64.getEncoder().encodeToString(mde.digest(digests[i]))); 2368 } 2369 } else { 2370 throw new IllegalStateException 2371 ("ManifestDigester failed to create " + 2372 "Manifest-Main-Attribute entry"); 2373 } 2374 2375 /* go through the manifest entries and create the digests */ 2376 2377 Map<String,Attributes> entries = sf.getEntries(); 2378 Iterator<Map.Entry<String,Attributes>> mit = 2379 mf.getEntries().entrySet().iterator(); 2380 while(mit.hasNext()) { 2381 Map.Entry<String,Attributes> e = mit.next(); 2382 String name = e.getKey(); 2383 mde = md.get(name, false); 2384 if (mde != null) { 2385 Attributes attr = new Attributes(); 2386 for (int i=0; i < digests.length; i++) { 2387 attr.putValue(digests[i].getAlgorithm()+"-Digest", 2388 Base64.getEncoder().encodeToString(mde.digest(digests[i]))); 2389 } 2390 entries.put(name, attr); 2391 } 2392 } 2393 } 2394 2395 /** 2396 * Writes the SignatureFile to the specified OutputStream. 2397 * 2398 * @param out the output stream 2399 * @exception IOException if an I/O error has occurred 2400 */ 2401 2402 public void write(OutputStream out) throws IOException 2403 { 2404 sf.write(out); 2405 } 2406 2407 /** 2408 * get .SF file name 2409 */ 2410 public String getMetaName() 2411 { 2412 return "META-INF/"+ baseName + ".SF"; 2413 } 2414 2415 /** 2416 * get base file name 2417 */ 2418 public String getBaseName() 2419 { 2420 return baseName; 2421 } 2422 2423 /* 2424 * Generate a signed data block. 2425 * If a URL or a certificate (containing a URL) for a Timestamping 2426 * Authority is supplied then a signature timestamp is generated and 2427 * inserted into the signed data block. 2428 * 2429 * @param sigalg signature algorithm to use, or null to use default 2430 * @param tsaUrl The location of the Timestamping Authority. If null 2431 * then no timestamp is requested. 2432 * @param tsaCert The certificate for the Timestamping Authority. If null 2433 * then no timestamp is requested. 2434 * @param signingMechanism The signing mechanism to use. 2435 * @param args The command-line arguments to jarsigner. 2436 * @param zipFile The original source Zip file. 2437 */ 2438 public Block generateBlock(PrivateKey privateKey, 2439 String sigalg, 2440 X509Certificate[] certChain, 2441 boolean externalSF, String tsaUrl, 2442 X509Certificate tsaCert, 2443 String tSAPolicyID, 2444 String tSADigestAlg, 2445 ContentSigner signingMechanism, 2446 String[] args, ZipFile zipFile) 2447 throws NoSuchAlgorithmException, InvalidKeyException, IOException, 2448 SignatureException, CertificateException 2449 { 2450 return new Block(this, privateKey, sigalg, certChain, externalSF, 2451 tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg, signingMechanism, args, zipFile); 2452 } 2453 2454 2455 public static class Block { 2456 2457 private byte[] block; 2458 private String blockFileName; 2459 2460 /* 2461 * Construct a new signature block. 2462 */ 2463 Block(SignatureFile sfg, PrivateKey privateKey, String sigalg, 2464 X509Certificate[] certChain, boolean externalSF, String tsaUrl, 2465 X509Certificate tsaCert, String tSAPolicyID, String tSADigestAlg, 2466 ContentSigner signingMechanism, String[] args, ZipFile zipFile) 2467 throws NoSuchAlgorithmException, InvalidKeyException, IOException, 2468 SignatureException, CertificateException { 2469 2470 Principal issuerName = certChain[0].getIssuerDN(); 2471 if (!(issuerName instanceof X500Name)) { 2472 // must extract the original encoded form of DN for subsequent 2473 // name comparison checks (converting to a String and back to 2474 // an encoded DN could cause the types of String attribute 2475 // values to be changed) 2476 X509CertInfo tbsCert = new 2477 X509CertInfo(certChain[0].getTBSCertificate()); 2478 issuerName = (Principal) 2479 tbsCert.get(X509CertInfo.ISSUER + "." + 2480 X509CertInfo.DN_NAME); 2481 } 2482 BigInteger serial = certChain[0].getSerialNumber(); 2483 2484 String signatureAlgorithm; 2485 String keyAlgorithm = privateKey.getAlgorithm(); 2486 /* 2487 * If no signature algorithm was specified, we choose a 2488 * default that is compatible with the private key algorithm. 2489 */ 2490 if (sigalg == null) { 2491 2492 if (keyAlgorithm.equalsIgnoreCase("DSA")) 2493 signatureAlgorithm = "SHA1withDSA"; 2494 else if (keyAlgorithm.equalsIgnoreCase("RSA")) 2495 signatureAlgorithm = "SHA256withRSA"; 2496 else if (keyAlgorithm.equalsIgnoreCase("EC")) 2497 signatureAlgorithm = "SHA256withECDSA"; 2498 else 2499 throw new RuntimeException("private key is not a DSA or " 2500 + "RSA key"); 2501 } else { 2502 signatureAlgorithm = sigalg; 2503 } 2504 2505 // check common invalid key/signature algorithm combinations 2506 String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH); 2507 if ((sigAlgUpperCase.endsWith("WITHRSA") && 2508 !keyAlgorithm.equalsIgnoreCase("RSA")) || 2509 (sigAlgUpperCase.endsWith("WITHECDSA") && 2510 !keyAlgorithm.equalsIgnoreCase("EC")) || 2511 (sigAlgUpperCase.endsWith("WITHDSA") && 2512 !keyAlgorithm.equalsIgnoreCase("DSA"))) { 2513 throw new SignatureException 2514 ("private key algorithm is not compatible with signature algorithm"); 2515 } 2516 2517 blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm; 2518 2519 AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm); 2520 AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm); 2521 2522 Signature sig = Signature.getInstance(signatureAlgorithm); 2523 sig.initSign(privateKey); 2524 2525 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 2526 sfg.write(baos); 2527 2528 byte[] content = baos.toByteArray(); 2529 2530 sig.update(content); 2531 byte[] signature = sig.sign(); 2532 2533 // Timestamp the signature and generate the signature block file 2534 if (signingMechanism == null) { 2535 signingMechanism = new TimestampedSigner(); 2536 } 2537 URI tsaUri = null; 2538 try { 2539 if (tsaUrl != null) { 2540 tsaUri = new URI(tsaUrl); 2541 } 2542 } catch (URISyntaxException e) { 2543 throw new IOException(e); 2544 } 2545 2546 // Assemble parameters for the signing mechanism 2547 ContentSignerParameters params = 2548 new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID, 2549 tSADigestAlg, signature, 2550 signatureAlgorithm, certChain, content, zipFile); 2551 2552 // Generate the signature block 2553 block = signingMechanism.generateSignedData( 2554 params, externalSF, (tsaUrl != null || tsaCert != null)); 2555 } 2556 2557 /* 2558 * get block file name. 2559 */ 2560 public String getMetaName() 2561 { 2562 return blockFileName; 2563 } 2564 2565 /** 2566 * Writes the block file to the specified OutputStream. 2567 * 2568 * @param out the output stream 2569 * @exception IOException if an I/O error has occurred 2570 */ 2571 2572 public void write(OutputStream out) throws IOException 2573 { 2574 out.write(block); 2575 } 2576 } 2577 }