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