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