src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
changeset 47216 71c04702a3d5
parent 46139 5196af754957
child 47469 6ae08c311cd3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,2007 @@
+/*
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.tools.jarsigner;
+
+import java.io.*;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXBuilderParameters;
+import java.util.*;
+import java.util.zip.*;
+import java.util.jar.*;
+import java.net.URI;
+import java.text.Collator;
+import java.text.MessageFormat;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateException;
+import java.security.*;
+
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.TrustAnchor;
+import java.util.Map.Entry;
+
+import jdk.security.jarsigner.JarSigner;
+import jdk.security.jarsigner.JarSignerException;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.timestamp.TimestampToken;
+import sun.security.tools.KeyStoreUtil;
+import sun.security.validator.Validator;
+import sun.security.validator.ValidatorException;
+import sun.security.x509.*;
+import sun.security.util.*;
+
+
+/**
+ * <p>The jarsigner utility.
+ *
+ * The exit codes for the main method are:
+ *
+ * 0: success
+ * 1: any error that the jar cannot be signed or verified, including:
+ *      keystore loading error
+ *      TSP communication error
+ *      jarsigner command line error...
+ * otherwise: error codes from -strict
+ *
+ * @author Roland Schemers
+ * @author Jan Luehe
+ */
+public class Main {
+
+    // for i18n
+    private static final java.util.ResourceBundle rb =
+        java.util.ResourceBundle.getBundle
+        ("sun.security.tools.jarsigner.Resources");
+    private static final Collator collator = Collator.getInstance();
+    static {
+        // this is for case insensitive string comparisions
+        collator.setStrength(Collator.PRIMARY);
+    }
+
+    private static final String NONE = "NONE";
+    private static final String P11KEYSTORE = "PKCS11";
+
+    private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
+
+    private static final DisabledAlgorithmConstraints DISABLED_CHECK =
+            new DisabledAlgorithmConstraints(
+                    DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
+
+    private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections
+            .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
+    private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
+            .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
+    // Attention:
+    // This is the entry that get launched by the security tool jarsigner.
+    public static void main(String args[]) throws Exception {
+        Main js = new Main();
+        js.run(args);
+    }
+
+    static final String VERSION = "1.0";
+
+    static final int IN_KEYSTORE = 0x01;        // signer is in keystore
+    static final int NOT_ALIAS = 0x04;          // alias list is NOT empty and
+                                                // signer is not in alias list
+    static final int SIGNED_BY_ALIAS = 0x08;    // signer is in alias list
+
+    X509Certificate[] certChain;    // signer's cert chain (when composing)
+    PrivateKey privateKey;          // private key
+    KeyStore store;                 // the keystore specified by -keystore
+                                    // or the default keystore, never null
+
+    String keystore; // key store file
+    boolean nullStream = false; // null keystore input stream (NONE)
+    boolean token = false; // token-based keystore
+    String jarfile;  // jar files to sign or verify
+    String alias;    // alias to sign jar with
+    List<String> ckaliases = new ArrayList<>(); // aliases in -verify
+    char[] storepass; // keystore password
+    boolean protectedPath; // protected authentication path
+    String storetype; // keystore type
+    String providerName; // provider name
+    List<String> providers = null; // list of provider names
+    List<String> providerClasses = null; // list of provider classes
+    // arguments for provider constructors
+    HashMap<String,String> providerArgs = new HashMap<>();
+    char[] keypass; // private key password
+    String sigfile; // name of .SF file
+    String sigalg; // name of signature algorithm
+    String digestalg; // name of digest algorithm
+    String signedjar; // output filename
+    String tsaUrl; // location of the Timestamping Authority
+    String tsaAlias; // alias for the Timestamping Authority's certificate
+    String altCertChain; // file to read alternative cert chain from
+    String tSAPolicyID;
+    String tSADigestAlg;
+    boolean verify = false; // verify the jar
+    String verbose = null; // verbose output when signing/verifying
+    boolean showcerts = false; // show certs when verifying
+    boolean debug = false; // debug
+    boolean signManifest = true; // "sign" the whole manifest
+    boolean externalSF = true; // leave the .SF out of the PKCS7 block
+    boolean strict = false;  // treat warnings as error
+
+    // read zip entry raw bytes
+    private String altSignerClass = null;
+    private String altSignerClasspath = null;
+    private ZipFile zipFile = null;
+
+    // Informational warnings
+    private boolean hasExpiringCert = false;
+    private boolean noTimestamp = false;
+    private Date expireDate = new Date(0L);     // used in noTimestamp warning
+
+    // Severe warnings
+    private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg
+    private boolean hasExpiredCert = false;
+    private boolean notYetValidCert = false;
+    private boolean chainNotValidated = false;
+    private boolean notSignedByAlias = false;
+    private boolean aliasNotInStore = false;
+    private boolean hasUnsignedEntry = false;
+    private boolean badKeyUsage = false;
+    private boolean badExtendedKeyUsage = false;
+    private boolean badNetscapeCertType = false;
+    private boolean signerSelfSigned = false;
+
+    private Throwable chainNotValidatedReason = null;
+
+    private boolean seeWeak = false;
+
+    PKIXBuilderParameters pkixParameters;
+
+    public void run(String args[]) {
+        try {
+            args = parseArgs(args);
+
+            // Try to load and install the specified providers
+            if (providers != null) {
+                for (String provName: providers) {
+                    try {
+                        KeyStoreUtil.loadProviderByName(provName,
+                                providerArgs.get(provName));
+                        if (debug) {
+                            System.out.println("loadProviderByName: " + provName);
+                        }
+                    } catch (IllegalArgumentException e) {
+                        throw new Exception(String.format(rb.getString(
+                                "provider.name.not.found"), provName));
+                    }
+                }
+            }
+
+            if (providerClasses != null) {
+                ClassLoader cl = ClassLoader.getSystemClassLoader();
+                for (String provClass: providerClasses) {
+                    try {
+                        KeyStoreUtil.loadProviderByClass(provClass,
+                                providerArgs.get(provClass), cl);
+                        if (debug) {
+                            System.out.println("loadProviderByClass: " + provClass);
+                        }
+                    } catch (ClassCastException cce) {
+                        throw new Exception(String.format(rb.getString(
+                                "provclass.not.a.provider"), provClass));
+                    } catch (IllegalArgumentException e) {
+                        throw new Exception(String.format(rb.getString(
+                                "provider.class.not.found"), provClass), e.getCause());
+                    }
+                }
+            }
+
+            if (verify) {
+                try {
+                    loadKeyStore(keystore, false);
+                } catch (Exception e) {
+                    if ((keystore != null) || (storepass != null)) {
+                        System.out.println(rb.getString("jarsigner.error.") +
+                                        e.getMessage());
+                        if (debug) {
+                            e.printStackTrace();
+                        }
+                        System.exit(1);
+                    }
+                }
+                /*              if (debug) {
+                    SignatureFileVerifier.setDebug(true);
+                    ManifestEntryVerifier.setDebug(true);
+                }
+                */
+                verifyJar(jarfile);
+            } else {
+                loadKeyStore(keystore, true);
+                getAliasInfo(alias);
+
+                signJar(jarfile, alias);
+            }
+        } catch (Exception e) {
+            System.out.println(rb.getString("jarsigner.error.") + e);
+            if (debug) {
+                e.printStackTrace();
+            }
+            System.exit(1);
+        } finally {
+            // zero-out private key password
+            if (keypass != null) {
+                Arrays.fill(keypass, ' ');
+                keypass = null;
+            }
+            // zero-out keystore password
+            if (storepass != null) {
+                Arrays.fill(storepass, ' ');
+                storepass = null;
+            }
+        }
+
+        if (strict) {
+            int exitCode = 0;
+            if (weakAlg != 0 || chainNotValidated || hasExpiredCert || notYetValidCert || signerSelfSigned) {
+                exitCode |= 4;
+            }
+            if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
+                exitCode |= 8;
+            }
+            if (hasUnsignedEntry) {
+                exitCode |= 16;
+            }
+            if (notSignedByAlias || aliasNotInStore) {
+                exitCode |= 32;
+            }
+            if (exitCode != 0) {
+                System.exit(exitCode);
+            }
+        }
+    }
+
+    /*
+     * Parse command line arguments.
+     */
+    String[] parseArgs(String args[]) throws Exception {
+        /* parse flags */
+        int n = 0;
+
+        if (args.length == 0) fullusage();
+
+        String confFile = null;
+        String command = "-sign";
+        for (n=0; n < args.length; n++) {
+            if (collator.compare(args[n], "-verify") == 0) {
+                command = "-verify";
+            } else if (collator.compare(args[n], "-conf") == 0) {
+                if (n == args.length - 1) {
+                    usageNoArg();
+                }
+                confFile = args[++n];
+            }
+        }
+
+        if (confFile != null) {
+            args = KeyStoreUtil.expandArgs(
+                    "jarsigner", confFile, command, null, args);
+        }
+
+        debug = Arrays.stream(args).anyMatch(
+                x -> collator.compare(x, "-debug") == 0);
+
+        if (debug) {
+            // No need to localize debug output
+            System.out.println("Command line args: " +
+                    Arrays.toString(args));
+        }
+
+        for (n=0; n < args.length; n++) {
+
+            String flags = args[n];
+            String modifier = null;
+
+            if (flags.startsWith("-")) {
+                int pos = flags.indexOf(':');
+                if (pos > 0) {
+                    modifier = flags.substring(pos+1);
+                    flags = flags.substring(0, pos);
+                }
+            }
+
+            if (!flags.startsWith("-")) {
+                if (jarfile == null) {
+                    jarfile = flags;
+                } else {
+                    alias = flags;
+                    ckaliases.add(alias);
+                }
+            } else if (collator.compare(flags, "-conf") == 0) {
+                if (++n == args.length) usageNoArg();
+            } else if (collator.compare(flags, "-keystore") == 0) {
+                if (++n == args.length) usageNoArg();
+                keystore = args[n];
+            } else if (collator.compare(flags, "-storepass") ==0) {
+                if (++n == args.length) usageNoArg();
+                storepass = getPass(modifier, args[n]);
+            } else if (collator.compare(flags, "-storetype") ==0) {
+                if (++n == args.length) usageNoArg();
+                storetype = args[n];
+            } else if (collator.compare(flags, "-providerName") ==0) {
+                if (++n == args.length) usageNoArg();
+                providerName = args[n];
+            } else if (collator.compare(flags, "-provider") == 0 ||
+                        collator.compare(flags, "-providerClass") == 0) {
+                if (++n == args.length) usageNoArg();
+                if (providerClasses == null) {
+                    providerClasses = new ArrayList<>(3);
+                }
+                providerClasses.add(args[n]);
+
+                if (args.length > (n+1)) {
+                    flags = args[n+1];
+                    if (collator.compare(flags, "-providerArg") == 0) {
+                        if (args.length == (n+2)) usageNoArg();
+                        providerArgs.put(args[n], args[n+2]);
+                        n += 2;
+                    }
+                }
+            } else if (collator.compare(flags, "-addprovider") == 0) {
+                if (++n == args.length) usageNoArg();
+                if (providers == null) {
+                    providers = new ArrayList<>(3);
+                }
+                providers.add(args[n]);
+
+                if (args.length > (n+1)) {
+                    flags = args[n+1];
+                    if (collator.compare(flags, "-providerArg") == 0) {
+                        if (args.length == (n+2)) usageNoArg();
+                        providerArgs.put(args[n], args[n+2]);
+                        n += 2;
+                    }
+                }
+            } else if (collator.compare(flags, "-protected") ==0) {
+                protectedPath = true;
+            } else if (collator.compare(flags, "-certchain") ==0) {
+                if (++n == args.length) usageNoArg();
+                altCertChain = args[n];
+            } else if (collator.compare(flags, "-tsapolicyid") ==0) {
+                if (++n == args.length) usageNoArg();
+                tSAPolicyID = args[n];
+            } else if (collator.compare(flags, "-tsadigestalg") ==0) {
+                if (++n == args.length) usageNoArg();
+                tSADigestAlg = args[n];
+            } else if (collator.compare(flags, "-debug") ==0) {
+                // Already processed
+            } else if (collator.compare(flags, "-keypass") ==0) {
+                if (++n == args.length) usageNoArg();
+                keypass = getPass(modifier, args[n]);
+            } else if (collator.compare(flags, "-sigfile") ==0) {
+                if (++n == args.length) usageNoArg();
+                sigfile = args[n];
+            } else if (collator.compare(flags, "-signedjar") ==0) {
+                if (++n == args.length) usageNoArg();
+                signedjar = args[n];
+            } else if (collator.compare(flags, "-tsa") ==0) {
+                if (++n == args.length) usageNoArg();
+                tsaUrl = args[n];
+            } else if (collator.compare(flags, "-tsacert") ==0) {
+                if (++n == args.length) usageNoArg();
+                tsaAlias = args[n];
+            } else if (collator.compare(flags, "-altsigner") ==0) {
+                if (++n == args.length) usageNoArg();
+                altSignerClass = args[n];
+                System.err.println(
+                        rb.getString("This.option.is.deprecated") +
+                                "-altsigner");
+            } else if (collator.compare(flags, "-altsignerpath") ==0) {
+                if (++n == args.length) usageNoArg();
+                altSignerClasspath = args[n];
+                System.err.println(
+                        rb.getString("This.option.is.deprecated") +
+                                "-altsignerpath");
+            } else if (collator.compare(flags, "-sectionsonly") ==0) {
+                signManifest = false;
+            } else if (collator.compare(flags, "-internalsf") ==0) {
+                externalSF = false;
+            } else if (collator.compare(flags, "-verify") ==0) {
+                verify = true;
+            } else if (collator.compare(flags, "-verbose") ==0) {
+                verbose = (modifier != null) ? modifier : "all";
+            } else if (collator.compare(flags, "-sigalg") ==0) {
+                if (++n == args.length) usageNoArg();
+                sigalg = args[n];
+            } else if (collator.compare(flags, "-digestalg") ==0) {
+                if (++n == args.length) usageNoArg();
+                digestalg = args[n];
+            } else if (collator.compare(flags, "-certs") ==0) {
+                showcerts = true;
+            } else if (collator.compare(flags, "-strict") ==0) {
+                strict = true;
+            } else if (collator.compare(flags, "-h") == 0 ||
+                        collator.compare(flags, "-?") == 0 ||
+                        collator.compare(flags, "-help") == 0) {
+                fullusage();
+            } else {
+                System.err.println(
+                        rb.getString("Illegal.option.") + flags);
+                usage();
+            }
+        }
+
+        // -certs must always be specified with -verbose
+        if (verbose == null) showcerts = false;
+
+        if (jarfile == null) {
+            System.err.println(rb.getString("Please.specify.jarfile.name"));
+            usage();
+        }
+        if (!verify && alias == null) {
+            System.err.println(rb.getString("Please.specify.alias.name"));
+            usage();
+        }
+        if (!verify && ckaliases.size() > 1) {
+            System.err.println(rb.getString("Only.one.alias.can.be.specified"));
+            usage();
+        }
+
+        if (storetype == null) {
+            storetype = KeyStore.getDefaultType();
+        }
+        storetype = KeyStoreUtil.niceStoreTypeName(storetype);
+
+        try {
+            if (signedjar != null && new File(signedjar).getCanonicalPath().equals(
+                    new File(jarfile).getCanonicalPath())) {
+                signedjar = null;
+            }
+        } catch (IOException ioe) {
+            // File system error?
+            // Just ignore it.
+        }
+
+        if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
+                KeyStoreUtil.isWindowsKeyStore(storetype)) {
+            token = true;
+            if (keystore == null) {
+                keystore = NONE;
+            }
+        }
+
+        if (NONE.equals(keystore)) {
+            nullStream = true;
+        }
+
+        if (token && !nullStream) {
+            System.err.println(MessageFormat.format(rb.getString
+                (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
+            usage();
+        }
+
+        if (token && keypass != null) {
+            System.err.println(MessageFormat.format(rb.getString
+                (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));
+            usage();
+        }
+
+        if (protectedPath) {
+            if (storepass != null || keypass != null) {
+                System.err.println(rb.getString
+                        ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));
+                usage();
+            }
+        }
+        if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
+            if (storepass != null || keypass != null) {
+                System.err.println(rb.getString
+                        ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));
+                usage();
+            }
+        }
+        return args;
+    }
+
+    static char[] getPass(String modifier, String arg) {
+        char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);
+        if (output != null) return output;
+        usage();
+        return null;    // Useless, usage() already exit
+    }
+
+    static void usageNoArg() {
+        System.out.println(rb.getString("Option.lacks.argument"));
+        usage();
+    }
+
+    static void usage() {
+        System.out.println();
+        System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
+        System.exit(1);
+    }
+
+    static void fullusage() {
+        System.out.println(rb.getString
+                ("Usage.jarsigner.options.jar.file.alias"));
+        System.out.println(rb.getString
+                (".jarsigner.verify.options.jar.file.alias."));
+        System.out.println();
+        System.out.println(rb.getString
+                (".keystore.url.keystore.location"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".storepass.password.password.for.keystore.integrity"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".storetype.type.keystore.type"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".keypass.password.password.for.private.key.if.different."));
+        System.out.println();
+        System.out.println(rb.getString
+                (".certchain.file.name.of.alternative.certchain.file"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".sigfile.file.name.of.SF.DSA.file"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".signedjar.file.name.of.signed.JAR.file"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".digestalg.algorithm.name.of.digest.algorithm"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".sigalg.algorithm.name.of.signature.algorithm"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".verify.verify.a.signed.JAR.file"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".verbose.suboptions.verbose.output.when.signing.verifying."));
+        System.out.println(rb.getString
+                (".suboptions.can.be.all.grouped.or.summary"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".certs.display.certificates.when.verbose.and.verifying"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".tsa.url.location.of.the.Timestamping.Authority"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".internalsf.include.the.SF.file.inside.the.signature.block"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".sectionsonly.don.t.compute.hash.of.entire.manifest"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".protected.keystore.has.protected.authentication.path"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".providerName.name.provider.name"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".add.provider.option"));
+        System.out.println(rb.getString
+                (".providerArg.option.1"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".providerClass.option"));
+        System.out.println(rb.getString
+                (".providerArg.option.2"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".strict.treat.warnings.as.errors"));
+        System.out.println();
+        System.out.println(rb.getString
+                (".conf.url.specify.a.pre.configured.options.file"));
+        System.out.println();
+
+        System.exit(0);
+    }
+
+    void verifyJar(String jarName)
+        throws Exception
+    {
+        boolean anySigned = false;  // if there exists entry inside jar signed
+        JarFile jf = null;
+        Map<String,String> digestMap = new HashMap<>();
+        Map<String,PKCS7> sigMap = new HashMap<>();
+        Map<String,String> sigNameMap = new HashMap<>();
+        Map<String,String> unparsableSignatures = new HashMap<>();
+
+        try {
+            jf = new JarFile(jarName, true);
+            Vector<JarEntry> entriesVec = new Vector<>();
+            byte[] buffer = new byte[8192];
+
+            Enumeration<JarEntry> entries = jf.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry je = entries.nextElement();
+                entriesVec.addElement(je);
+                try (InputStream is = jf.getInputStream(je)) {
+                    String name = je.getName();
+                    if (signatureRelated(name)
+                            && SignatureFileVerifier.isBlockOrSF(name)) {
+                        String alias = name.substring(name.lastIndexOf('/') + 1,
+                                name.lastIndexOf('.'));
+                        try {
+                            if (name.endsWith(".SF")) {
+                                Manifest sf = new Manifest(is);
+                                boolean found = false;
+                                for (Object obj : sf.getMainAttributes().keySet()) {
+                                    String key = obj.toString();
+                                    if (key.endsWith("-Digest-Manifest")) {
+                                        digestMap.put(alias,
+                                                key.substring(0, key.length() - 16));
+                                        found = true;
+                                        break;
+                                    }
+                                }
+                                if (!found) {
+                                    unparsableSignatures.putIfAbsent(alias,
+                                        String.format(
+                                            rb.getString("history.unparsable"),
+                                            name));
+                                }
+                            } else {
+                                sigNameMap.put(alias, name);
+                                sigMap.put(alias, new PKCS7(is));
+                            }
+                        } catch (IOException ioe) {
+                            unparsableSignatures.putIfAbsent(alias, String.format(
+                                    rb.getString("history.unparsable"), name));
+                        }
+                    } else {
+                        while (is.read(buffer, 0, buffer.length) != -1) {
+                            // we just read. this will throw a SecurityException
+                            // if  a signature/digest check fails.
+                        }
+                    }
+                }
+            }
+
+            Manifest man = jf.getManifest();
+            boolean hasSignature = false;
+
+            // The map to record display info, only used when -verbose provided
+            //      key: signer info string
+            //      value: the list of files with common key
+            Map<String,List<String>> output = new LinkedHashMap<>();
+
+            if (man != null) {
+                if (verbose != null) System.out.println();
+                Enumeration<JarEntry> e = entriesVec.elements();
+
+                String tab = rb.getString("6SPACE");
+
+                while (e.hasMoreElements()) {
+                    JarEntry je = e.nextElement();
+                    String name = je.getName();
+
+                    hasSignature = hasSignature
+                            || SignatureFileVerifier.isBlockOrSF(name);
+
+                    CodeSigner[] signers = je.getCodeSigners();
+                    boolean isSigned = (signers != null);
+                    anySigned |= isSigned;
+                    hasUnsignedEntry |= !je.isDirectory() && !isSigned
+                                        && !signatureRelated(name);
+
+                    int inStoreWithAlias = inKeyStore(signers);
+
+                    boolean inStore = (inStoreWithAlias & IN_KEYSTORE) != 0;
+
+                    notSignedByAlias |= (inStoreWithAlias & NOT_ALIAS) != 0;
+                    if (keystore != null) {
+                        aliasNotInStore |= isSigned && !inStore;
+                    }
+
+                    // Only used when -verbose provided
+                    StringBuffer sb = null;
+                    if (verbose != null) {
+                        sb = new StringBuffer();
+                        boolean inManifest =
+                            ((man.getAttributes(name) != null) ||
+                             (man.getAttributes("./"+name) != null) ||
+                             (man.getAttributes("/"+name) != null));
+                        sb.append(isSigned ? rb.getString("s") : rb.getString("SPACE"))
+                                .append(inManifest ? rb.getString("m") : rb.getString("SPACE"))
+                                .append(inStore ? rb.getString("k") : rb.getString("SPACE"))
+                                .append((inStoreWithAlias & NOT_ALIAS) != 0 ? 'X' : ' ')
+                                .append(rb.getString("SPACE"));
+                        sb.append('|');
+                    }
+
+                    // When -certs provided, display info has extra empty
+                    // lines at the beginning and end.
+                    if (isSigned) {
+                        if (showcerts) sb.append('\n');
+                        for (CodeSigner signer: signers) {
+                            // signerInfo() must be called even if -verbose
+                            // not provided. The method updates various
+                            // warning flags.
+                            String si = signerInfo(signer, tab);
+                            if (showcerts) {
+                                sb.append(si);
+                                sb.append('\n');
+                            }
+                        }
+                    } else if (showcerts && !verbose.equals("all")) {
+                        // Print no info for unsigned entries when -verbose:all,
+                        // to be consistent with old behavior.
+                        if (signatureRelated(name)) {
+                            sb.append('\n')
+                                    .append(tab)
+                                    .append(rb
+                                            .getString(".Signature.related.entries."))
+                                    .append("\n\n");
+                        } else {
+                            sb.append('\n').append(tab)
+                                    .append(rb.getString(".Unsigned.entries."))
+                                    .append("\n\n");
+                        }
+                    }
+
+                    if (verbose != null) {
+                        String label = sb.toString();
+                        if (signatureRelated(name)) {
+                            // Entries inside META-INF and other unsigned
+                            // entries are grouped separately.
+                            label = "-" + label;
+                        }
+
+                        // The label finally contains 2 parts separated by '|':
+                        // The legend displayed before the entry names, and
+                        // the cert info (if -certs specified).
+
+                        if (!output.containsKey(label)) {
+                            output.put(label, new ArrayList<String>());
+                        }
+
+                        StringBuilder fb = new StringBuilder();
+                        String s = Long.toString(je.getSize());
+                        for (int i = 6 - s.length(); i > 0; --i) {
+                            fb.append(' ');
+                        }
+                        fb.append(s).append(' ').
+                                append(new Date(je.getTime()).toString());
+                        fb.append(' ').append(name);
+
+                        output.get(label).add(fb.toString());
+                    }
+                }
+            }
+            if (verbose != null) {
+                for (Entry<String,List<String>> s: output.entrySet()) {
+                    List<String> files = s.getValue();
+                    String key = s.getKey();
+                    if (key.charAt(0) == '-') { // the signature-related group
+                        key = key.substring(1);
+                    }
+                    int pipe = key.indexOf('|');
+                    if (verbose.equals("all")) {
+                        for (String f: files) {
+                            System.out.println(key.substring(0, pipe) + f);
+                            System.out.printf(key.substring(pipe+1));
+                        }
+                    } else {
+                        if (verbose.equals("grouped")) {
+                            for (String f: files) {
+                                System.out.println(key.substring(0, pipe) + f);
+                            }
+                        } else if (verbose.equals("summary")) {
+                            System.out.print(key.substring(0, pipe));
+                            if (files.size() > 1) {
+                                System.out.println(files.get(0) + " " +
+                                        String.format(rb.getString(
+                                        ".and.d.more."), files.size()-1));
+                            } else {
+                                System.out.println(files.get(0));
+                            }
+                        }
+                        System.out.printf(key.substring(pipe+1));
+                    }
+                }
+                System.out.println();
+                System.out.println(rb.getString(
+                    ".s.signature.was.verified."));
+                System.out.println(rb.getString(
+                    ".m.entry.is.listed.in.manifest"));
+                System.out.println(rb.getString(
+                    ".k.at.least.one.certificate.was.found.in.keystore"));
+                if (ckaliases.size() > 0) {
+                    System.out.println(rb.getString(
+                        ".X.not.signed.by.specified.alias.es."));
+                }
+            }
+            if (man == null) {
+                System.out.println();
+                System.out.println(rb.getString("no.manifest."));
+            }
+
+            // If signer is a trusted cert or private entry in user's own
+            // keystore, it can be self-signed.
+            if (!aliasNotInStore) {
+                signerSelfSigned = false;
+            }
+
+            // Even if the verbose option is not specified, all out strings
+            // must be generated so seeWeak can be updated.
+            if (!digestMap.isEmpty()
+                    || !sigMap.isEmpty()
+                    || !unparsableSignatures.isEmpty()) {
+                if (verbose != null) {
+                    System.out.println();
+                }
+                for (String s : sigMap.keySet()) {
+                    if (!digestMap.containsKey(s)) {
+                        unparsableSignatures.putIfAbsent(s, String.format(
+                                rb.getString("history.nosf"), s));
+                    }
+                }
+                for (String s : digestMap.keySet()) {
+                    PKCS7 p7 = sigMap.get(s);
+                    if (p7 != null) {
+                        String history;
+                        try {
+                            SignerInfo si = p7.getSignerInfos()[0];
+                            X509Certificate signer = si.getCertificate(p7);
+                            String digestAlg = digestMap.get(s);
+                            String sigAlg = AlgorithmId.makeSigAlg(
+                                    si.getDigestAlgorithmId().getName(),
+                                    si.getDigestEncryptionAlgorithmId().getName());
+                            PublicKey key = signer.getPublicKey();
+                            PKCS7 tsToken = si.getTsToken();
+                            if (tsToken != null) {
+                                SignerInfo tsSi = tsToken.getSignerInfos()[0];
+                                X509Certificate tsSigner = tsSi.getCertificate(tsToken);
+                                byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
+                                TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
+                                PublicKey tsKey = tsSigner.getPublicKey();
+                                String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
+                                String tsSigAlg = AlgorithmId.makeSigAlg(
+                                        tsSi.getDigestAlgorithmId().getName(),
+                                        tsSi.getDigestEncryptionAlgorithmId().getName());
+                                Calendar c = Calendar.getInstance(
+                                        TimeZone.getTimeZone("UTC"),
+                                        Locale.getDefault(Locale.Category.FORMAT));
+                                c.setTime(tsTokenInfo.getDate());
+                                history = String.format(
+                                        rb.getString("history.with.ts"),
+                                        signer.getSubjectX500Principal(),
+                                        withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
+                                        withWeak(sigAlg, SIG_PRIMITIVE_SET),
+                                        withWeak(key),
+                                        c,
+                                        tsSigner.getSubjectX500Principal(),
+                                        withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET),
+                                        withWeak(tsSigAlg, SIG_PRIMITIVE_SET),
+                                        withWeak(tsKey));
+                            } else {
+                                history = String.format(
+                                        rb.getString("history.without.ts"),
+                                        signer.getSubjectX500Principal(),
+                                        withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
+                                        withWeak(sigAlg, SIG_PRIMITIVE_SET),
+                                        withWeak(key));
+                            }
+                        } catch (Exception e) {
+                            // The only usage of sigNameMap, remember the name
+                            // of the block file if it's invalid.
+                            history = String.format(
+                                    rb.getString("history.unparsable"),
+                                    sigNameMap.get(s));
+                        }
+                        if (verbose != null) {
+                            System.out.println(history);
+                        }
+                    } else {
+                        unparsableSignatures.putIfAbsent(s, String.format(
+                                rb.getString("history.nobk"), s));
+                    }
+                }
+                if (verbose != null) {
+                    for (String s : unparsableSignatures.keySet()) {
+                        System.out.println(unparsableSignatures.get(s));
+                    }
+                }
+            }
+            System.out.println();
+            if (!anySigned) {
+                if (seeWeak) {
+                    if (verbose != null) {
+                        System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
+                        System.out.println("\n  " +
+                                DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +
+                                "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));
+                    } else {
+                        System.out.println(rb.getString("jar.treated.unsigned.see.weak"));
+                    }
+                } else if (hasSignature) {
+                    System.out.println(rb.getString("jar.treated.unsigned"));
+                } else {
+                    System.out.println(rb.getString("jar.is.unsigned"));
+                }
+            } else {
+                boolean warningAppeared = false;
+                boolean errorAppeared = false;
+                if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
+                        notYetValidCert || chainNotValidated || hasExpiredCert ||
+                        hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
+                        aliasNotInStore || notSignedByAlias) {
+
+                    if (strict) {
+                        System.out.println(rb.getString("jar.verified.with.signer.errors."));
+                        System.out.println();
+                        System.out.println(rb.getString("Error."));
+                        errorAppeared = true;
+                    } else {
+                        System.out.println(rb.getString("jar.verified."));
+                        System.out.println();
+                        System.out.println(rb.getString("Warning."));
+                        warningAppeared = true;
+                    }
+
+                    if (weakAlg != 0) {
+                        // In fact, jarsigner verification did not catch this
+                        // since it has not read the JarFile content itself.
+                        // Everything is done with JarFile API. The signing
+                        // history (digestMap etc) will show these info and
+                        // print out proper warnings.
+                    }
+
+                    if (badKeyUsage) {
+                        System.out.println(
+                            rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
+                    }
+
+                    if (badExtendedKeyUsage) {
+                        System.out.println(
+                            rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
+                    }
+
+                    if (badNetscapeCertType) {
+                        System.out.println(
+                            rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
+                    }
+
+                    if (hasUnsignedEntry) {
+                        System.out.println(rb.getString(
+                            "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
+                    }
+                    if (hasExpiredCert) {
+                        System.out.println(rb.getString(
+                            "This.jar.contains.entries.whose.signer.certificate.has.expired."));
+                    }
+                    if (notYetValidCert) {
+                        System.out.println(rb.getString(
+                            "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
+                    }
+
+                    if (chainNotValidated) {
+                        System.out.println(String.format(
+                                rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1"),
+                                chainNotValidatedReason.getLocalizedMessage()));
+                    }
+
+                    if (notSignedByAlias) {
+                        System.out.println(
+                                rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
+                    }
+
+                    if (aliasNotInStore) {
+                        System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
+                    }
+
+                    if (signerSelfSigned) {
+                        System.out.println(rb.getString(
+                                "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
+                    }
+                } else {
+                    System.out.println(rb.getString("jar.verified."));
+                }
+                if (hasExpiringCert || noTimestamp) {
+                    if (!warningAppeared) {
+                        System.out.println();
+                        System.out.println(rb.getString("Warning."));
+                        warningAppeared = true;
+                    }
+                    if (hasExpiringCert) {
+                        System.out.println(rb.getString(
+                                "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
+                    }
+                    if (noTimestamp) {
+                        System.out.println(
+                                String.format(rb.getString("no.timestamp.verifying"), expireDate));
+                    }
+                }
+                if (warningAppeared || errorAppeared) {
+                    if (! (verbose != null && showcerts)) {
+                        System.out.println();
+                        System.out.println(rb.getString(
+                                "Re.run.with.the.verbose.and.certs.options.for.more.details."));
+                    }
+                }
+            }
+            return;
+        } catch (Exception e) {
+            System.out.println(rb.getString("jarsigner.") + e);
+            if (debug) {
+                e.printStackTrace();
+            }
+        } finally { // close the resource
+            if (jf != null) {
+                jf.close();
+            }
+        }
+
+        System.exit(1);
+    }
+
+    private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) {
+        if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
+            return alg;
+        } else {
+            seeWeak = true;
+            return String.format(rb.getString("with.weak"), alg);
+        }
+    }
+
+    private String withWeak(PublicKey key) {
+        if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
+            int kLen = KeyUtil.getKeySize(key);
+            if (kLen >= 0) {
+                return String.format(rb.getString("key.bit"), kLen);
+            } else {
+                return rb.getString("unknown.size");
+            }
+        } else {
+            seeWeak = true;
+            return String.format(
+                    rb.getString("key.bit.weak"), KeyUtil.getKeySize(key));
+        }
+    }
+
+    private static MessageFormat validityTimeForm = null;
+    private static MessageFormat notYetTimeForm = null;
+    private static MessageFormat expiredTimeForm = null;
+    private static MessageFormat expiringTimeForm = null;
+
+    /*
+     * Display some details about a certificate:
+     *
+     * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
+     * [<validity-period> | <expiry-warning>]
+     *
+     * Note: no newline character at the end
+     */
+    String printCert(String tab, Certificate c, boolean checkValidityPeriod,
+        Date timestamp, boolean checkUsage) {
+
+        StringBuilder certStr = new StringBuilder();
+        String space = rb.getString("SPACE");
+        X509Certificate x509Cert = null;
+
+        if (c instanceof X509Certificate) {
+            x509Cert = (X509Certificate) c;
+            certStr.append(tab).append(x509Cert.getType())
+                .append(rb.getString("COMMA"))
+                .append(x509Cert.getSubjectDN().getName());
+        } else {
+            certStr.append(tab).append(c.getType());
+        }
+
+        String alias = storeHash.get(c);
+        if (alias != null) {
+            certStr.append(space).append(alias);
+        }
+
+        if (checkValidityPeriod && x509Cert != null) {
+
+            certStr.append("\n").append(tab).append("[");
+            Date notAfter = x509Cert.getNotAfter();
+            try {
+                boolean printValidity = true;
+                if (timestamp == null) {
+                    if (expireDate.getTime() == 0 || expireDate.after(notAfter)) {
+                        expireDate = notAfter;
+                    }
+                    x509Cert.checkValidity();
+                    // test if cert will expire within six months
+                    if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) {
+                        hasExpiringCert = true;
+                        if (expiringTimeForm == null) {
+                            expiringTimeForm = new MessageFormat(
+                                rb.getString("certificate.will.expire.on"));
+                        }
+                        Object[] source = { notAfter };
+                        certStr.append(expiringTimeForm.format(source));
+                        printValidity = false;
+                    }
+                } else {
+                    x509Cert.checkValidity(timestamp);
+                }
+                if (printValidity) {
+                    if (validityTimeForm == null) {
+                        validityTimeForm = new MessageFormat(
+                            rb.getString("certificate.is.valid.from"));
+                    }
+                    Object[] source = { x509Cert.getNotBefore(), notAfter };
+                    certStr.append(validityTimeForm.format(source));
+                }
+            } catch (CertificateExpiredException cee) {
+                hasExpiredCert = true;
+
+                if (expiredTimeForm == null) {
+                    expiredTimeForm = new MessageFormat(
+                        rb.getString("certificate.expired.on"));
+                }
+                Object[] source = { notAfter };
+                certStr.append(expiredTimeForm.format(source));
+
+            } catch (CertificateNotYetValidException cnyve) {
+                notYetValidCert = true;
+
+                if (notYetTimeForm == null) {
+                    notYetTimeForm = new MessageFormat(
+                        rb.getString("certificate.is.not.valid.until"));
+                }
+                Object[] source = { x509Cert.getNotBefore() };
+                certStr.append(notYetTimeForm.format(source));
+            }
+            certStr.append("]");
+
+            if (checkUsage) {
+                boolean[] bad = new boolean[3];
+                checkCertUsage(x509Cert, bad);
+                if (bad[0] || bad[1] || bad[2]) {
+                    String x = "";
+                    if (bad[0]) {
+                        x ="KeyUsage";
+                    }
+                    if (bad[1]) {
+                        if (x.length() > 0) x = x + ", ";
+                        x = x + "ExtendedKeyUsage";
+                    }
+                    if (bad[2]) {
+                        if (x.length() > 0) x = x + ", ";
+                        x = x + "NetscapeCertType";
+                    }
+                    certStr.append("\n").append(tab)
+                        .append(MessageFormat.format(rb.getString(
+                        ".{0}.extension.does.not.support.code.signing."), x));
+                }
+            }
+        }
+        return certStr.toString();
+    }
+
+    private static MessageFormat signTimeForm = null;
+
+    private String printTimestamp(String tab, Timestamp timestamp) {
+
+        if (signTimeForm == null) {
+            signTimeForm =
+                new MessageFormat(rb.getString("entry.was.signed.on"));
+        }
+        Object[] source = { timestamp.getTimestamp() };
+
+        return new StringBuilder().append(tab).append("[")
+            .append(signTimeForm.format(source)).append("]").toString();
+    }
+
+    private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>();
+
+    private int inKeyStoreForOneSigner(CodeSigner signer) {
+        if (cacheForInKS.containsKey(signer)) {
+            return cacheForInKS.get(signer);
+        }
+
+        int result = 0;
+        List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
+        for (Certificate c : certs) {
+            String alias = storeHash.get(c);
+            if (alias != null) {
+                if (alias.startsWith("(")) {
+                    result |= IN_KEYSTORE;
+                }
+                if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
+                    result |= SIGNED_BY_ALIAS;
+                }
+            } else {
+                if (store != null) {
+                    try {
+                        alias = store.getCertificateAlias(c);
+                    } catch (KeyStoreException kse) {
+                        // never happens, because keystore has been loaded
+                    }
+                    if (alias != null) {
+                        storeHash.put(c, "(" + alias + ")");
+                        result |= IN_KEYSTORE;
+                    }
+                }
+                if (ckaliases.contains(alias)) {
+                    result |= SIGNED_BY_ALIAS;
+                }
+            }
+        }
+        cacheForInKS.put(signer, result);
+        return result;
+    }
+
+    Hashtable<Certificate, String> storeHash = new Hashtable<>();
+
+    int inKeyStore(CodeSigner[] signers) {
+
+        if (signers == null)
+            return 0;
+
+        int output = 0;
+
+        for (CodeSigner signer: signers) {
+            int result = inKeyStoreForOneSigner(signer);
+            output |= result;
+        }
+        if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
+            output |= NOT_ALIAS;
+        }
+        return output;
+    }
+
+    void signJar(String jarName, String alias)
+            throws Exception {
+
+        if (digestalg != null && !DISABLED_CHECK.permits(
+                DIGEST_PRIMITIVE_SET, digestalg, null)) {
+            weakAlg |= 1;
+        }
+        if (tSADigestAlg != null && !DISABLED_CHECK.permits(
+                DIGEST_PRIMITIVE_SET, tSADigestAlg, null)) {
+            weakAlg |= 4;
+        }
+        if (sigalg != null && !DISABLED_CHECK.permits(
+                SIG_PRIMITIVE_SET , sigalg, null)) {
+            weakAlg |= 2;
+        }
+        if (!DISABLED_CHECK.permits(
+                SIG_PRIMITIVE_SET, privateKey)) {
+            weakAlg |= 8;
+        }
+
+        boolean aliasUsed = false;
+        X509Certificate tsaCert = null;
+
+        if (sigfile == null) {
+            sigfile = alias;
+            aliasUsed = true;
+        }
+
+        if (sigfile.length() > 8) {
+            sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH);
+        } else {
+            sigfile = sigfile.toUpperCase(Locale.ENGLISH);
+        }
+
+        StringBuilder tmpSigFile = new StringBuilder(sigfile.length());
+        for (int j = 0; j < sigfile.length(); j++) {
+            char c = sigfile.charAt(j);
+            if (!
+                    ((c>= 'A' && c<= 'Z') ||
+                            (c>= '0' && c<= '9') ||
+                            (c == '-') ||
+                            (c == '_'))) {
+                if (aliasUsed) {
+                    // convert illegal characters from the alias to be _'s
+                    c = '_';
+                } else {
+                    throw new
+                            RuntimeException(rb.getString
+                            ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));
+                }
+            }
+            tmpSigFile.append(c);
+        }
+
+        sigfile = tmpSigFile.toString();
+
+        String tmpJarName;
+        if (signedjar == null) tmpJarName = jarName+".sig";
+        else tmpJarName = signedjar;
+
+        File jarFile = new File(jarName);
+        File signedJarFile = new File(tmpJarName);
+
+        // Open the jar (zip) file
+        try {
+            zipFile = new ZipFile(jarName);
+        } catch (IOException ioe) {
+            error(rb.getString("unable.to.open.jar.file.")+jarName, ioe);
+        }
+
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(signedJarFile);
+        } catch (IOException ioe) {
+            error(rb.getString("unable.to.create.")+tmpJarName, ioe);
+        }
+
+        CertPath cp = CertificateFactory.getInstance("X.509")
+                .generateCertPath(Arrays.asList(certChain));
+        JarSigner.Builder builder = new JarSigner.Builder(privateKey, cp);
+
+        if (verbose != null) {
+            builder.eventHandler((action, file) -> {
+                System.out.println(rb.getString("." + action + ".") + file);
+            });
+        }
+
+        if (digestalg != null) {
+            builder.digestAlgorithm(digestalg);
+        }
+        if (sigalg != null) {
+            builder.signatureAlgorithm(sigalg);
+        }
+
+        URI tsaURI = null;
+
+        if (tsaUrl != null) {
+            tsaURI = new URI(tsaUrl);
+        } else if (tsaAlias != null) {
+            tsaCert = getTsaCert(tsaAlias);
+            tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);
+        }
+
+        if (tsaURI != null) {
+            if (verbose != null) {
+                System.out.println(
+                        rb.getString("requesting.a.signature.timestamp"));
+                if (tsaUrl != null) {
+                    System.out.println(rb.getString("TSA.location.") + tsaUrl);
+                } else if (tsaCert != null) {
+                    System.out.println(rb.getString("TSA.certificate.") +
+                            printCert("", tsaCert, false, null, false));
+                }
+            }
+            builder.tsa(tsaURI);
+            if (tSADigestAlg != null) {
+                builder.setProperty("tsaDigestAlg", tSADigestAlg);
+            }
+
+            if (tSAPolicyID != null) {
+                builder.setProperty("tsaPolicyId", tSAPolicyID);
+            }
+        } else {
+            noTimestamp = true;
+        }
+
+        if (altSignerClass != null) {
+            builder.setProperty("altSigner", altSignerClass);
+            if (verbose != null) {
+                System.out.println(
+                        rb.getString("using.an.alternative.signing.mechanism"));
+            }
+        }
+
+        if (altSignerClasspath != null) {
+            builder.setProperty("altSignerPath", altSignerClasspath);
+        }
+
+        builder.signerName(sigfile);
+
+        builder.setProperty("sectionsOnly", Boolean.toString(!signManifest));
+        builder.setProperty("internalSF", Boolean.toString(!externalSF));
+
+        try {
+            builder.build().sign(zipFile, fos);
+        } catch (JarSignerException e) {
+            Throwable cause = e.getCause();
+            if (cause != null && cause instanceof SocketTimeoutException) {
+                // Provide a helpful message when TSA is beyond a firewall
+                error(rb.getString("unable.to.sign.jar.") +
+                        rb.getString("no.response.from.the.Timestamping.Authority.") +
+                        "\n  -J-Dhttp.proxyHost=<hostname>" +
+                        "\n  -J-Dhttp.proxyPort=<portnumber>\n" +
+                        rb.getString("or") +
+                        "\n  -J-Dhttps.proxyHost=<hostname> " +
+                        "\n  -J-Dhttps.proxyPort=<portnumber> ", e);
+            } else {
+                error(rb.getString("unable.to.sign.jar.")+e.getCause(), e.getCause());
+            }
+        } finally {
+            // close the resouces
+            if (zipFile != null) {
+                zipFile.close();
+                zipFile = null;
+            }
+
+            if (fos != null) {
+                fos.close();
+            }
+        }
+
+        // no IOException thrown in the follow try clause, so disable
+        // the try clause.
+        // try {
+            if (signedjar == null) {
+                // attempt an atomic rename. If that fails,
+                // rename the original jar file, then the signed
+                // one, then delete the original.
+                if (!signedJarFile.renameTo(jarFile)) {
+                    File origJar = new File(jarName+".orig");
+
+                    if (jarFile.renameTo(origJar)) {
+                        if (signedJarFile.renameTo(jarFile)) {
+                            origJar.delete();
+                        } else {
+                            MessageFormat form = new MessageFormat(rb.getString
+                        ("attempt.to.rename.signedJarFile.to.jarFile.failed"));
+                            Object[] source = {signedJarFile, jarFile};
+                            error(form.format(source));
+                        }
+                    } else {
+                        MessageFormat form = new MessageFormat(rb.getString
+                            ("attempt.to.rename.jarFile.to.origJar.failed"));
+                        Object[] source = {jarFile, origJar};
+                        error(form.format(source));
+                    }
+                }
+            }
+
+            boolean warningAppeared = false;
+            if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
+                    notYetValidCert || chainNotValidated || hasExpiredCert || signerSelfSigned) {
+                if (strict) {
+                    System.out.println(rb.getString("jar.signed.with.signer.errors."));
+                    System.out.println();
+                    System.out.println(rb.getString("Error."));
+                } else {
+                    System.out.println(rb.getString("jar.signed."));
+                    System.out.println();
+                    System.out.println(rb.getString("Warning."));
+                    warningAppeared = true;
+                }
+
+                if (badKeyUsage) {
+                    System.out.println(
+                        rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
+                }
+
+                if (badExtendedKeyUsage) {
+                    System.out.println(
+                        rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
+                }
+
+                if (badNetscapeCertType) {
+                    System.out.println(
+                        rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
+                }
+
+                if (hasExpiredCert) {
+                    System.out.println(
+                        rb.getString("The.signer.certificate.has.expired."));
+                } else if (notYetValidCert) {
+                    System.out.println(
+                        rb.getString("The.signer.certificate.is.not.yet.valid."));
+                }
+
+                if (chainNotValidated) {
+                    System.out.println(String.format(
+                            rb.getString("The.signer.s.certificate.chain.is.not.validated.reason.1"),
+                            chainNotValidatedReason.getLocalizedMessage()));
+                }
+
+                if (signerSelfSigned) {
+                    System.out.println(
+                            rb.getString("The.signer.s.certificate.is.self.signed."));
+                }
+
+                if ((weakAlg & 1) == 1) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
+                            digestalg, "-digestalg"));
+                }
+
+                if ((weakAlg & 2) == 2) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
+                            sigalg, "-sigalg"));
+                }
+                if ((weakAlg & 4) == 4) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
+                            tSADigestAlg, "-tsadigestalg"));
+                }
+                if ((weakAlg & 8) == 8) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."),
+                            privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
+                }
+            } else {
+                System.out.println(rb.getString("jar.signed."));
+            }
+            if (hasExpiringCert || noTimestamp) {
+                if (!warningAppeared) {
+                    System.out.println();
+                    System.out.println(rb.getString("Warning."));
+                }
+
+                if (hasExpiringCert) {
+                    System.out.println(
+                            rb.getString("The.signer.certificate.will.expire.within.six.months."));
+                }
+
+                if (noTimestamp) {
+                    System.out.println(
+                            String.format(rb.getString("no.timestamp.signing"), expireDate));
+                }
+            }
+
+        // no IOException thrown in the above try clause, so disable
+        // the catch clause.
+        // } catch(IOException ioe) {
+        //     error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
+        // }
+    }
+
+    /**
+     * signature-related files include:
+     * . META-INF/MANIFEST.MF
+     * . META-INF/SIG-*
+     * . META-INF/*.SF
+     * . META-INF/*.DSA
+     * . META-INF/*.RSA
+     * . META-INF/*.EC
+     */
+    private boolean signatureRelated(String name) {
+        return SignatureFileVerifier.isSigningRelated(name);
+    }
+
+    Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();
+
+    /**
+     * Returns a string of singer info, with a newline at the end
+     */
+    private String signerInfo(CodeSigner signer, String tab) {
+        if (cacheForSignerInfo.containsKey(signer)) {
+            return cacheForSignerInfo.get(signer);
+        }
+        StringBuilder sb = new StringBuilder();
+        List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
+        // display the signature timestamp, if present
+        Date timestamp;
+        Timestamp ts = signer.getTimestamp();
+        if (ts != null) {
+            sb.append(printTimestamp(tab, ts));
+            sb.append('\n');
+            timestamp = ts.getTimestamp();
+        } else {
+            timestamp = null;
+            noTimestamp = true;
+        }
+        // display the certificate(sb). The first one is end-entity cert and
+        // its KeyUsage should be checked.
+        boolean first = true;
+        for (Certificate c : certs) {
+            sb.append(printCert(tab, c, true, timestamp, first));
+            sb.append('\n');
+            first = false;
+        }
+        try {
+            validateCertChain(certs);
+        } catch (Exception e) {
+            chainNotValidated = true;
+            chainNotValidatedReason = e;
+            sb.append(tab).append(rb.getString(".CertPath.not.validated."))
+                    .append(e.getLocalizedMessage()).append("]\n"); // TODO
+        }
+        if (certs.size() == 1
+                && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) {
+            signerSelfSigned = true;
+        }
+        String result = sb.toString();
+        cacheForSignerInfo.put(signer, result);
+        return result;
+    }
+
+    void loadKeyStore(String keyStoreName, boolean prompt) {
+
+        if (!nullStream && keyStoreName == null) {
+            keyStoreName = System.getProperty("user.home") + File.separator
+                + ".keystore";
+        }
+
+        try {
+            Set<TrustAnchor> tas = new HashSet<>();
+            try {
+                KeyStore caks = KeyStoreUtil.getCacertsKeyStore();
+                if (caks != null) {
+                    Enumeration<String> aliases = caks.aliases();
+                    while (aliases.hasMoreElements()) {
+                        String a = aliases.nextElement();
+                        try {
+                            tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
+                        } catch (Exception e2) {
+                            // ignore, when a SecretkeyEntry does not include a cert
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // Ignore, if cacerts cannot be loaded
+            }
+
+            if (providerName == null) {
+                store = KeyStore.getInstance(storetype);
+            } else {
+                store = KeyStore.getInstance(storetype, providerName);
+            }
+
+            // Get pass phrase
+            // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z
+            // and on NT call ??
+            if (token && storepass == null && !protectedPath
+                    && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
+                storepass = getPass
+                        (rb.getString("Enter.Passphrase.for.keystore."));
+            } else if (!token && storepass == null && prompt) {
+                storepass = getPass
+                        (rb.getString("Enter.Passphrase.for.keystore."));
+            }
+
+            try {
+                if (nullStream) {
+                    store.load(null, storepass);
+                } else {
+                    keyStoreName = keyStoreName.replace(File.separatorChar, '/');
+                    URL url = null;
+                    try {
+                        url = new URL(keyStoreName);
+                    } catch (java.net.MalformedURLException e) {
+                        // try as file
+                        url = new File(keyStoreName).toURI().toURL();
+                    }
+                    InputStream is = null;
+                    try {
+                        is = url.openStream();
+                        store.load(is, storepass);
+                    } finally {
+                        if (is != null) {
+                            is.close();
+                        }
+                    }
+                }
+                Enumeration<String> aliases = store.aliases();
+                while (aliases.hasMoreElements()) {
+                    String a = aliases.nextElement();
+                    try {
+                        X509Certificate c = (X509Certificate)store.getCertificate(a);
+                        // Only add TrustedCertificateEntry and self-signed
+                        // PrivateKeyEntry
+                        if (store.isCertificateEntry(a) ||
+                                c.getSubjectDN().equals(c.getIssuerDN())) {
+                            tas.add(new TrustAnchor(c, null));
+                        }
+                    } catch (Exception e2) {
+                        // ignore, when a SecretkeyEntry does not include a cert
+                    }
+                }
+            } finally {
+                try {
+                    pkixParameters = new PKIXBuilderParameters(tas, null);
+                    pkixParameters.setRevocationEnabled(false);
+                } catch (InvalidAlgorithmParameterException ex) {
+                    // Only if tas is empty
+                }
+            }
+        } catch (IOException ioe) {
+            throw new RuntimeException(rb.getString("keystore.load.") +
+                                        ioe.getMessage());
+        } catch (java.security.cert.CertificateException ce) {
+            throw new RuntimeException(rb.getString("certificate.exception.") +
+                                        ce.getMessage());
+        } catch (NoSuchProviderException pe) {
+            throw new RuntimeException(rb.getString("keystore.load.") +
+                                        pe.getMessage());
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new RuntimeException(rb.getString("keystore.load.") +
+                                        nsae.getMessage());
+        } catch (KeyStoreException kse) {
+            throw new RuntimeException
+                (rb.getString("unable.to.instantiate.keystore.class.") +
+                kse.getMessage());
+        }
+    }
+
+    X509Certificate getTsaCert(String alias) {
+
+        java.security.cert.Certificate cs = null;
+
+        try {
+            cs = store.getCertificate(alias);
+        } catch (KeyStoreException kse) {
+            // this never happens, because keystore has been loaded
+        }
+        if (cs == null || (!(cs instanceof X509Certificate))) {
+            MessageFormat form = new MessageFormat(rb.getString
+                ("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the"));
+            Object[] source = {alias, alias};
+            error(form.format(source));
+        }
+        return (X509Certificate) cs;
+    }
+
+    /**
+     * Check if userCert is designed to be a code signer
+     * @param userCert the certificate to be examined
+     * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
+     *            NetscapeCertType has codeSigning flag turned on.
+     *            If null, the class field badKeyUsage, badExtendedKeyUsage,
+     *            badNetscapeCertType will be set.
+     */
+    void checkCertUsage(X509Certificate userCert, boolean[] bad) {
+
+        // Can act as a signer?
+        // 1. if KeyUsage, then [0:digitalSignature] or
+        //    [1:nonRepudiation] should be true
+        // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
+        // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
+        // 1,2,3 must be true
+
+        if (bad != null) {
+            bad[0] = bad[1] = bad[2] = false;
+        }
+
+        boolean[] keyUsage = userCert.getKeyUsage();
+        if (keyUsage != null) {
+            keyUsage = Arrays.copyOf(keyUsage, 9);
+            if (!keyUsage[0] && !keyUsage[1]) {
+                if (bad != null) {
+                    bad[0] = true;
+                    badKeyUsage = true;
+                }
+            }
+        }
+
+        try {
+            List<String> xKeyUsage = userCert.getExtendedKeyUsage();
+            if (xKeyUsage != null) {
+                if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
+                        && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) {  // codeSigning
+                    if (bad != null) {
+                        bad[1] = true;
+                        badExtendedKeyUsage = true;
+                    }
+                }
+            }
+        } catch (java.security.cert.CertificateParsingException e) {
+            // shouldn't happen
+        }
+
+        try {
+            // OID_NETSCAPE_CERT_TYPE
+            byte[] netscapeEx = userCert.getExtensionValue
+                    ("2.16.840.1.113730.1.1");
+            if (netscapeEx != null) {
+                DerInputStream in = new DerInputStream(netscapeEx);
+                byte[] encoded = in.getOctetString();
+                encoded = new DerValue(encoded).getUnalignedBitString()
+                        .toByteArray();
+
+                NetscapeCertTypeExtension extn =
+                        new NetscapeCertTypeExtension(encoded);
+
+                Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING);
+                if (!val) {
+                    if (bad != null) {
+                        bad[2] = true;
+                        badNetscapeCertType = true;
+                    }
+                }
+            }
+        } catch (IOException e) {
+            //
+        }
+    }
+
+    void getAliasInfo(String alias) {
+
+        Key key = null;
+
+        try {
+            java.security.cert.Certificate[] cs = null;
+            if (altCertChain != null) {
+                try (FileInputStream fis = new FileInputStream(altCertChain)) {
+                    cs = CertificateFactory.getInstance("X.509").
+                            generateCertificates(fis).
+                            toArray(new Certificate[0]);
+                } catch (FileNotFoundException ex) {
+                    error(rb.getString("File.specified.by.certchain.does.not.exist"));
+                } catch (CertificateException | IOException ex) {
+                    error(rb.getString("Cannot.restore.certchain.from.file.specified"));
+                }
+            } else {
+                try {
+                    cs = store.getCertificateChain(alias);
+                } catch (KeyStoreException kse) {
+                    // this never happens, because keystore has been loaded
+                }
+            }
+            if (cs == null || cs.length == 0) {
+                if (altCertChain != null) {
+                    error(rb.getString
+                            ("Certificate.chain.not.found.in.the.file.specified."));
+                } else {
+                    MessageFormat form = new MessageFormat(rb.getString
+                        ("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and"));
+                    Object[] source = {alias, alias};
+                    error(form.format(source));
+                }
+            }
+
+            certChain = new X509Certificate[cs.length];
+            for (int i=0; i<cs.length; i++) {
+                if (!(cs[i] instanceof X509Certificate)) {
+                    error(rb.getString
+                        ("found.non.X.509.certificate.in.signer.s.chain"));
+                }
+                certChain[i] = (X509Certificate)cs[i];
+            }
+
+            // We don't meant to print anything, the next call
+            // checks validity and keyUsage etc
+            printCert("", certChain[0], true, null, true);
+
+            try {
+                validateCertChain(Arrays.asList(certChain));
+            } catch (Exception e) {
+                chainNotValidated = true;
+                chainNotValidatedReason = e;
+            }
+
+            if (KeyStoreUtil.isSelfSigned(certChain[0])) {
+                signerSelfSigned = true;
+            }
+
+            try {
+                if (!token && keypass == null)
+                    key = store.getKey(alias, storepass);
+                else
+                    key = store.getKey(alias, keypass);
+            } catch (UnrecoverableKeyException e) {
+                if (token) {
+                    throw e;
+                } else if (keypass == null) {
+                    // Did not work out, so prompt user for key password
+                    MessageFormat form = new MessageFormat(rb.getString
+                        ("Enter.key.password.for.alias."));
+                    Object[] source = {alias};
+                    keypass = getPass(form.format(source));
+                    key = store.getKey(alias, keypass);
+                }
+            }
+        } catch (NoSuchAlgorithmException e) {
+            error(e.getMessage());
+        } catch (UnrecoverableKeyException e) {
+            error(rb.getString("unable.to.recover.key.from.keystore"));
+        } catch (KeyStoreException kse) {
+            // this never happens, because keystore has been loaded
+        }
+
+        if (!(key instanceof PrivateKey)) {
+            MessageFormat form = new MessageFormat(rb.getString
+                ("key.associated.with.alias.not.a.private.key"));
+            Object[] source = {alias};
+            error(form.format(source));
+        } else {
+            privateKey = (PrivateKey)key;
+        }
+    }
+
+    void error(String message) {
+        System.out.println(rb.getString("jarsigner.")+message);
+        System.exit(1);
+    }
+
+
+    void error(String message, Throwable e) {
+        System.out.println(rb.getString("jarsigner.")+message);
+        if (debug) {
+            e.printStackTrace();
+        }
+        System.exit(1);
+    }
+
+    void validateCertChain(List<? extends Certificate> certs) throws Exception {
+        try {
+            Validator.getInstance(Validator.TYPE_PKIX,
+                    Validator.VAR_CODE_SIGNING,
+                    pkixParameters)
+                    .validate(certs.toArray(new X509Certificate[certs.size()]));
+        } catch (Exception e) {
+            if (debug) {
+                e.printStackTrace();
+            }
+            if (e instanceof ValidatorException) {
+                // Throw cause if it's CertPathValidatorException,
+                if (e.getCause() != null &&
+                        e.getCause() instanceof CertPathValidatorException) {
+                    e = (Exception) e.getCause();
+                    Throwable t = e.getCause();
+                    if ((t instanceof CertificateExpiredException &&
+                                hasExpiredCert) ||
+                            (t instanceof CertificateNotYetValidException &&
+                                    notYetValidCert)) {
+                        // we already have hasExpiredCert and notYetValidCert
+                        return;
+                    }
+                }
+                if (e instanceof ValidatorException) {
+                    ValidatorException ve = (ValidatorException)e;
+                    if (ve.getErrorType() == ValidatorException.T_EE_EXTENSIONS &&
+                            (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType)) {
+                        // We already have badKeyUsage, badExtendedKeyUsage
+                        // and badNetscapeCertType
+                        return;
+                    }
+                }
+            }
+            throw e;
+        }
+    }
+
+    char[] getPass(String prompt) {
+        System.err.print(prompt);
+        System.err.flush();
+        try {
+            char[] pass = Password.readPassword(System.in);
+
+            if (pass == null) {
+                error(rb.getString("you.must.enter.key.password"));
+            } else {
+                return pass;
+            }
+        } catch (IOException ioe) {
+            error(rb.getString("unable.to.read.password.")+ioe.getMessage());
+        }
+        // this shouldn't happen
+        return null;
+    }
+}