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