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