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