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