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