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