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