JDK 11 jdk.jartool.jmod - JAR Tool

JDK 11 jdk.jartool.jmod is the JMOD file for JDK 11 JAR tool, which can be invoked by the "jar" command.

JDK 11 JAR tool compiled class files are stored in \fyicenter\jdk-11.0.1\jmods\jdk.jartool.jmod.

JDK 11 JAR tool compiled class files are also linked and stored in the \fyicenter\jdk-11.0.1\lib\modules JImage file.

JDK 11 JAR tool source code files are stored in \fyicenter\jdk-11.0.1\lib\src.zip\jdk.jartool.

You can click and view the content of each source code file in the list below.

✍: FYIcenter

sun/security/tools/jarsigner/Main.java

/*
 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package sun.security.tools.jarsigner;

import java.io.*;
import java.net.UnknownHostException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXBuilderParameters;
import java.util.*;
import java.util.stream.Collectors;
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 long ONE_YEAR = 366*24*60*60*1000L;

    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));

    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

    // 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);
    }

    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 hasExpiringTsaCert = false;
    private boolean noTimestamp = true;

    // Expiration date. The value could be null if signed by a trusted cert.
    private Date expireDate = null;
    private Date tsaExpireDate = null;

    // If there is a time stamp block inside the PKCS7 block file
    boolean hasTimestampBlock = false;


    // Severe warnings.

    // jarsigner used to check signer cert chain validity and key usages
    // itself and set various warnings. Later CertPath validation is
    // added but chainNotValidated is only flagged when no other existing
    // warnings are set. TSA cert chain check is added separately and
    // only tsaChainNotValidated is set, i.e. has no affect on hasExpiredCert,
    // notYetValidCert, or any badXyzUsage.

    private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg
    private boolean hasExpiredCert = false;
    private boolean hasExpiredTsaCert = false;
    private boolean notYetValidCert = false;
    private boolean chainNotValidated = false;
    private boolean tsaChainNotValidated = 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 Throwable tsaChainNotValidatedReason = null;

    private boolean seeWeak = false;

    PKIXBuilderParameters pkixParameters;
    Set<X509Certificate> trustedCerts = new HashSet<>();

    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
                    || hasExpiredTsaCert || notYetValidCert || signerSelfSigned) {
                exitCode |= 4;
            }
            if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
                exitCode |= 8;
            }
            if (hasUnsignedEntry) {
                exitCode |= 16;
            }
            if (notSignedByAlias || aliasNotInStore) {
                exitCode |= 32;
            }
            if (tsaChainNotValidated) {
                exitCode |= 64;
            }
            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, "-?") == 0 ||
                       collator.compare(flags, "-h") == 0 ||
                       collator.compare(flags, "--help") == 0 ||
                       // -help: legacy.
                       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.out.println(rb.getString
                (".print.this.help.message"));
        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. Please note aliasNotInStore
            // is always false when ~/.keystore is used.
            if (!aliasNotInStore && keystore != null) {
                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) {
                                hasTimestampBlock = true;
                                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 {
                displayMessagesAndResult(false);
            }
            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 void displayMessagesAndResult(boolean isSigning) {
        String result;
        List<String> errors = new ArrayList<>();
        List<String> warnings = new ArrayList<>();
        List<String> info = new ArrayList<>();

        boolean signerNotExpired = expireDate == null
                || expireDate.after(new Date());

        if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
                notYetValidCert || chainNotValidated || hasExpiredCert ||
                hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
                aliasNotInStore || notSignedByAlias ||
                tsaChainNotValidated ||
                (hasExpiredTsaCert && !signerNotExpired)) {

            if (strict) {
                result = rb.getString(isSigning
                        ? "jar.signed.with.signer.errors."
                        : "jar.verified.with.signer.errors.");
            } else {
                result = rb.getString(isSigning
                        ? "jar.signed."
                        : "jar.verified.");
            }

            if (badKeyUsage) {
                errors.add(rb.getString(isSigning
                        ? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."
                        : "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
            }

            if (badExtendedKeyUsage) {
                errors.add(rb.getString(isSigning
                        ? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."
                        : "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
            }

            if (badNetscapeCertType) {
                errors.add(rb.getString(isSigning
                        ? "The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."
                        : "This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
            }

            // only in verifying
            if (hasUnsignedEntry) {
                errors.add(rb.getString(
                        "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
            }
            if (hasExpiredCert) {
                errors.add(rb.getString(isSigning
                        ? "The.signer.certificate.has.expired."
                        : "This.jar.contains.entries.whose.signer.certificate.has.expired."));
            }
            if (notYetValidCert) {
                errors.add(rb.getString(isSigning
                        ? "The.signer.certificate.is.not.yet.valid."
                        : "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
            }

            if (chainNotValidated) {
                errors.add(String.format(rb.getString(isSigning
                                ? "The.signer.s.certificate.chain.is.invalid.reason.1"
                                : "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
                        chainNotValidatedReason.getLocalizedMessage()));
            }

            if (hasExpiredTsaCert) {
                errors.add(rb.getString("The.timestamp.has.expired."));
            }
            if (tsaChainNotValidated) {
                errors.add(String.format(rb.getString(isSigning
                                ? "The.tsa.certificate.chain.is.invalid.reason.1"
                                : "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),
                        tsaChainNotValidatedReason.getLocalizedMessage()));
            }

            // only in verifying
            if (notSignedByAlias) {
                errors.add(
                        rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
            }

            // only in verifying
            if (aliasNotInStore) {
                errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
            }

            if (signerSelfSigned) {
                errors.add(rb.getString(isSigning
                        ? "The.signer.s.certificate.is.self.signed."
                        : "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
            }

            // weakAlg only detected in signing. The jar file is
            // now simply treated unsigned in verifying.
            if ((weakAlg & 1) == 1) {
                errors.add(String.format(
                        rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
                        digestalg, "-digestalg"));
            }

            if ((weakAlg & 2) == 2) {
                errors.add(String.format(
                        rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
                        sigalg, "-sigalg"));
            }
            if ((weakAlg & 4) == 4) {
                errors.add(String.format(
                        rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
                        tSADigestAlg, "-tsadigestalg"));
            }
            if ((weakAlg & 8) == 8) {
                errors.add(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 {
            result = rb.getString(isSigning ? "jar.signed." : "jar.verified.");
        }

        if (hasExpiredTsaCert) {
            // No need to warn about expiring if already expired
            hasExpiringTsaCert = false;
        }

        if (hasExpiringCert ||
                (hasExpiringTsaCert  && expireDate != null) ||
                (noTimestamp && expireDate != null) ||
                (hasExpiredTsaCert && signerNotExpired)) {

            if (hasExpiredTsaCert && signerNotExpired) {
                if (expireDate != null) {
                    warnings.add(String.format(
                            rb.getString("The.timestamp.expired.1.but.usable.2"),
                            tsaExpireDate,
                            expireDate));
                }
                // Reset the flag so exit code is 0
                hasExpiredTsaCert = false;
            }
            if (hasExpiringCert) {
                warnings.add(rb.getString(isSigning
                        ? "The.signer.certificate.will.expire.within.six.months."
                        : "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
            }
            if (hasExpiringTsaCert && expireDate != null) {
                if (expireDate.after(tsaExpireDate)) {
                    warnings.add(String.format(rb.getString(
                            "The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate));
                } else {
                    warnings.add(String.format(rb.getString(
                            "The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate));
                }
            }
            if (noTimestamp && expireDate != null) {
                if (hasTimestampBlock) {
                    warnings.add(String.format(rb.getString(isSigning
                            ? "invalid.timestamp.signing"
                            : "bad.timestamp.verifying"), expireDate));
                } else {
                    warnings.add(String.format(rb.getString(isSigning
                            ? "no.timestamp.signing"
                            : "no.timestamp.verifying"), expireDate));
                }
            }
        }

        System.out.println(result);
        if (strict) {
            if (!errors.isEmpty()) {
                System.out.println();
                System.out.println(rb.getString("Error."));
                errors.forEach(System.out::println);
            }
            if (!warnings.isEmpty()) {
                System.out.println();
                System.out.println(rb.getString("Warning."));
                warnings.forEach(System.out::println);
            }
        } else {
            if (!errors.isEmpty() || !warnings.isEmpty()) {
                System.out.println();
                System.out.println(rb.getString("Warning."));
                errors.forEach(System.out::println);
                warnings.forEach(System.out::println);
            }
        }
        if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {
            if (! (verbose != null && showcerts)) {
                System.out.println();
                System.out.println(rb.getString(
                        "Re.run.with.the.verbose.and.certs.options.for.more.details."));
            }
        }

        if (isSigning || verbose != null) {
            // Always print out expireDate, unless expired or expiring.
            if (!hasExpiringCert && !hasExpiredCert
                    && expireDate != null && signerNotExpired) {
                info.add(String.format(rb.getString(
                        "The.signer.certificate.will.expire.on.1."), expireDate));
            }
            if (!noTimestamp) {
                if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) {
                    if (signerNotExpired) {
                        info.add(String.format(rb.getString(
                                "The.timestamp.will.expire.on.1."), tsaExpireDate));
                    } else {
                        info.add(String.format(rb.getString(
                                "signer.cert.expired.1.but.timestamp.good.2."),
                                expireDate,
                                tsaExpireDate));
                    }
                }
            }
        }

        if (!info.isEmpty()) {
            System.out.println();
            info.forEach(System.out::println);
        }
    }

    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;

    /**
     * Returns a string about a certificate:
     *
     * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
     * [<validity-period> | <expiry-warning>]
     * [<key-usage-warning>]
     *
     * Note: no newline character at the end.
     *
     * This method sets global flags like hasExpiringCert, hasExpiredCert,
     * notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType,
     * hasExpiringTsaCert, hasExpiredTsaCert.
     *
     * @param isTsCert true if c is in the TSA cert chain, false otherwise.
     * @param checkUsage true to check code signer keyUsage
     */
    String printCert(boolean isTsCert, String tab, Certificate c,
        Date timestamp, boolean checkUsage) throws Exception {

        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 (x509Cert != null) {

            certStr.append("\n").append(tab).append("[");

            if (trustedCerts.contains(x509Cert)) {
                certStr.append(rb.getString("trusted.certificate"));
            } else {
                Date notAfter = x509Cert.getNotAfter();
                try {
                    boolean printValidity = true;
                    if (isTsCert) {
                        if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) {
                            tsaExpireDate = notAfter;
                        }
                    } else {
                        if (expireDate == null || expireDate.after(notAfter)) {
                            expireDate = notAfter;
                        }
                    }
                    if (timestamp == null) {
                        x509Cert.checkValidity();
                        // test if cert will expire within six months (or one year for tsa)
                        long age = isTsCert ? ONE_YEAR : SIX_MONTHS;
                        if (notAfter.getTime() < System.currentTimeMillis() + age) {
                            if (isTsCert) {
                                hasExpiringTsaCert = true;
                            } else {
                                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) {
                    if (isTsCert) {
                        hasExpiredTsaCert = true;
                    } else {
                        hasExpiredCert = true;
                    }

                    if (expiredTimeForm == null) {
                        expiredTimeForm = new MessageFormat(
                                rb.getString("certificate.expired.on"));
                    }
                    Object[] source = {notAfter};
                    certStr.append(expiredTimeForm.format(source));

                } catch (CertificateNotYetValidException cnyve) {
                    if (!isTsCert) 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);
        }

        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(true, "", tsaCert, null, false));
                }
            }
            builder.tsa(tsaURI);
            if (tSADigestAlg != null) {
                builder.setProperty("tsaDigestAlg", tSADigestAlg);
            }

            if (tSAPolicyID != null) {
                builder.setProperty("tsaPolicyId", tSAPolicyID);
            }
        }

        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));

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(signedJarFile);
        } catch (IOException ioe) {
            error(rb.getString("unable.to.create.")+tmpJarName, ioe);
        }

        Throwable failedCause = null;
        String failedMessage = null;

        try {
            builder.build().sign(zipFile, fos);
        } catch (JarSignerException e) {
            failedCause = e.getCause();
            if (failedCause instanceof SocketTimeoutException
                    || failedCause instanceof UnknownHostException) {
                // Provide a helpful message when TSA is beyond a firewall
                failedMessage = 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> ";
            } else {
                // JarSignerException might have a null cause
                if (failedCause == null) {
                    failedCause = e;
                }
                failedMessage = rb.getString("unable.to.sign.jar.") + failedCause;
            }
        } catch (Exception e) {
            failedCause = e;
            failedMessage = rb.getString("unable.to.sign.jar.") + failedCause;
        } finally {
            // close the resources
            if (zipFile != null) {
                zipFile.close();
                zipFile = null;
            }

            if (fos != null) {
                fos.close();
            }

        }

        if (failedCause != null) {
            signedJarFile.delete();
            error(failedMessage, failedCause);
        }

        if (verbose != null) {
            System.out.println();
        }

        // The JarSigner API always accepts the timestamp received.
        // We need to extract the certs from the signed jar to
        // validate it.
        try (JarFile check = new JarFile(signedJarFile)) {
            PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry(
                    "META-INF/" + sigfile + "." + privateKey.getAlgorithm())));
            Timestamp ts = null;
            try {
                SignerInfo si = p7.getSignerInfos()[0];
                if (si.getTsToken() != null) {
                    hasTimestampBlock = true;
                }
                ts = si.getTimestamp();
            } catch (Exception e) {
                tsaChainNotValidated = true;
                tsaChainNotValidatedReason = e;
            }
            // Spaces before the ">>> Signer" and other lines are different
            String result = certsAndTSInfo("", "    ", Arrays.asList(certChain), ts);
            if (verbose != null) {
                System.out.println(result);
            }
        } catch (Exception e) {
            if (debug) {
                e.printStackTrace();
            }
        }

        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));
                }
            }
        }

        displayMessagesAndResult(true);
    }

    /**
     * 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 signer info, with a newline at the end.
     * Called by verifyJar().
     */
    private String signerInfo(CodeSigner signer, String tab) throws Exception {
        if (cacheForSignerInfo.containsKey(signer)) {
            return cacheForSignerInfo.get(signer);
        }
        List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
        // signing time is only displayed on verification
        Timestamp ts = signer.getTimestamp();
        String tsLine = "";
        if (ts != null) {
            tsLine = printTimestamp(tab, ts) + "\n";
        }
        // Spaces before the ">>> Signer" and other lines are the same.

        String result = certsAndTSInfo(tab, tab, certs, ts);
        cacheForSignerInfo.put(signer, tsLine + result);
        return result;
    }

    /**
     * Fills info on certs and timestamp into a StringBuilder, sets
     * warning flags (through printCert) and validates cert chains.
     *
     * @param tab1 spaces before the ">>> Signer" line
     * @param tab2 spaces before the other lines
     * @param certs the signer cert
     * @param ts the timestamp, can be null
     * @return the info as a string
     */
    private String certsAndTSInfo(
            String tab1,
            String tab2,
            List<? extends Certificate> certs, Timestamp ts)
            throws Exception {

        Date timestamp;
        if (ts != null) {
            timestamp = ts.getTimestamp();
            noTimestamp = false;
        } else {
            timestamp = null;
        }
        // display the certificate(sb). The first one is end-entity cert and
        // its KeyUsage should be checked.
        boolean first = true;
        StringBuilder sb = new StringBuilder();
        sb.append(tab1).append(rb.getString("...Signer")).append('\n');
        for (Certificate c : certs) {
            sb.append(printCert(false, tab2, c, timestamp, first));
            sb.append('\n');
            first = false;
        }
        try {
            validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts);
        } catch (Exception e) {
            chainNotValidated = true;
            chainNotValidatedReason = e;
            sb.append(tab2).append(rb.getString(".Invalid.certificate.chain."))
                    .append(e.getLocalizedMessage()).append("]\n");
        }
        if (ts != null) {
            sb.append(tab1).append(rb.getString("...TSA")).append('\n');
            for (Certificate c : ts.getSignerCertPath().getCertificates()) {
                sb.append(printCert(true, tab2, c, null, false));
                sb.append('\n');
            }
            try {
                validateCertChain(Validator.VAR_TSA_SERVER,
                        ts.getSignerCertPath().getCertificates(), null);
            } catch (Exception e) {
                tsaChainNotValidated = true;
                tsaChainNotValidatedReason = e;
                sb.append(tab2).append(rb.getString(".Invalid.TSA.certificate.chain."))
                        .append(e.getLocalizedMessage()).append("]\n");
            }
        }
        if (certs.size() == 1
                && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) {
            signerSelfSigned = true;
        }

        return sb.toString();
    }

    void loadKeyStore(String keyStoreName, boolean prompt) {

        if (!nullStream && keyStoreName == null) {
            keyStoreName = System.getProperty("user.home") + File.separator
                + ".keystore";
        }

        try {
            try {
                KeyStore caks = KeyStoreUtil.getCacertsKeyStore();
                if (caks != null) {
                    Enumeration<String> aliases = caks.aliases();
                    while (aliases.hasMoreElements()) {
                        String a = aliases.nextElement();
                        try {
                            trustedCerts.add((X509Certificate)caks.getCertificate(a));
                        } 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())) {
                            trustedCerts.add(c);
                        }
                    } catch (Exception e2) {
                        // ignore, when a SecretkeyEntry does not include a cert
                    }
                }
            } finally {
                try {
                    pkixParameters = new PKIXBuilderParameters(
                            trustedCerts.stream()
                                    .map(c -> new TrustAnchor(c, null))
                                    .collect(Collectors.toSet()),
                            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) {
            //
        }
    }

    // Called by signJar().
    void getAliasInfo(String alias) throws Exception {

        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];
            }

            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);
    }

    /**
     * Validates a cert chain.
     *
     * @param parameter this might be a timestamp
     */
    void validateCertChain(String variant, List<? extends Certificate> certs,
                           Timestamp parameter)
            throws Exception {
        try {
            Validator.getInstance(Validator.TYPE_PKIX,
                    variant,
                    pkixParameters)
                    .validate(certs.toArray(new X509Certificate[certs.size()]),
                            null, parameter);
        } catch (Exception e) {
            if (debug) {
                e.printStackTrace();
            }

            // Exception might be dismissed if another warning flag
            // is already set by printCert.

            if (variant.equals(Validator.VAR_TSA_SERVER) &&
                    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 &&
                            hasExpiredTsaCert)) {
                        // we already have hasExpiredTsaCert
                        return;
                    }
                }
            }

            if (variant.equals(Validator.VAR_CODE_SIGNING) &&
                    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;
    }
}

sun/security/tools/jarsigner/Main.java

 

JDK 11 jdk.javadoc.jmod - Java Document Tool

JDK 11 jdk.internal.vm.compiler.management.jmod - Internal VM Compiler Management Module

Download and Use JDK 11

⇑⇑ FAQ for JDK (Java Development Kit)

2020-07-22, 2864👍, 0💬