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