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             boolean wasSigned = false;
 955 
 956             for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
 957                         enum_.hasMoreElements();) {
 958                 ZipEntry ze = enum_.nextElement();
 959 
 960                 if (ze.getName().startsWith(META_INF)) {
 961                     // Store META-INF files in vector, so they can be written
 962                     // out first
 963                     mfFiles.addElement(ze);
 964 
 965                     if (SignatureFileVerifier.isBlockOrSF(
 966                             ze.getName().toUpperCase(Locale.ENGLISH))) {
 967                         wasSigned = true;
 968                     }
 969 
 970                     if (signatureRelated(ze.getName())) {
 971                         // ignore signature-related and manifest files
 972                         continue;
 973                     }
 974                 }
 975 
 976                 if (manifest.getAttributes(ze.getName()) != null) {
 977                     // jar entry is contained in manifest, check and
 978                     // possibly update its digest attributes
 979                     if (updateDigests(ze, zipFile, digests, encoder,
 980                                       manifest) == true) {
 981                         mfModified = true;
 982                     }
 983                 } else if (!ze.isDirectory()) {
 984                     // Add entry to manifest
 985                     Attributes attrs = getDigestAttributes(ze, zipFile,
 986                                                            digests,
 987                                                            encoder);
 988                     mfEntries.put(ze.getName(), attrs);
 989                     mfModified = true;
 990                 }
 991             }
 992 
 993             // Recalculate the manifest raw bytes if necessary
 994             if (mfModified) {
 995                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 996                 manifest.write(baos);
 997                 if (wasSigned) {
 998                     byte[] newBytes = baos.toByteArray();
 999                     if (mfRawBytes != null
1000                             && oldAttr.equals(manifest.getMainAttributes())) {
1001 
1002                         /*
1003                          * Note:
1004                          *
1005                          * The Attributes object is based on HashMap and can handle
1006                          * continuation columns. Therefore, even if the contents are
1007                          * not changed (in a Map view), the bytes that it write()
1008                          * may be different from the original bytes that it read()
1009                          * from. Since the signature on the main attributes is based
1010                          * on raw bytes, we must retain the exact bytes.
1011                          */
1012 
1013                         int newPos = findHeaderEnd(newBytes);
1014                         int oldPos = findHeaderEnd(mfRawBytes);
1015 
1016                         if (newPos == oldPos) {
1017                             System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
1018                         } else {
1019                             // cat oldHead newTail > newBytes
1020                             byte[] lastBytes = new byte[oldPos +
1021                                     newBytes.length - newPos];
1022                             System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
1023                             System.arraycopy(newBytes, newPos, lastBytes, oldPos,
1024                                     newBytes.length - newPos);
1025                             newBytes = lastBytes;
1026                         }
1027                     }
1028                     mfRawBytes = newBytes;
1029                 } else {
1030                     mfRawBytes = baos.toByteArray();
1031                 }
1032             }
1033 
1034             // Write out the manifest
1035             if (mfModified) {
1036                 // manifest file has new length
1037                 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1038             }
1039             if (verbose) {
1040                 if (mfCreated) {
1041                     System.out.println(rb.getString("   adding: ") +
1042                                         mfFile.getName());
1043                 } else if (mfModified) {
1044                     System.out.println(rb.getString(" updating: ") +
1045                                         mfFile.getName());
1046                 }
1047             }
1048             zos.putNextEntry(mfFile);
1049             zos.write(mfRawBytes);
1050 
1051             // Calculate SignatureFile (".SF") and SignatureBlockFile
1052             ManifestDigester manDig = new ManifestDigester(mfRawBytes);
1053             SignatureFile sf = new SignatureFile(digests, manifest, manDig,
1054                                                  sigfile, signManifest);
1055 
1056             if (tsaAlias != null) {
1057                 tsaCert = getTsaCert(tsaAlias);
1058             }
1059 
1060             SignatureFile.Block block = null;
1061 
1062             try {
1063                 block =
1064                     sf.generateBlock(privateKey, sigalg, certChain,
1065                         externalSF, tsaUrl, tsaCert, signingMechanism, args,
1066                         zipFile);
1067             } catch (SocketTimeoutException e) {
1068                 // Provide a helpful message when TSA is beyond a firewall
1069                 error(rb.getString("unable to sign jar: ") +
1070                 rb.getString("no response from the Timestamping Authority. ") +
1071                 rb.getString("When connecting from behind a firewall then an HTTP proxy may need to be specified. ") +
1072                 rb.getString("Supply the following options to jarsigner: ") +
1073                 "\n  -J-Dhttp.proxyHost=<hostname> " +
1074                 "\n  -J-Dhttp.proxyPort=<portnumber> ", e);
1075             }
1076 
1077             sfFilename = sf.getMetaName();
1078             bkFilename = block.getMetaName();
1079 
1080             ZipEntry sfFile = new ZipEntry(sfFilename);
1081             ZipEntry bkFile = new ZipEntry(bkFilename);
1082 
1083             long time = System.currentTimeMillis();
1084             sfFile.setTime(time);
1085             bkFile.setTime(time);
1086 
1087             // signature file
1088             zos.putNextEntry(sfFile);
1089             sf.write(zos);
1090             if (verbose) {
1091                 if (zipFile.getEntry(sfFilename) != null) {
1092                     System.out.println(rb.getString(" updating: ") +
1093                                 sfFilename);
1094                 } else {
1095                     System.out.println(rb.getString("   adding: ") +
1096                                 sfFilename);
1097                 }
1098             }
1099 
1100             if (verbose) {
1101                 if (tsaUrl != null || tsaCert != null) {
1102                     System.out.println(
1103                         rb.getString("requesting a signature timestamp"));
1104                 }
1105                 if (tsaUrl != null) {
1106                     System.out.println(rb.getString("TSA location: ") + tsaUrl);
1107                 }
1108                 if (tsaCert != null) {
1109                     String certUrl =
1110                         TimestampedSigner.getTimestampingUrl(tsaCert);
1111                     if (certUrl != null) {
1112                         System.out.println(rb.getString("TSA location: ") +
1113                             certUrl);
1114                     }
1115                     System.out.println(
1116                         rb.getString("TSA certificate: ") + printCert(tsaCert));
1117                 }
1118                 if (signingMechanism != null) {
1119                     System.out.println(
1120                         rb.getString("using an alternative signing mechanism"));
1121                 }
1122             }
1123 
1124             // signature block file
1125             zos.putNextEntry(bkFile);
1126             block.write(zos);
1127             if (verbose) {
1128                 if (zipFile.getEntry(bkFilename) != null) {
1129                     System.out.println(rb.getString(" updating: ") +
1130                         bkFilename);
1131                 } else {
1132                     System.out.println(rb.getString("   adding: ") +
1133                         bkFilename);
1134                 }
1135             }
1136 
1137             // Write out all other META-INF files that we stored in the
1138             // vector
1139             for (int i=0; i<mfFiles.size(); i++) {
1140                 ZipEntry ze = mfFiles.elementAt(i);
1141                 if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
1142                     && !ze.getName().equalsIgnoreCase(sfFilename)
1143                     && !ze.getName().equalsIgnoreCase(bkFilename)) {
1144                     writeEntry(zipFile, zos, ze);
1145                 }
1146             }
1147 
1148             // Write out all other files
1149             for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1150                         enum_.hasMoreElements();) {
1151                 ZipEntry ze = enum_.nextElement();
1152 
1153                 if (!ze.getName().startsWith(META_INF)) {
1154                     if (verbose) {
1155                         if (manifest.getAttributes(ze.getName()) != null)
1156                           System.out.println(rb.getString("  signing: ") +
1157                                 ze.getName());
1158                         else
1159                           System.out.println(rb.getString("   adding: ") +
1160                                 ze.getName());
1161                     }
1162                     writeEntry(zipFile, zos, ze);
1163                 }
1164             }
1165         } catch(IOException ioe) {
1166             error(rb.getString("unable to sign jar: ")+ioe, ioe);
1167         } finally {
1168             // close the resouces
1169             if (zipFile != null) {
1170                 zipFile.close();
1171                 zipFile = null;
1172             }
1173 
1174             if (zos != null) {
1175                 zos.close();
1176             }
1177         }
1178 
1179         // no IOException thrown in the follow try clause, so disable
1180         // the try clause.
1181         // try {
1182             if (signedjar == null) {
1183                 // attempt an atomic rename. If that fails,
1184                 // rename the original jar file, then the signed
1185                 // one, then delete the original.
1186                 if (!signedJarFile.renameTo(jarFile)) {
1187                     File origJar = new File(jarName+".orig");
1188 
1189                     if (jarFile.renameTo(origJar)) {
1190                         if (signedJarFile.renameTo(jarFile)) {
1191                             origJar.delete();
1192                         } else {
1193                             MessageFormat form = new MessageFormat(rb.getString
1194                         ("attempt to rename signedJarFile to jarFile failed"));
1195                             Object[] source = {signedJarFile, jarFile};
1196                             error(form.format(source));
1197                         }
1198                     } else {
1199                         MessageFormat form = new MessageFormat(rb.getString
1200                             ("attempt to rename jarFile to origJar failed"));
1201                         Object[] source = {jarFile, origJar};
1202                         error(form.format(source));
1203                     }
1204                 }
1205             }
1206 
1207             if (hasExpiredCert || hasExpiringCert || notYetValidCert
1208                     || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
1209                 System.out.println();
1210 
1211                 System.out.println(rb.getString("Warning: "));
1212                 if (badKeyUsage) {
1213                     System.out.println(
1214                         rb.getString("The signer certificate's KeyUsage extension doesn't allow code signing."));
1215                 }
1216 
1217                 if (badExtendedKeyUsage) {
1218                     System.out.println(
1219                         rb.getString("The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."));
1220                 }
1221 
1222                 if (badNetscapeCertType) {
1223                     System.out.println(
1224                         rb.getString("The signer certificate's NetscapeCertType extension doesn't allow code signing."));
1225                 }
1226 
1227                 if (hasExpiredCert) {
1228                     System.out.println(
1229                         rb.getString("The signer certificate has expired."));
1230                 } else if (hasExpiringCert) {
1231                     System.out.println(
1232                         rb.getString("The signer certificate will expire within six months."));
1233                 } else if (notYetValidCert) {
1234                     System.out.println(
1235                         rb.getString("The signer certificate is not yet valid."));
1236                 }
1237             }
1238 
1239         // no IOException thrown in the above try clause, so disable
1240         // the catch clause.
1241         // } catch(IOException ioe) {
1242         //     error(rb.getString("unable to sign jar: ")+ioe, ioe);
1243         // }
1244     }
1245 
1246     /**
1247      * Find the length of header inside bs. The header is a multiple (>=0)
1248      * lines of attributes plus an empty line. The empty line is included
1249      * in the header.
1250      */
1251     private int findHeaderEnd(byte[] bs) {
1252         // Initial state true to deal with empty header
1253         boolean newline = true;     // just met a newline
1254         int len = bs.length;
1255         for (int i=0; i<len; i++) {
1256             switch (bs[i]) {
1257                 case '\r':
1258                     if (i < len - 1 && bs[i+1] == '\n') i++;
1259                     // fallthrough
1260                 case '\n':
1261                     if (newline) return i+1;    //+1 to get length
1262                     newline = true;
1263                     break;
1264                 default:
1265                     newline = false;
1266             }
1267         }
1268         // If header end is not found, it means the MANIFEST.MF has only
1269         // the main attributes section and it does not end with 2 newlines.
1270         // Returns the whole length so that it can be completely replaced.
1271         return len;
1272     }
1273 
1274     /**
1275      * signature-related files include:
1276      * . META-INF/MANIFEST.MF
1277      * . META-INF/SIG-*
1278      * . META-INF/*.SF
1279      * . META-INF/*.DSA
1280      * . META-INF/*.RSA
1281      */
1282     private boolean signatureRelated(String name) {
1283         String ucName = name.toUpperCase();
1284         if (ucName.equals(JarFile.MANIFEST_NAME) ||
1285             ucName.equals(META_INF) ||
1286             (ucName.startsWith(SIG_PREFIX) &&
1287                 ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
1288             return true;
1289         }
1290 
1291         if (ucName.startsWith(META_INF) &&
1292             SignatureFileVerifier.isBlockOrSF(ucName)) {
1293             // .SF/.DSA/.RSA files in META-INF subdirs
1294             // are not considered signature-related
1295             return (ucName.indexOf("/") == ucName.lastIndexOf("/"));
1296         }
1297 
1298         return false;
1299     }
1300 
1301     private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
1302     throws IOException
1303     {
1304         ZipEntry ze2 = new ZipEntry(ze.getName());
1305         ze2.setMethod(ze.getMethod());
1306         ze2.setTime(ze.getTime());
1307         ze2.setComment(ze.getComment());
1308         ze2.setExtra(ze.getExtra());
1309         if (ze.getMethod() == ZipEntry.STORED) {
1310             ze2.setSize(ze.getSize());
1311             ze2.setCrc(ze.getCrc());
1312         }
1313         os.putNextEntry(ze2);
1314         writeBytes(zf, ze, os);
1315     }
1316 
1317     /**
1318      * Writes all the bytes for a given entry to the specified output stream.
1319      */
1320     private synchronized void writeBytes
1321         (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
1322         int n;
1323 
1324         InputStream is = null;
1325         try {
1326             is = zf.getInputStream(ze);
1327             long left = ze.getSize();
1328 
1329             while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
1330                 os.write(buffer, 0, n);
1331                 left -= n;
1332             }
1333         } finally {
1334             if (is != null) {
1335                 is.close();
1336             }
1337         }
1338     }
1339 
1340     void loadKeyStore(String keyStoreName, boolean prompt) {
1341 
1342         if (!nullStream && keyStoreName == null) {
1343             keyStoreName = System.getProperty("user.home") + File.separator
1344                 + ".keystore";
1345         }
1346 
1347         try {
1348             if (providerName == null) {
1349                 store = KeyStore.getInstance(storetype);
1350             } else {
1351                 store = KeyStore.getInstance(storetype, providerName);
1352             }
1353 
1354             // Get pass phrase
1355             // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z
1356             // and on NT call ??
1357             if (token && storepass == null && !protectedPath
1358                     && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
1359                 storepass = getPass
1360                         (rb.getString("Enter Passphrase for keystore: "));
1361             } else if (!token && storepass == null && prompt) {
1362                 storepass = getPass
1363                         (rb.getString("Enter Passphrase for keystore: "));
1364             }
1365 
1366             if (nullStream) {
1367                 store.load(null, storepass);
1368             } else {
1369                 keyStoreName = keyStoreName.replace(File.separatorChar, '/');
1370                 URL url = null;
1371                 try {
1372                     url = new URL(keyStoreName);
1373                 } catch (java.net.MalformedURLException e) {
1374                     // try as file
1375                     url = new File(keyStoreName).toURI().toURL();
1376                 }
1377                 InputStream is = null;
1378                 try {
1379                     is = url.openStream();
1380                     store.load(is, storepass);
1381                 } finally {
1382                     if (is != null) {
1383                         is.close();
1384                     }
1385                 }
1386             }
1387         } catch (IOException ioe) {
1388             throw new RuntimeException(rb.getString("keystore load: ") +
1389                                         ioe.getMessage());
1390         } catch (java.security.cert.CertificateException ce) {
1391             throw new RuntimeException(rb.getString("certificate exception: ") +
1392                                         ce.getMessage());
1393         } catch (NoSuchProviderException pe) {
1394             throw new RuntimeException(rb.getString("keystore load: ") +
1395                                         pe.getMessage());
1396         } catch (NoSuchAlgorithmException nsae) {
1397             throw new RuntimeException(rb.getString("keystore load: ") +
1398                                         nsae.getMessage());
1399         } catch (KeyStoreException kse) {
1400             throw new RuntimeException
1401                 (rb.getString("unable to instantiate keystore class: ") +
1402                 kse.getMessage());
1403         }
1404     }
1405 
1406     X509Certificate getTsaCert(String alias) {
1407 
1408         java.security.cert.Certificate cs = null;
1409 
1410         try {
1411             cs = store.getCertificate(alias);
1412         } catch (KeyStoreException kse) {
1413             // this never happens, because keystore has been loaded
1414         }
1415         if (cs == null || (!(cs instanceof X509Certificate))) {
1416             MessageFormat form = new MessageFormat(rb.getString
1417                 ("Certificate not found for: alias.  alias must reference a valid KeyStore entry containing an X.509 public key certificate for the Timestamping Authority."));
1418             Object[] source = {alias, alias};
1419             error(form.format(source));
1420         }
1421         return (X509Certificate) cs;
1422     }
1423 
1424     /**
1425      * Check if userCert is designed to be a code signer
1426      * @param userCert the certificate to be examined
1427      * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
1428      *            NetscapeCertType has codeSigning flag turned on.
1429      *            If null, the class field badKeyUsage, badExtendedKeyUsage,
1430      *            badNetscapeCertType will be set.
1431      */
1432     void checkCertUsage(X509Certificate userCert, boolean[] bad) {
1433 
1434         // Can act as a signer?
1435         // 1. if KeyUsage, then [0] should be true
1436         // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
1437         // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
1438         // 1,2,3 must be true
1439 
1440         if (bad != null) {
1441             bad[0] = bad[1] = bad[2] = false;
1442         }
1443 
1444         boolean[] keyUsage = userCert.getKeyUsage();
1445         if (keyUsage != null) {
1446             if (keyUsage.length < 1 || !keyUsage[0]) {
1447                 if (bad != null) {
1448                     bad[0] = true;
1449                 } else {
1450                     badKeyUsage = true;
1451                 }
1452             }
1453         }
1454 
1455         try {
1456             List<String> xKeyUsage = userCert.getExtendedKeyUsage();
1457             if (xKeyUsage != null) {
1458                 if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
1459                         && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) {  // codeSigning
1460                     if (bad != null) {
1461                         bad[1] = true;
1462                     } else {
1463                         badExtendedKeyUsage = true;
1464                     }
1465                 }
1466             }
1467         } catch (java.security.cert.CertificateParsingException e) {
1468             // shouldn't happen
1469         }
1470 
1471         try {
1472             // OID_NETSCAPE_CERT_TYPE
1473             byte[] netscapeEx = userCert.getExtensionValue
1474                     ("2.16.840.1.113730.1.1");
1475             if (netscapeEx != null) {
1476                 DerInputStream in = new DerInputStream(netscapeEx);
1477                 byte[] encoded = in.getOctetString();
1478                 encoded = new DerValue(encoded).getUnalignedBitString()
1479                         .toByteArray();
1480 
1481                 NetscapeCertTypeExtension extn =
1482                         new NetscapeCertTypeExtension(encoded);
1483 
1484                 Boolean val = (Boolean)extn.get(
1485                         NetscapeCertTypeExtension.OBJECT_SIGNING);
1486                 if (!val) {
1487                     if (bad != null) {
1488                         bad[2] = true;
1489                     } else {
1490                         badNetscapeCertType = true;
1491                     }
1492                 }
1493             }
1494         } catch (IOException e) {
1495             //
1496         }
1497     }
1498 
1499     void getAliasInfo(String alias) {
1500 
1501         Key key = null;
1502 
1503         try {
1504 
1505             java.security.cert.Certificate[] cs = null;
1506 
1507             try {
1508                 cs = store.getCertificateChain(alias);
1509             } catch (KeyStoreException kse) {
1510                 // this never happens, because keystore has been loaded
1511             }
1512             if (cs == null) {
1513                 MessageFormat form = new MessageFormat(rb.getString
1514                     ("Certificate chain not found for: alias.  alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."));
1515                 Object[] source = {alias, alias};
1516                 error(form.format(source));
1517             }
1518 
1519             certChain = new X509Certificate[cs.length];
1520             for (int i=0; i<cs.length; i++) {
1521                 if (!(cs[i] instanceof X509Certificate)) {
1522                     error(rb.getString
1523                         ("found non-X.509 certificate in signer's chain"));
1524                 }
1525                 certChain[i] = (X509Certificate)cs[i];
1526             }
1527 
1528             // order the cert chain if necessary (put user cert first,
1529             // root-cert last in the chain)
1530             X509Certificate userCert
1531                 = (X509Certificate)store.getCertificate(alias);
1532 
1533             // check validity of signer certificate
1534             try {
1535                 userCert.checkValidity();
1536 
1537                 if (userCert.getNotAfter().getTime() <
1538                     System.currentTimeMillis() + SIX_MONTHS) {
1539 
1540                     hasExpiringCert = true;
1541                 }
1542             } catch (CertificateExpiredException cee) {
1543                 hasExpiredCert = true;
1544 
1545             } catch (CertificateNotYetValidException cnyve) {
1546                 notYetValidCert = true;
1547             }
1548 
1549             checkCertUsage(userCert, null);
1550 
1551             if (!userCert.equals(certChain[0])) {
1552                 // need to order ...
1553                 X509Certificate[] certChainTmp
1554                     = new X509Certificate[certChain.length];
1555                 certChainTmp[0] = userCert;
1556                 Principal issuer = userCert.getIssuerDN();
1557                 for (int i=1; i<certChain.length; i++) {
1558                     int j;
1559                     // look for the cert whose subject corresponds to the
1560                     // given issuer
1561                     for (j=0; j<certChainTmp.length; j++) {
1562                         if (certChainTmp[j] == null)
1563                             continue;
1564                         Principal subject = certChainTmp[j].getSubjectDN();
1565                         if (issuer.equals(subject)) {
1566                             certChain[i] = certChainTmp[j];
1567                             issuer = certChainTmp[j].getIssuerDN();
1568                             certChainTmp[j] = null;
1569                             break;
1570                         }
1571                     }
1572                     if (j == certChainTmp.length) {
1573                         error(rb.getString("incomplete certificate chain"));
1574                     }
1575 
1576                 }
1577                 certChain = certChainTmp; // ordered
1578             }
1579 
1580             try {
1581                 if (!token && keypass == null)
1582                     key = store.getKey(alias, storepass);
1583                 else
1584                     key = store.getKey(alias, keypass);
1585             } catch (UnrecoverableKeyException e) {
1586                 if (token) {
1587                     throw e;
1588                 } else if (keypass == null) {
1589                     // Did not work out, so prompt user for key password
1590                     MessageFormat form = new MessageFormat(rb.getString
1591                         ("Enter key password for alias: "));
1592                     Object[] source = {alias};
1593                     keypass = getPass(form.format(source));
1594                     key = store.getKey(alias, keypass);
1595                 }
1596             }
1597         } catch (NoSuchAlgorithmException e) {
1598             error(e.getMessage());
1599         } catch (UnrecoverableKeyException e) {
1600             error(rb.getString("unable to recover key from keystore"));
1601         } catch (KeyStoreException kse) {
1602             // this never happens, because keystore has been loaded
1603         }
1604 
1605         if (!(key instanceof PrivateKey)) {
1606             MessageFormat form = new MessageFormat(rb.getString
1607                 ("key associated with alias not a private key"));
1608             Object[] source = {alias};
1609             error(form.format(source));
1610         } else {
1611             privateKey = (PrivateKey)key;
1612         }
1613     }
1614 
1615     void error(String message)
1616     {
1617         System.out.println(rb.getString("jarsigner: ")+message);
1618         System.exit(1);
1619     }
1620 
1621 
1622     void error(String message, Exception e)
1623     {
1624         System.out.println(rb.getString("jarsigner: ")+message);
1625         if (debug) {
1626             e.printStackTrace();
1627         }
1628         System.exit(1);
1629     }
1630 
1631     char[] getPass(String prompt)
1632     {
1633         System.err.print(prompt);
1634         System.err.flush();
1635         try {
1636             char[] pass = Password.readPassword(System.in);
1637 
1638             if (pass == null) {
1639                 error(rb.getString("you must enter key password"));
1640             } else {
1641                 return pass;
1642             }
1643         } catch (IOException ioe) {
1644             error(rb.getString("unable to read password: ")+ioe.getMessage());
1645         }
1646         // this shouldn't happen
1647         return null;
1648     }
1649 
1650     /*
1651      * Reads all the bytes for a given zip entry.
1652      */
1653     private synchronized byte[] getBytes(ZipFile zf,
1654                                          ZipEntry ze) throws IOException {
1655         int n;
1656 
1657         InputStream is = null;
1658         try {
1659             is = zf.getInputStream(ze);
1660             baos.reset();
1661             long left = ze.getSize();
1662 
1663             while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
1664                 baos.write(buffer, 0, n);
1665                 left -= n;
1666             }
1667         } finally {
1668             if (is != null) {
1669                 is.close();
1670             }
1671         }
1672 
1673         return baos.toByteArray();
1674     }
1675 
1676     /*
1677      * Returns manifest entry from given jar file, or null if given jar file
1678      * does not have a manifest entry.
1679      */
1680     private ZipEntry getManifestFile(ZipFile zf) {
1681         ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
1682         if (ze == null) {
1683             // Check all entries for matching name
1684             Enumeration<? extends ZipEntry> enum_ = zf.entries();
1685             while (enum_.hasMoreElements() && ze == null) {
1686                 ze = enum_.nextElement();
1687                 if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
1688                     (ze.getName())) {
1689                     ze = null;
1690                 }
1691             }
1692         }
1693         return ze;
1694     }
1695 
1696     /*
1697      * Computes the digests of a zip entry, and returns them as an array
1698      * of base64-encoded strings.
1699      */
1700     private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
1701                                              MessageDigest[] digests,
1702                                              BASE64Encoder encoder)
1703         throws IOException {
1704 
1705         int n, i;
1706         InputStream is = null;
1707         try {
1708             is = zf.getInputStream(ze);
1709             long left = ze.getSize();
1710             while((left > 0)
1711                 && (n = is.read(buffer, 0, buffer.length)) != -1) {
1712                 for (i=0; i<digests.length; i++) {
1713                     digests[i].update(buffer, 0, n);
1714                 }
1715                 left -= n;
1716             }
1717         } finally {
1718             if (is != null) {
1719                 is.close();
1720             }
1721         }
1722 
1723         // complete the digests
1724         String[] base64Digests = new String[digests.length];
1725         for (i=0; i<digests.length; i++) {
1726             base64Digests[i] = encoder.encode(digests[i].digest());
1727         }
1728         return base64Digests;
1729     }
1730 
1731     /*
1732      * Computes the digests of a zip entry, and returns them as a list of
1733      * attributes
1734      */
1735     private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
1736                                            MessageDigest[] digests,
1737                                            BASE64Encoder encoder)
1738         throws IOException {
1739 
1740         String[] base64Digests = getDigests(ze, zf, digests, encoder);
1741         Attributes attrs = new Attributes();
1742 
1743         for (int i=0; i<digests.length; i++) {
1744             attrs.putValue(digests[i].getAlgorithm()+"-Digest",
1745                            base64Digests[i]);
1746         }
1747         return attrs;
1748     }
1749 
1750     /*
1751      * Updates the digest attributes of a manifest entry, by adding or
1752      * replacing digest values.
1753      * A digest value is added if the manifest entry does not contain a digest
1754      * for that particular algorithm.
1755      * A digest value is replaced if it is obsolete.
1756      *
1757      * Returns true if the manifest entry has been changed, and false
1758      * otherwise.
1759      */
1760     private boolean updateDigests(ZipEntry ze, ZipFile zf,
1761                                   MessageDigest[] digests,
1762                                   BASE64Encoder encoder,
1763                                   Manifest mf) throws IOException {
1764         boolean update = false;
1765 
1766         Attributes attrs = mf.getAttributes(ze.getName());
1767         String[] base64Digests = getDigests(ze, zf, digests, encoder);
1768 
1769         for (int i=0; i<digests.length; i++) {
1770             String name = digests[i].getAlgorithm()+"-Digest";
1771             String mfDigest = attrs.getValue(name);
1772             if (mfDigest == null
1773                 && digests[i].getAlgorithm().equalsIgnoreCase("SHA")) {
1774                 // treat "SHA" and "SHA1" the same
1775                 mfDigest = attrs.getValue("SHA-Digest");
1776             }
1777             if (mfDigest == null) {
1778                 // compute digest and add it to list of attributes
1779                 attrs.putValue(name, base64Digests[i]);
1780                 update=true;
1781             } else {
1782                 // compare digests, and replace the one in the manifest
1783                 // if they are different
1784                 if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
1785                     attrs.putValue(name, base64Digests[i]);
1786                     update=true;
1787                 }
1788             }
1789         }
1790         return update;
1791     }
1792 
1793     /*
1794      * Try to load the specified signing mechanism.
1795      * The URL class loader is used.
1796      */
1797     private ContentSigner loadSigningMechanism(String signerClassName,
1798         String signerClassPath) throws Exception {
1799 
1800         // construct class loader
1801         String cpString = null;   // make sure env.class.path defaults to dot
1802 
1803         // do prepends to get correct ordering
1804         cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);
1805         cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
1806         cpString = PathList.appendPath(signerClassPath, cpString);
1807         URL[] urls = PathList.pathToURLs(cpString);
1808         ClassLoader appClassLoader = new URLClassLoader(urls);
1809 
1810         // attempt to find signer
1811         Class signerClass = appClassLoader.loadClass(signerClassName);
1812 
1813         // Check that it implements ContentSigner
1814         Object signer = signerClass.newInstance();
1815         if (!(signer instanceof ContentSigner)) {
1816             MessageFormat form = new MessageFormat(
1817                 rb.getString("signerClass is not a signing mechanism"));
1818             Object[] source = {signerClass.getName()};
1819             throw new IllegalArgumentException(form.format(source));
1820         }
1821         return (ContentSigner)signer;
1822     }
1823 }
1824 
1825 /**
1826  * This is a BASE64Encoder that does not insert a default newline at the end of
1827  * every output line. This is necessary because java.util.jar does its own
1828  * line management (see Manifest.make72Safe()). Inserting additional new lines
1829  * can cause line-wrapping problems (see CR 6219522).
1830  */
1831 class JarBASE64Encoder extends BASE64Encoder {
1832     /**
1833      * Encode the suffix that ends every output line.
1834      */
1835     protected void encodeLineSuffix(OutputStream aStream) throws IOException { }
1836 }
1837 
1838 class SignatureFile {
1839 
1840     /** SignatureFile */
1841     Manifest sf;
1842 
1843     /** .SF base name */
1844     String baseName;
1845 
1846     public SignatureFile(MessageDigest digests[],
1847                          Manifest mf,
1848                          ManifestDigester md,
1849                          String baseName,
1850                          boolean signManifest)
1851 
1852     {
1853         this.baseName = baseName;
1854 
1855         String version = System.getProperty("java.version");
1856         String javaVendor = System.getProperty("java.vendor");
1857 
1858         sf = new Manifest();
1859         Attributes mattr = sf.getMainAttributes();
1860         BASE64Encoder encoder = new JarBASE64Encoder();
1861 
1862         mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
1863         mattr.putValue("Created-By", version + " (" + javaVendor + ")");
1864 
1865         if (signManifest) {
1866             // sign the whole manifest
1867             for (int i=0; i < digests.length; i++) {
1868                 mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
1869                                encoder.encode(md.manifestDigest(digests[i])));
1870             }
1871         }
1872 
1873         // create digest of the manifest main attributes
1874         ManifestDigester.Entry mde =
1875                 md.get(ManifestDigester.MF_MAIN_ATTRS, false);
1876         if (mde != null) {
1877             for (int i=0; i < digests.length; i++) {
1878                 mattr.putValue(digests[i].getAlgorithm() +
1879                         "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
1880                         encoder.encode(mde.digest(digests[i])));
1881             }
1882         } else {
1883             throw new IllegalStateException
1884                 ("ManifestDigester failed to create " +
1885                 "Manifest-Main-Attribute entry");
1886         }
1887 
1888         /* go through the manifest entries and create the digests */
1889 
1890         Map<String,Attributes> entries = sf.getEntries();
1891         Iterator<Map.Entry<String,Attributes>> mit =
1892                                 mf.getEntries().entrySet().iterator();
1893         while(mit.hasNext()) {
1894             Map.Entry<String,Attributes> e = mit.next();
1895             String name = e.getKey();
1896             mde = md.get(name, false);
1897             if (mde != null) {
1898                 Attributes attr = new Attributes();
1899                 for (int i=0; i < digests.length; i++) {
1900                     attr.putValue(digests[i].getAlgorithm()+"-Digest",
1901                                   encoder.encode(mde.digest(digests[i])));
1902                 }
1903                 entries.put(name, attr);
1904             }
1905         }
1906     }
1907 
1908     /**
1909      * Writes the SignatureFile to the specified OutputStream.
1910      *
1911      * @param out the output stream
1912      * @exception IOException if an I/O error has occurred
1913      */
1914 
1915     public void write(OutputStream out) throws IOException
1916     {
1917         sf.write(out);
1918     }
1919 
1920     /**
1921      * get .SF file name
1922      */
1923     public String getMetaName()
1924     {
1925         return "META-INF/"+ baseName + ".SF";
1926     }
1927 
1928     /**
1929      * get base file name
1930      */
1931     public String getBaseName()
1932     {
1933         return baseName;
1934     }
1935 
1936     /*
1937      * Generate a signed data block.
1938      * If a URL or a certificate (containing a URL) for a Timestamping
1939      * Authority is supplied then a signature timestamp is generated and
1940      * inserted into the signed data block.
1941      *
1942      * @param sigalg signature algorithm to use, or null to use default
1943      * @param tsaUrl The location of the Timestamping Authority. If null
1944      *               then no timestamp is requested.
1945      * @param tsaCert The certificate for the Timestamping Authority. If null
1946      *               then no timestamp is requested.
1947      * @param signingMechanism The signing mechanism to use.
1948      * @param args The command-line arguments to jarsigner.
1949      * @param zipFile The original source Zip file.
1950      */
1951     public Block generateBlock(PrivateKey privateKey,
1952                                String sigalg,
1953                                X509Certificate[] certChain,
1954                                boolean externalSF, String tsaUrl,
1955                                X509Certificate tsaCert,
1956                                ContentSigner signingMechanism,
1957                                String[] args, ZipFile zipFile)
1958         throws NoSuchAlgorithmException, InvalidKeyException, IOException,
1959             SignatureException, CertificateException
1960     {
1961         return new Block(this, privateKey, sigalg, certChain, externalSF,
1962                 tsaUrl, tsaCert, signingMechanism, args, zipFile);
1963     }
1964 
1965 
1966     public static class Block {
1967 
1968         private byte[] block;
1969         private String blockFileName;
1970 
1971         /*
1972          * Construct a new signature block.
1973          */
1974         Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
1975             X509Certificate[] certChain, boolean externalSF, String tsaUrl,
1976             X509Certificate tsaCert, ContentSigner signingMechanism,
1977             String[] args, ZipFile zipFile)
1978             throws NoSuchAlgorithmException, InvalidKeyException, IOException,
1979             SignatureException, CertificateException {
1980 
1981             Principal issuerName = certChain[0].getIssuerDN();
1982             if (!(issuerName instanceof X500Name)) {
1983                 // must extract the original encoded form of DN for subsequent
1984                 // name comparison checks (converting to a String and back to
1985                 // an encoded DN could cause the types of String attribute
1986                 // values to be changed)
1987                 X509CertInfo tbsCert = new
1988                     X509CertInfo(certChain[0].getTBSCertificate());
1989                 issuerName = (Principal)
1990                     tbsCert.get(CertificateIssuerName.NAME + "." +
1991                                 CertificateIssuerName.DN_NAME);
1992             }
1993             BigInteger serial = certChain[0].getSerialNumber();
1994 
1995             String digestAlgorithm;
1996             String signatureAlgorithm;
1997             String keyAlgorithm = privateKey.getAlgorithm();
1998             /*
1999              * If no signature algorithm was specified, we choose a
2000              * default that is compatible with the private key algorithm.
2001              */
2002             if (sigalg == null) {
2003 
2004                 if (keyAlgorithm.equalsIgnoreCase("DSA"))
2005                     digestAlgorithm = "SHA1";
2006                 else if (keyAlgorithm.equalsIgnoreCase("RSA"))
2007                     digestAlgorithm = "SHA1";
2008                 else {
2009                     throw new RuntimeException("private key is not a DSA or "
2010                                                + "RSA key");
2011                 }
2012                 signatureAlgorithm = digestAlgorithm + "with" + keyAlgorithm;
2013             } else {
2014                 signatureAlgorithm = sigalg;
2015             }
2016 
2017             // check common invalid key/signature algorithm combinations
2018             String sigAlgUpperCase = signatureAlgorithm.toUpperCase();
2019             if ((sigAlgUpperCase.endsWith("WITHRSA") &&
2020                 !keyAlgorithm.equalsIgnoreCase("RSA")) ||
2021                 (sigAlgUpperCase.endsWith("WITHDSA") &&
2022                 !keyAlgorithm.equalsIgnoreCase("DSA"))) {
2023                 throw new SignatureException
2024                     ("private key algorithm is not compatible with signature algorithm");
2025             }
2026 
2027             blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;
2028 
2029             AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);
2030             AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);
2031 
2032             Signature sig = Signature.getInstance(signatureAlgorithm);
2033             sig.initSign(privateKey);
2034 
2035             ByteArrayOutputStream baos = new ByteArrayOutputStream();
2036             sfg.write(baos);
2037 
2038             byte[] content = baos.toByteArray();
2039 
2040             sig.update(content);
2041             byte[] signature = sig.sign();
2042 
2043             // Timestamp the signature and generate the signature block file
2044             if (signingMechanism == null) {
2045                 signingMechanism = new TimestampedSigner();
2046             }
2047             URI tsaUri = null;
2048             try {
2049                 if (tsaUrl != null) {
2050                     tsaUri = new URI(tsaUrl);
2051                 }
2052             } catch (URISyntaxException e) {
2053                 IOException ioe = new IOException();
2054                 ioe.initCause(e);
2055                 throw ioe;
2056             }
2057 
2058             // Assemble parameters for the signing mechanism
2059             ContentSignerParameters params =
2060                 new JarSignerParameters(args, tsaUri, tsaCert, signature,
2061                     signatureAlgorithm, certChain, content, zipFile);
2062 
2063             // Generate the signature block
2064             block = signingMechanism.generateSignedData(
2065                     params, externalSF, (tsaUrl != null || tsaCert != null));
2066         }
2067 
2068         /*
2069          * get block file name.
2070          */
2071         public String getMetaName()
2072         {
2073             return blockFileName;
2074         }
2075 
2076         /**
2077          * Writes the block file to the specified OutputStream.
2078          *
2079          * @param out the output stream
2080          * @exception IOException if an I/O error has occurred
2081          */
2082 
2083         public void write(OutputStream out) throws IOException
2084         {
2085             out.write(block);
2086         }
2087     }
2088 }
2089 
2090 
2091 /*
2092  * This object encapsulates the parameters used to perform content signing.
2093  */
2094 class JarSignerParameters implements ContentSignerParameters {
2095 
2096     private String[] args;
2097     private URI tsa;
2098     private X509Certificate tsaCertificate;
2099     private byte[] signature;
2100     private String signatureAlgorithm;
2101     private X509Certificate[] signerCertificateChain;
2102     private byte[] content;
2103     private ZipFile source;
2104 
2105     /**
2106      * Create a new object.
2107      */
2108     JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
2109         byte[] signature, String signatureAlgorithm,
2110         X509Certificate[] signerCertificateChain, byte[] content,
2111         ZipFile source) {
2112 
2113         if (signature == null || signatureAlgorithm == null ||
2114             signerCertificateChain == null) {
2115             throw new NullPointerException();
2116         }
2117         this.args = args;
2118         this.tsa = tsa;
2119         this.tsaCertificate = tsaCertificate;
2120         this.signature = signature;
2121         this.signatureAlgorithm = signatureAlgorithm;
2122         this.signerCertificateChain = signerCertificateChain;
2123         this.content = content;
2124         this.source = source;
2125     }
2126 
2127     /**
2128      * Retrieves the command-line arguments.
2129      *
2130      * @return The command-line arguments. May be null.
2131      */
2132     public String[] getCommandLine() {
2133         return args;
2134     }
2135 
2136     /**
2137      * Retrieves the identifier for a Timestamping Authority (TSA).
2138      *
2139      * @return The TSA identifier. May be null.
2140      */
2141     public URI getTimestampingAuthority() {
2142         return tsa;
2143     }
2144 
2145     /**
2146      * Retrieves the certificate for a Timestamping Authority (TSA).
2147      *
2148      * @return The TSA certificate. May be null.
2149      */
2150     public X509Certificate getTimestampingAuthorityCertificate() {
2151         return tsaCertificate;
2152     }
2153 
2154     /**
2155      * Retrieves the signature.
2156      *
2157      * @return The non-null signature bytes.
2158      */
2159     public byte[] getSignature() {
2160         return signature;
2161     }
2162 
2163     /**
2164      * Retrieves the name of the signature algorithm.
2165      *
2166      * @return The non-null string name of the signature algorithm.
2167      */
2168     public String getSignatureAlgorithm() {
2169         return signatureAlgorithm;
2170     }
2171 
2172     /**
2173      * Retrieves the signer's X.509 certificate chain.
2174      *
2175      * @return The non-null array of X.509 public-key certificates.
2176      */
2177     public X509Certificate[] getSignerCertificateChain() {
2178         return signerCertificateChain;
2179     }
2180 
2181     /**
2182      * Retrieves the content that was signed.
2183      *
2184      * @return The content bytes. May be null.
2185      */
2186     public byte[] getContent() {
2187         return content;
2188     }
2189 
2190     /**
2191      * Retrieves the original source ZIP file before it was signed.
2192      *
2193      * @return The original ZIP file. May be null.
2194      */
2195     public ZipFile getSource() {
2196         return source;
2197     }
2198 }