Path: blob/master/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
66649 views
/*1* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.tools.jarsigner;2627import java.io.*;28import java.net.UnknownHostException;29import java.security.cert.CertPathValidatorException;30import java.security.cert.PKIXBuilderParameters;31import java.util.*;32import java.util.stream.Collectors;33import java.util.zip.*;34import java.util.jar.*;35import java.net.URI;36import java.text.Collator;37import java.text.MessageFormat;38import java.security.cert.Certificate;39import java.security.cert.X509Certificate;40import java.security.cert.CertificateException;41import java.security.*;4243import java.net.SocketTimeoutException;44import java.net.URL;45import java.security.cert.CertPath;46import java.security.cert.CertificateExpiredException;47import java.security.cert.CertificateFactory;48import java.security.cert.CertificateNotYetValidException;49import java.security.cert.TrustAnchor;50import java.util.Map.Entry;5152import jdk.internal.access.JavaUtilZipFileAccess;53import jdk.internal.access.SharedSecrets;54import jdk.security.jarsigner.JarSigner;55import jdk.security.jarsigner.JarSignerException;56import sun.security.pkcs.PKCS7;57import sun.security.pkcs.SignerInfo;58import sun.security.provider.certpath.CertPathConstraintsParameters;59import sun.security.timestamp.TimestampToken;60import sun.security.tools.KeyStoreUtil;61import sun.security.validator.Validator;62import sun.security.validator.ValidatorException;63import sun.security.x509.*;64import sun.security.util.*;656667/**68* <p>The jarsigner utility.69*70* The exit codes for the main method are:71*72* 0: success73* 1: any error that the jar cannot be signed or verified, including:74* keystore loading error75* TSP communication error76* jarsigner command line error...77* otherwise: error codes from -strict78*79* @author Roland Schemers80* @author Jan Luehe81*/82public class Main {8384// for i18n85private static final java.util.ResourceBundle rb =86java.util.ResourceBundle.getBundle87("sun.security.tools.jarsigner.Resources");88private static final Collator collator = Collator.getInstance();89static {90// this is for case insensitive string comparisions91collator.setStrength(Collator.PRIMARY);92}9394private static final String NONE = "NONE";95private static final String P11KEYSTORE = "PKCS11";9697private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds98private static final long ONE_YEAR = 366*24*60*60*1000L;99100private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =101new DisabledAlgorithmConstraints(102DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);103104private static final DisabledAlgorithmConstraints CERTPATH_DISABLED_CHECK =105new DisabledAlgorithmConstraints(106DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);107108private static final DisabledAlgorithmConstraints LEGACY_CHECK =109new DisabledAlgorithmConstraints(110DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);111112private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections113.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));114private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections115.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));116117private static boolean extraAttrsDetected;118119static final String VERSION = "1.0";120121static final int IN_KEYSTORE = 0x01; // signer is in keystore122static final int NOT_ALIAS = 0x04; // alias list is NOT empty and123// signer is not in alias list124static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list125126static final JavaUtilZipFileAccess JUZFA = SharedSecrets.getJavaUtilZipFileAccess();127128// Attention:129// This is the entry that get launched by the security tool jarsigner.130public static void main(String args[]) throws Exception {131Main js = new Main();132js.run(args);133}134135X509Certificate[] certChain; // signer's cert chain (when composing)136PrivateKey privateKey; // private key137KeyStore store; // the keystore specified by -keystore138// or the default keystore, never null139140String keystore; // key store file141boolean nullStream = false; // null keystore input stream (NONE)142boolean token = false; // token-based keystore143String jarfile; // jar files to sign or verify144String alias; // alias to sign jar with145List<String> ckaliases = new ArrayList<>(); // aliases in -verify146char[] storepass; // keystore password147boolean protectedPath; // protected authentication path148String storetype; // keystore type149String providerName; // provider name150List<String> providers = null; // list of provider names151List<String> providerClasses = null; // list of provider classes152// arguments for provider constructors153HashMap<String,String> providerArgs = new HashMap<>();154char[] keypass; // private key password155String sigfile; // name of .SF file156String sigalg; // name of signature algorithm157String digestalg; // name of digest algorithm158String signedjar; // output filename159String tsaUrl; // location of the Timestamping Authority160String tsaAlias; // alias for the Timestamping Authority's certificate161String altCertChain; // file to read alternative cert chain from162String tSAPolicyID;163String tSADigestAlg;164boolean verify = false; // verify the jar165String verbose = null; // verbose output when signing/verifying166boolean showcerts = false; // show certs when verifying167boolean debug = false; // debug168boolean signManifest = true; // "sign" the whole manifest169boolean externalSF = true; // leave the .SF out of the PKCS7 block170boolean strict = false; // treat warnings as error171boolean revocationCheck = false; // Revocation check flag172173// read zip entry raw bytes174private String altSignerClass = null;175private String altSignerClasspath = null;176private ZipFile zipFile = null;177178// Informational warnings179private boolean hasExpiringCert = false;180private boolean hasExpiringTsaCert = false;181private boolean noTimestamp = true;182183// Expiration date. The value could be null if signed by a trusted cert.184private Date expireDate = null;185private Date tsaExpireDate = null;186187// If there is a time stamp block inside the PKCS7 block file188boolean hasTimestampBlock = false;189190private PublicKey weakPublicKey = null;191private boolean disabledAlgFound = false;192private String legacyDigestAlg = null;193private String legacyTsaDigestAlg = null;194private String legacySigAlg = null;195196// Severe warnings.197198// jarsigner used to check signer cert chain validity and key usages199// itself and set various warnings. Later CertPath validation is200// added but chainNotValidated is only flagged when no other existing201// warnings are set. TSA cert chain check is added separately and202// only tsaChainNotValidated is set, i.e. has no affect on hasExpiredCert,203// notYetValidCert, or any badXyzUsage.204205private int legacyAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg, 8. key206private int disabledAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg, 8. key207private boolean hasExpiredCert = false;208private boolean hasExpiredTsaCert = false;209private boolean notYetValidCert = false;210private boolean chainNotValidated = false;211private boolean tsaChainNotValidated = false;212private boolean notSignedByAlias = false;213private boolean aliasNotInStore = false;214private boolean hasUnsignedEntry = false;215private boolean badKeyUsage = false;216private boolean badExtendedKeyUsage = false;217private boolean badNetscapeCertType = false;218private boolean signerSelfSigned = false;219220private Throwable chainNotValidatedReason = null;221private Throwable tsaChainNotValidatedReason = null;222223PKIXBuilderParameters pkixParameters;224Set<X509Certificate> trustedCerts = new HashSet<>();225226public void run(String args[]) {227try {228args = parseArgs(args);229230// Try to load and install the specified providers231if (providers != null) {232for (String provName: providers) {233try {234KeyStoreUtil.loadProviderByName(provName,235providerArgs.get(provName));236if (debug) {237System.out.println("loadProviderByName: " + provName);238}239} catch (IllegalArgumentException e) {240throw new Exception(String.format(rb.getString(241"provider.name.not.found"), provName));242}243}244}245246if (providerClasses != null) {247ClassLoader cl = ClassLoader.getSystemClassLoader();248for (String provClass: providerClasses) {249try {250KeyStoreUtil.loadProviderByClass(provClass,251providerArgs.get(provClass), cl);252if (debug) {253System.out.println("loadProviderByClass: " + provClass);254}255} catch (ClassCastException cce) {256throw new Exception(String.format(rb.getString(257"provclass.not.a.provider"), provClass));258} catch (IllegalArgumentException e) {259throw new Exception(String.format(rb.getString(260"provider.class.not.found"), provClass), e.getCause());261}262}263}264265if (verify) {266try {267loadKeyStore(keystore, false);268} catch (Exception e) {269if ((keystore != null) || (storepass != null)) {270System.out.println(rb.getString("jarsigner.error.") +271e.getMessage());272if (debug) {273e.printStackTrace();274}275System.exit(1);276}277}278/* if (debug) {279SignatureFileVerifier.setDebug(true);280ManifestEntryVerifier.setDebug(true);281}282*/283verifyJar(jarfile);284} else {285loadKeyStore(keystore, true);286getAliasInfo(alias);287288signJar(jarfile, alias);289}290} catch (Exception e) {291System.out.println(rb.getString("jarsigner.error.") + e);292if (debug) {293e.printStackTrace();294}295System.exit(1);296} finally {297// zero-out private key password298if (keypass != null) {299Arrays.fill(keypass, ' ');300keypass = null;301}302// zero-out keystore password303if (storepass != null) {304Arrays.fill(storepass, ' ');305storepass = null;306}307Event.clearReportListener(Event.ReporterCategory.CRLCHECK);308}309310if (strict) {311int exitCode = 0;312if (disabledAlg != 0 || chainNotValidated || hasExpiredCert313|| hasExpiredTsaCert || notYetValidCert || signerSelfSigned) {314exitCode |= 4;315}316if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {317exitCode |= 8;318}319if (hasUnsignedEntry) {320exitCode |= 16;321}322if (notSignedByAlias || aliasNotInStore) {323exitCode |= 32;324}325if (tsaChainNotValidated) {326exitCode |= 64;327}328if (exitCode != 0) {329System.exit(exitCode);330}331}332}333334/*335* Parse command line arguments.336*/337String[] parseArgs(String args[]) throws Exception {338/* parse flags */339int n = 0;340341if (args.length == 0) fullusage();342343String confFile = null;344String command = "-sign";345for (n=0; n < args.length; n++) {346if (collator.compare(args[n], "-verify") == 0) {347command = "-verify";348} else if (collator.compare(args[n], "-conf") == 0) {349if (n == args.length - 1) {350usageNoArg();351}352confFile = args[++n];353}354}355356if (confFile != null) {357args = KeyStoreUtil.expandArgs(358"jarsigner", confFile, command, null, args);359}360361debug = Arrays.stream(args).anyMatch(362x -> collator.compare(x, "-debug") == 0);363364if (debug) {365// No need to localize debug output366System.out.println("Command line args: " +367Arrays.toString(args));368}369370for (n=0; n < args.length; n++) {371372String flags = args[n];373String modifier = null;374375if (flags.startsWith("-")) {376int pos = flags.indexOf(':');377if (pos > 0) {378modifier = flags.substring(pos+1);379flags = flags.substring(0, pos);380}381}382383if (!flags.startsWith("-")) {384if (jarfile == null) {385jarfile = flags;386} else {387alias = flags;388ckaliases.add(alias);389}390} else if (collator.compare(flags, "-conf") == 0) {391if (++n == args.length) usageNoArg();392} else if (collator.compare(flags, "-keystore") == 0) {393if (++n == args.length) usageNoArg();394keystore = args[n];395} else if (collator.compare(flags, "-storepass") ==0) {396if (++n == args.length) usageNoArg();397storepass = getPass(modifier, args[n]);398} else if (collator.compare(flags, "-storetype") ==0) {399if (++n == args.length) usageNoArg();400storetype = args[n];401} else if (collator.compare(flags, "-providerName") ==0) {402if (++n == args.length) usageNoArg();403providerName = args[n];404} else if (collator.compare(flags, "-provider") == 0 ||405collator.compare(flags, "-providerClass") == 0) {406if (++n == args.length) usageNoArg();407if (providerClasses == null) {408providerClasses = new ArrayList<>(3);409}410providerClasses.add(args[n]);411412if (args.length > (n+1)) {413flags = args[n+1];414if (collator.compare(flags, "-providerArg") == 0) {415if (args.length == (n+2)) usageNoArg();416providerArgs.put(args[n], args[n+2]);417n += 2;418}419}420} else if (collator.compare(flags, "-addprovider") == 0) {421if (++n == args.length) usageNoArg();422if (providers == null) {423providers = new ArrayList<>(3);424}425providers.add(args[n]);426427if (args.length > (n+1)) {428flags = args[n+1];429if (collator.compare(flags, "-providerArg") == 0) {430if (args.length == (n+2)) usageNoArg();431providerArgs.put(args[n], args[n+2]);432n += 2;433}434}435} else if (collator.compare(flags, "-protected") ==0) {436protectedPath = true;437} else if (collator.compare(flags, "-certchain") ==0) {438if (++n == args.length) usageNoArg();439altCertChain = args[n];440} else if (collator.compare(flags, "-tsapolicyid") ==0) {441if (++n == args.length) usageNoArg();442tSAPolicyID = args[n];443} else if (collator.compare(flags, "-tsadigestalg") ==0) {444if (++n == args.length) usageNoArg();445tSADigestAlg = args[n];446} else if (collator.compare(flags, "-debug") ==0) {447// Already processed448} else if (collator.compare(flags, "-keypass") ==0) {449if (++n == args.length) usageNoArg();450keypass = getPass(modifier, args[n]);451} else if (collator.compare(flags, "-sigfile") ==0) {452if (++n == args.length) usageNoArg();453sigfile = args[n];454} else if (collator.compare(flags, "-signedjar") ==0) {455if (++n == args.length) usageNoArg();456signedjar = args[n];457} else if (collator.compare(flags, "-tsa") ==0) {458if (++n == args.length) usageNoArg();459tsaUrl = args[n];460} else if (collator.compare(flags, "-tsacert") ==0) {461if (++n == args.length) usageNoArg();462tsaAlias = args[n];463} else if (collator.compare(flags, "-altsigner") ==0) {464if (++n == args.length) usageNoArg();465altSignerClass = args[n];466System.err.println(467rb.getString("This.option.is.forremoval") +468"-altsigner");469} else if (collator.compare(flags, "-altsignerpath") ==0) {470if (++n == args.length) usageNoArg();471altSignerClasspath = args[n];472System.err.println(473rb.getString("This.option.is.forremoval") +474"-altsignerpath");475} else if (collator.compare(flags, "-sectionsonly") ==0) {476signManifest = false;477} else if (collator.compare(flags, "-internalsf") ==0) {478externalSF = false;479} else if (collator.compare(flags, "-verify") ==0) {480verify = true;481} else if (collator.compare(flags, "-verbose") ==0) {482verbose = (modifier != null) ? modifier : "all";483} else if (collator.compare(flags, "-sigalg") ==0) {484if (++n == args.length) usageNoArg();485sigalg = args[n];486} else if (collator.compare(flags, "-digestalg") ==0) {487if (++n == args.length) usageNoArg();488digestalg = args[n];489} else if (collator.compare(flags, "-certs") ==0) {490showcerts = true;491} else if (collator.compare(flags, "-strict") ==0) {492strict = true;493} else if (collator.compare(flags, "-?") == 0 ||494collator.compare(flags, "-h") == 0 ||495collator.compare(flags, "--help") == 0 ||496// -help: legacy.497collator.compare(flags, "-help") == 0) {498fullusage();499} else if (collator.compare(flags, "-revCheck") == 0) {500revocationCheck = true;501} else {502System.err.println(503rb.getString("Illegal.option.") + flags);504usage();505}506}507508// -certs must always be specified with -verbose509if (verbose == null) showcerts = false;510511if (jarfile == null) {512System.err.println(rb.getString("Please.specify.jarfile.name"));513usage();514}515if (!verify && alias == null) {516System.err.println(rb.getString("Please.specify.alias.name"));517usage();518}519if (!verify && ckaliases.size() > 1) {520System.err.println(rb.getString("Only.one.alias.can.be.specified"));521usage();522}523524if (storetype == null) {525storetype = KeyStore.getDefaultType();526}527storetype = KeyStoreUtil.niceStoreTypeName(storetype);528529try {530if (signedjar != null && new File(signedjar).getCanonicalPath().equals(531new File(jarfile).getCanonicalPath())) {532signedjar = null;533}534} catch (IOException ioe) {535// File system error?536// Just ignore it.537}538539if (P11KEYSTORE.equalsIgnoreCase(storetype) ||540KeyStoreUtil.isWindowsKeyStore(storetype)) {541token = true;542if (keystore == null) {543keystore = NONE;544}545}546547if (NONE.equals(keystore)) {548nullStream = true;549}550551if (token && !nullStream) {552System.err.println(MessageFormat.format(rb.getString553(".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));554usage();555}556557if (token && keypass != null) {558System.err.println(MessageFormat.format(rb.getString559(".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));560usage();561}562563if (protectedPath) {564if (storepass != null || keypass != null) {565System.err.println(rb.getString566("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));567usage();568}569}570if (KeyStoreUtil.isWindowsKeyStore(storetype)) {571if (storepass != null || keypass != null) {572System.err.println(rb.getString573("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));574usage();575}576}577return args;578}579580static char[] getPass(String modifier, String arg) {581char[] output =582KeyStoreUtil.getPassWithModifier(modifier, arg, rb, collator);583if (output != null) return output;584usage();585return null; // Useless, usage() already exit586}587588static void usageNoArg() {589System.out.println(rb.getString("Option.lacks.argument"));590usage();591}592593static void usage() {594System.out.println();595System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));596System.exit(1);597}598599static void fullusage() {600System.out.println(rb.getString601("Usage.jarsigner.options.jar.file.alias"));602System.out.println(rb.getString603(".jarsigner.verify.options.jar.file.alias."));604System.out.println();605System.out.println(rb.getString606(".keystore.url.keystore.location"));607System.out.println();608System.out.println(rb.getString609(".storepass.password.password.for.keystore.integrity"));610System.out.println();611System.out.println(rb.getString612(".storetype.type.keystore.type"));613System.out.println();614System.out.println(rb.getString615(".keypass.password.password.for.private.key.if.different."));616System.out.println();617System.out.println(rb.getString618(".certchain.file.name.of.alternative.certchain.file"));619System.out.println();620System.out.println(rb.getString621(".sigfile.file.name.of.SF.DSA.file"));622System.out.println();623System.out.println(rb.getString624(".signedjar.file.name.of.signed.JAR.file"));625System.out.println();626System.out.println(rb.getString627(".digestalg.algorithm.name.of.digest.algorithm"));628System.out.println();629System.out.println(rb.getString630(".sigalg.algorithm.name.of.signature.algorithm"));631System.out.println();632System.out.println(rb.getString633(".verify.verify.a.signed.JAR.file"));634System.out.println();635System.out.println(rb.getString636(".verbose.suboptions.verbose.output.when.signing.verifying."));637System.out.println(rb.getString638(".suboptions.can.be.all.grouped.or.summary"));639System.out.println();640System.out.println(rb.getString641(".certs.display.certificates.when.verbose.and.verifying"));642System.out.println();643System.out.println(rb.getString644(".certs.revocation.check"));645System.out.println();646System.out.println(rb.getString647(".tsa.url.location.of.the.Timestamping.Authority"));648System.out.println();649System.out.println(rb.getString650(".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));651System.out.println();652System.out.println(rb.getString653(".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));654System.out.println();655System.out.println(rb.getString656(".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));657System.out.println();658System.out.println(rb.getString659(".altsigner.class.class.name.of.an.alternative.signing.mechanism"));660System.out.println();661System.out.println(rb.getString662(".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));663System.out.println();664System.out.println(rb.getString665(".internalsf.include.the.SF.file.inside.the.signature.block"));666System.out.println();667System.out.println(rb.getString668(".sectionsonly.don.t.compute.hash.of.entire.manifest"));669System.out.println();670System.out.println(rb.getString671(".protected.keystore.has.protected.authentication.path"));672System.out.println();673System.out.println(rb.getString674(".providerName.name.provider.name"));675System.out.println();676System.out.println(rb.getString677(".add.provider.option"));678System.out.println(rb.getString679(".providerArg.option.1"));680System.out.println();681System.out.println(rb.getString682(".providerClass.option"));683System.out.println(rb.getString684(".providerArg.option.2"));685System.out.println();686System.out.println(rb.getString687(".strict.treat.warnings.as.errors"));688System.out.println();689System.out.println(rb.getString690(".conf.url.specify.a.pre.configured.options.file"));691System.out.println();692System.out.println(rb.getString693(".print.this.help.message"));694System.out.println();695696System.exit(0);697}698699void verifyJar(String jarName)700throws Exception701{702boolean anySigned = false; // if there exists entry inside jar signed703JarFile jf = null;704Map<String,String> digestMap = new HashMap<>();705Map<String,PKCS7> sigMap = new HashMap<>();706Map<String,String> sigNameMap = new HashMap<>();707Map<String,String> unparsableSignatures = new HashMap<>();708709try {710jf = new JarFile(jarName, true);711Vector<JarEntry> entriesVec = new Vector<>();712byte[] buffer = new byte[8192];713714String suffix1 = "-Digest-Manifest";715String suffix2 = "-Digest-" + ManifestDigester.MF_MAIN_ATTRS;716717int suffixLength1 = suffix1.length();718int suffixLength2 = suffix2.length();719720Enumeration<JarEntry> entries = jf.entries();721while (entries.hasMoreElements()) {722JarEntry je = entries.nextElement();723entriesVec.addElement(je);724try (InputStream is = jf.getInputStream(je)) {725String name = je.getName();726if (signatureRelated(name)727&& SignatureFileVerifier.isBlockOrSF(name)) {728String alias = name.substring(name.lastIndexOf('/') + 1,729name.lastIndexOf('.'));730try {731if (name.endsWith(".SF")) {732Manifest sf = new Manifest(is);733boolean found = false;734for (Object obj : sf.getMainAttributes().keySet()) {735String key = obj.toString();736if (key.endsWith(suffix1)) {737digestMap.put(alias, key.substring(7380, key.length() - suffixLength1));739found = true;740break;741} else if (key.endsWith(suffix2)) {742digestMap.put(alias, key.substring(7430, key.length() - suffixLength2));744found = true;745break;746}747}748if (!found) {749unparsableSignatures.putIfAbsent(alias,750String.format(751rb.getString("history.unparsable"),752name));753}754} else {755sigNameMap.put(alias, name);756sigMap.put(alias, new PKCS7(is));757}758} catch (IOException ioe) {759unparsableSignatures.putIfAbsent(alias, String.format(760rb.getString("history.unparsable"), name));761}762} else {763while (is.read(buffer, 0, buffer.length) != -1) {764// we just read. this will throw a SecurityException765// if a signature/digest check fails.766}767}768}769}770771Manifest man = jf.getManifest();772boolean hasSignature = false;773774// The map to record display info, only used when -verbose provided775// key: signer info string776// value: the list of files with common key777Map<String,List<String>> output = new LinkedHashMap<>();778779if (man != null) {780if (verbose != null) System.out.println();781Enumeration<JarEntry> e = entriesVec.elements();782783String tab = rb.getString("6SPACE");784785while (e.hasMoreElements()) {786JarEntry je = e.nextElement();787String name = je.getName();788789if (!extraAttrsDetected && JUZFA.getExtraAttributes(je) != -1) {790extraAttrsDetected = true;791}792hasSignature = hasSignature793|| SignatureFileVerifier.isBlockOrSF(name);794795CodeSigner[] signers = je.getCodeSigners();796boolean isSigned = (signers != null);797anySigned |= isSigned;798799boolean unsignedEntry = !isSigned800&& ((!je.isDirectory() && !signatureRelated(name))801// a directory entry but with a suspicious size802|| (je.isDirectory() && je.getSize() > 0));803hasUnsignedEntry |= unsignedEntry;804805int inStoreWithAlias = inKeyStore(signers);806807boolean inStore = (inStoreWithAlias & IN_KEYSTORE) != 0;808809notSignedByAlias |= (inStoreWithAlias & NOT_ALIAS) != 0;810if (keystore != null) {811aliasNotInStore |= isSigned && !inStore;812}813814// Only used when -verbose provided815StringBuffer sb = null;816if (verbose != null) {817sb = new StringBuffer();818boolean inManifest =819((man.getAttributes(name) != null) ||820(man.getAttributes("./"+name) != null) ||821(man.getAttributes("/"+name) != null));822sb.append(isSigned ? rb.getString("s") : rb.getString("SPACE"))823.append(inManifest ? rb.getString("m") : rb.getString("SPACE"))824.append(inStore ? rb.getString("k") : rb.getString("SPACE"))825.append((inStoreWithAlias & NOT_ALIAS) != 0 ?826rb.getString("X") : rb.getString("SPACE"))827.append(unsignedEntry ? rb.getString("q") : rb.getString("SPACE"))828.append(rb.getString("SPACE"));829sb.append('|');830}831832// When -certs provided, display info has extra empty833// lines at the beginning and end.834if (isSigned) {835if (showcerts) sb.append('\n');836for (CodeSigner signer: signers) {837// signerInfo() must be called even if -verbose838// not provided. The method updates various839// warning flags.840String si = signerInfo(signer, tab);841if (showcerts) {842sb.append(si);843sb.append('\n');844}845}846} else if (showcerts && !verbose.equals("all")) {847// Print no info for unsigned entries when -verbose:all,848// to be consistent with old behavior.849if (signatureRelated(name)) {850sb.append('\n')851.append(tab)852.append(rb853.getString(".Signature.related.entries."))854.append("\n\n");855} else if (unsignedEntry) {856sb.append('\n').append(tab)857.append(rb.getString(".Unsigned.entries."))858.append("\n\n");859} else {860sb.append('\n').append(tab)861.append(rb.getString(".Directory.entries."))862.append("\n\n");863}864}865866if (verbose != null) {867String label = sb.toString();868if (signatureRelated(name)) {869// Entries inside META-INF and other unsigned870// entries are grouped separately.871label = "-" + label;872}873874// The label finally contains 2 parts separated by '|':875// The legend displayed before the entry names, and876// the cert info (if -certs specified).877878if (!output.containsKey(label)) {879output.put(label, new ArrayList<String>());880}881882StringBuilder fb = new StringBuilder();883String s = Long.toString(je.getSize());884for (int i = 6 - s.length(); i > 0; --i) {885fb.append(' ');886}887fb.append(s).append(' ').888append(new Date(je.getTime()).toString());889fb.append(' ').append(name);890891output.get(label).add(fb.toString());892}893}894}895if (verbose != null) {896for (Entry<String,List<String>> s: output.entrySet()) {897List<String> files = s.getValue();898String key = s.getKey();899if (key.charAt(0) == '-') { // the signature-related group900key = key.substring(1);901}902int pipe = key.indexOf('|');903if (verbose.equals("all")) {904for (String f: files) {905System.out.println(key.substring(0, pipe) + f);906System.out.printf(key.substring(pipe+1));907}908} else {909if (verbose.equals("grouped")) {910for (String f: files) {911System.out.println(key.substring(0, pipe) + f);912}913} else if (verbose.equals("summary")) {914System.out.print(key.substring(0, pipe));915if (files.size() > 1) {916System.out.println(files.get(0) + " " +917String.format(rb.getString(918".and.d.more."), files.size()-1));919} else {920System.out.println(files.get(0));921}922}923System.out.printf(key.substring(pipe+1));924}925}926System.out.println();927System.out.println(rb.getString(928".s.signature.was.verified."));929System.out.println(rb.getString(930".m.entry.is.listed.in.manifest"));931System.out.println(rb.getString(932".k.at.least.one.certificate.was.found.in.keystore"));933if (ckaliases.size() > 0) {934System.out.println(rb.getString(935".X.not.signed.by.specified.alias.es."));936}937938if (hasUnsignedEntry) {939System.out.println(rb.getString(940".q.unsigned.entry"));941}942}943if (man == null) {944System.out.println();945System.out.println(rb.getString("no.manifest."));946}947948// If signer is a trusted cert or private entry in user's own949// keystore, it can be self-signed. Please note aliasNotInStore950// is always false when ~/.keystore is used.951if (!aliasNotInStore && keystore != null) {952signerSelfSigned = false;953}954955// Even if the verbose option is not specified, all out strings956// must be generated so disabledAlgFound can be updated.957if (!digestMap.isEmpty()958|| !sigMap.isEmpty()959|| !unparsableSignatures.isEmpty()) {960if (verbose != null) {961System.out.println();962}963for (String s : sigMap.keySet()) {964if (!digestMap.containsKey(s)) {965unparsableSignatures.putIfAbsent(s, String.format(966rb.getString("history.nosf"), s));967}968}969for (String s : digestMap.keySet()) {970PKCS7 p7 = sigMap.get(s);971if (p7 != null) {972String history;973try {974SignerInfo si = p7.getSignerInfos()[0];975ArrayList<X509Certificate> chain = si.getCertificateChain(p7);976X509Certificate signer = chain.get(0);977String digestAlg = digestMap.get(s);978String sigAlg = SignerInfo.makeSigAlg(979si.getDigestAlgorithmId(),980si.getDigestEncryptionAlgorithmId(),981si.getAuthenticatedAttributes() == null);982PublicKey key = signer.getPublicKey();983PKCS7 tsToken = si.getTsToken();984if (tsToken != null) {985hasTimestampBlock = true;986SignerInfo tsSi = tsToken.getSignerInfos()[0];987X509Certificate tsSigner = tsSi.getCertificate(tsToken);988byte[] encTsTokenInfo = tsToken.getContentInfo().getData();989TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);990PublicKey tsKey = tsSigner.getPublicKey();991String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();992String tsSigAlg = SignerInfo.makeSigAlg(993tsSi.getDigestAlgorithmId(),994tsSi.getDigestEncryptionAlgorithmId(),995tsSi.getAuthenticatedAttributes() == null);996Calendar c = Calendar.getInstance(997TimeZone.getTimeZone("UTC"),998Locale.getDefault(Locale.Category.FORMAT));999c.setTime(tsTokenInfo.getDate());1000JarConstraintsParameters jcp =1001new JarConstraintsParameters(chain, si.getTimestamp());1002history = String.format(1003rb.getString("history.with.ts"),1004signer.getSubjectX500Principal(),1005verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false, jcp),1006verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false, jcp),1007verifyWithWeak(key, jcp),1008c,1009tsSigner.getSubjectX500Principal(),1010verifyWithWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET, true, jcp),1011verifyWithWeak(tsSigAlg, SIG_PRIMITIVE_SET, true, jcp),1012verifyWithWeak(tsKey, jcp));1013} else {1014JarConstraintsParameters jcp =1015new JarConstraintsParameters(chain, null);1016history = String.format(1017rb.getString("history.without.ts"),1018signer.getSubjectX500Principal(),1019verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false, jcp),1020verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false, jcp),1021verifyWithWeak(key, jcp));1022}1023} catch (Exception e) {1024e.printStackTrace();1025// The only usage of sigNameMap, remember the name1026// of the block file if it's invalid.1027history = String.format(1028rb.getString("history.unparsable"),1029sigNameMap.get(s));1030}1031if (verbose != null) {1032System.out.println(history);1033}1034} else {1035unparsableSignatures.putIfAbsent(s, String.format(1036rb.getString("history.nobk"), s));1037}1038}1039if (verbose != null) {1040for (String s : unparsableSignatures.keySet()) {1041System.out.println(unparsableSignatures.get(s));1042}1043}1044}1045System.out.println();10461047if (!anySigned) {1048if (disabledAlgFound) {1049if (verbose != null) {1050System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));1051System.out.println("\n " +1052DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +1053"=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));1054} else {1055System.out.println(rb.getString("jar.treated.unsigned.see.weak"));1056}1057} else if (hasSignature) {1058System.out.println(rb.getString("jar.treated.unsigned"));1059} else {1060System.out.println(rb.getString("jar.is.unsigned"));1061}1062} else {1063displayMessagesAndResult(false);1064}1065return;1066} catch (Exception e) {1067System.out.println(rb.getString("jarsigner.") + e);1068if (debug) {1069e.printStackTrace();1070}1071} finally { // close the resource1072if (jf != null) {1073jf.close();1074}1075}10761077System.exit(1);1078}10791080private void displayMessagesAndResult(boolean isSigning) {1081String result;1082List<String> errors = new ArrayList<>();1083List<String> warnings = new ArrayList<>();1084List<String> info = new ArrayList<>();10851086boolean signerNotExpired = expireDate == null1087|| expireDate.after(new Date());10881089if (badKeyUsage) {1090errors.add(isSigning1091? rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")1092: rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));1093}10941095if (badExtendedKeyUsage) {1096errors.add(isSigning1097? rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")1098: rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));1099}11001101if (badNetscapeCertType) {1102errors.add(isSigning1103? rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")1104: rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));1105}11061107// only in verifying1108if (hasUnsignedEntry) {1109errors.add(rb.getString(1110"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));1111}11121113if (hasExpiredCert) {1114errors.add(isSigning1115? rb.getString("The.signer.certificate.has.expired.")1116: rb.getString("This.jar.contains.entries.whose.signer.certificate.has.expired."));1117}11181119if (notYetValidCert) {1120errors.add(isSigning1121? rb.getString("The.signer.certificate.is.not.yet.valid.")1122: rb.getString("This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));1123}11241125if (chainNotValidated) {1126errors.add(String.format(isSigning1127? rb.getString("The.signer.s.certificate.chain.is.invalid.reason.1")1128: rb.getString("This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),1129chainNotValidatedReason.getLocalizedMessage()));1130}11311132if (tsaChainNotValidated) {1133errors.add(String.format(isSigning1134? rb.getString("The.tsa.certificate.chain.is.invalid.reason.1")1135: rb.getString("This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),1136tsaChainNotValidatedReason.getLocalizedMessage()));1137}11381139// only in verifying1140if (notSignedByAlias) {1141errors.add(1142rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));1143}11441145// only in verifying1146if (aliasNotInStore) {1147errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));1148}11491150if (signerSelfSigned) {1151errors.add(isSigning1152? rb.getString("The.signer.s.certificate.is.self.signed.")1153: rb.getString("This.jar.contains.entries.whose.signer.certificate.is.self.signed."));1154}11551156if (isSigning) {1157if ((legacyAlg & 1) == 1) {1158warnings.add(String.format(1159rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1160digestalg, "-digestalg"));1161}11621163if ((disabledAlg & 1) == 1) {1164errors.add(String.format(1165rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),1166digestalg, "-digestalg"));1167}11681169if ((legacyAlg & 2) == 2) {1170warnings.add(String.format(1171rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1172sigalg, "-sigalg"));1173}11741175if ((disabledAlg & 2) == 2) {1176errors.add(String.format(1177rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),1178sigalg, "-sigalg"));1179}11801181if ((legacyAlg & 4) == 4) {1182warnings.add(String.format(1183rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1184tSADigestAlg, "-tsadigestalg"));1185}11861187if ((disabledAlg & 4) == 4) {1188errors.add(String.format(1189rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),1190tSADigestAlg, "-tsadigestalg"));1191}11921193if ((legacyAlg & 8) == 8) {1194warnings.add(String.format(1195rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk..This.key.size.will.be.disabled.in.a.future.update."),1196privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));1197}11981199if ((disabledAlg & 8) == 8) {1200errors.add(String.format(1201rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk.and.is.disabled."),1202privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));1203}1204} else {1205if ((legacyAlg & 1) != 0) {1206warnings.add(String.format(1207rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1208legacyDigestAlg));1209}12101211if ((legacyAlg & 2) == 2) {1212warnings.add(String.format(1213rb.getString("The.signature.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1214legacySigAlg));1215}12161217if ((legacyAlg & 4) != 0) {1218warnings.add(String.format(1219rb.getString("The.timestamp.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1220legacyTsaDigestAlg));1221}12221223if ((legacyAlg & 8) == 8) {1224warnings.add(String.format(1225rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk..This.key.size.will.be.disabled.in.a.future.update."),1226weakPublicKey.getAlgorithm(), KeyUtil.getKeySize(weakPublicKey)));1227}1228}12291230// This check must be placed after all other "errors.add()" calls were done.1231if (hasExpiredTsaCert) {1232// No need to warn about expiring if already expired1233hasExpiringTsaCert = false;1234// If there are already another errors, we just say it expired.1235if (!signerNotExpired || !errors.isEmpty()) {1236errors.add(rb.getString("The.timestamp.has.expired."));1237} else if (signerNotExpired) {1238if (expireDate != null) {1239warnings.add(String.format(1240rb.getString("The.timestamp.expired.1.but.usable.2"),1241tsaExpireDate,1242expireDate));1243}1244// Reset the flag so exit code is 01245hasExpiredTsaCert = false;1246}1247}12481249if (hasExpiringCert) {1250warnings.add(isSigning1251? rb.getString("The.signer.certificate.will.expire.within.six.months.")1252: rb.getString("This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));1253}12541255if (hasExpiringTsaCert && expireDate != null) {1256if (expireDate.after(tsaExpireDate)) {1257warnings.add(String.format(rb.getString(1258"The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate));1259} else {1260warnings.add(String.format(rb.getString(1261"The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate));1262}1263}12641265if (noTimestamp && expireDate != null) {1266if (hasTimestampBlock) {1267warnings.add(String.format(isSigning1268? rb.getString("invalid.timestamp.signing")1269: rb.getString("bad.timestamp.verifying"), expireDate));1270} else {1271warnings.add(String.format(isSigning1272? rb.getString("no.timestamp.signing")1273: rb.getString("no.timestamp.verifying"), expireDate));1274}1275}12761277if (extraAttrsDetected) {1278warnings.add(rb.getString("extra.attributes.detected"));1279}12801281if ((strict) && (!errors.isEmpty())) {1282result = isSigning1283? rb.getString("jar.signed.with.signer.errors.")1284: rb.getString("jar.verified.with.signer.errors.");1285} else {1286result = isSigning1287? rb.getString("jar.signed.")1288: rb.getString("jar.verified.");1289}1290System.out.println(result);12911292if (strict) {1293if (!errors.isEmpty()) {1294System.out.println();1295System.out.println(rb.getString("Error."));1296errors.forEach(System.out::println);1297}1298if (!warnings.isEmpty()) {1299System.out.println();1300System.out.println(rb.getString("Warning."));1301warnings.forEach(System.out::println);1302}1303} else {1304if (!errors.isEmpty() || !warnings.isEmpty()) {1305System.out.println();1306System.out.println(rb.getString("Warning."));1307errors.forEach(System.out::println);1308warnings.forEach(System.out::println);1309}1310}13111312if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {1313if (! (verbose != null && showcerts)) {1314System.out.println();1315System.out.println(rb.getString(1316"Re.run.with.the.verbose.and.certs.options.for.more.details."));1317}1318}13191320if (isSigning || verbose != null) {1321// Always print out expireDate, unless expired or expiring.1322if (!hasExpiringCert && !hasExpiredCert1323&& expireDate != null && signerNotExpired) {1324info.add(String.format(rb.getString(1325"The.signer.certificate.will.expire.on.1."), expireDate));1326}1327if (!noTimestamp) {1328if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) {1329if (signerNotExpired) {1330info.add(String.format(rb.getString(1331"The.timestamp.will.expire.on.1."), tsaExpireDate));1332} else {1333info.add(String.format(rb.getString(1334"signer.cert.expired.1.but.timestamp.good.2."),1335expireDate,1336tsaExpireDate));1337}1338}1339}1340}13411342if (!info.isEmpty()) {1343System.out.println();1344info.forEach(System.out::println);1345}1346}13471348private String verifyWithWeak(String alg, Set<CryptoPrimitive> primitiveSet,1349boolean tsa, JarConstraintsParameters jcp) {13501351try {1352JAR_DISABLED_CHECK.permits(alg, jcp);1353} catch (CertPathValidatorException e) {1354disabledAlgFound = true;1355return String.format(rb.getString("with.disabled"), alg);1356}1357try {1358LEGACY_CHECK.permits(alg, jcp);1359return alg;1360} catch (CertPathValidatorException e) {1361if (primitiveSet == SIG_PRIMITIVE_SET) {1362legacyAlg |= 2;1363legacySigAlg = alg;1364} else {1365if (tsa) {1366legacyAlg |= 4;1367legacyTsaDigestAlg = alg;1368} else {1369legacyAlg |= 1;1370legacyDigestAlg = alg;1371}1372}1373return String.format(rb.getString("with.weak"), alg);1374}1375}13761377private String verifyWithWeak(PublicKey key, JarConstraintsParameters jcp) {1378int kLen = KeyUtil.getKeySize(key);1379try {1380JAR_DISABLED_CHECK.permits(key.getAlgorithm(), jcp);1381} catch (CertPathValidatorException e) {1382disabledAlgFound = true;1383return String.format(rb.getString("key.bit.disabled"), kLen);1384}1385try {1386LEGACY_CHECK.permits(key.getAlgorithm(), jcp);1387if (kLen >= 0) {1388return String.format(rb.getString("key.bit"), kLen);1389} else {1390return rb.getString("unknown.size");1391}1392} catch (CertPathValidatorException e) {1393weakPublicKey = key;1394legacyAlg |= 8;1395return String.format(rb.getString("key.bit.weak"), kLen);1396}1397}13981399private void checkWeakSign(String alg, Set<CryptoPrimitive> primitiveSet,1400boolean tsa, JarConstraintsParameters jcp) {14011402try {1403JAR_DISABLED_CHECK.permits(alg, jcp);1404try {1405LEGACY_CHECK.permits(alg, jcp);1406} catch (CertPathValidatorException e) {1407if (primitiveSet == SIG_PRIMITIVE_SET) {1408legacyAlg |= 2;1409} else {1410if (tsa) {1411legacyAlg |= 4;1412} else {1413legacyAlg |= 1;1414}1415}1416}1417} catch (CertPathValidatorException e) {1418if (primitiveSet == SIG_PRIMITIVE_SET) {1419disabledAlg |= 2;1420} else {1421if (tsa) {1422disabledAlg |= 4;1423} else {1424disabledAlg |= 1;1425}1426}1427}1428}14291430private void checkWeakSign(PrivateKey key, JarConstraintsParameters jcp) {1431try {1432JAR_DISABLED_CHECK.permits(key.getAlgorithm(), jcp);1433try {1434LEGACY_CHECK.permits(key.getAlgorithm(), jcp);1435} catch (CertPathValidatorException e) {1436legacyAlg |= 8;1437}1438} catch (CertPathValidatorException e) {1439disabledAlg |= 8;1440}1441}14421443private static String checkWeakKey(PublicKey key, CertPathConstraintsParameters cpcp) {1444int kLen = KeyUtil.getKeySize(key);1445try {1446CERTPATH_DISABLED_CHECK.permits(key.getAlgorithm(), cpcp);1447} catch (CertPathValidatorException e) {1448return String.format(rb.getString("key.bit.disabled"), kLen);1449}1450try {1451LEGACY_CHECK.permits(key.getAlgorithm(), cpcp);1452if (kLen >= 0) {1453return String.format(rb.getString("key.bit"), kLen);1454} else {1455return rb.getString("unknown.size");1456}1457} catch (CertPathValidatorException e) {1458return String.format(rb.getString("key.bit.weak"), kLen);1459}1460}14611462private static String checkWeakAlg(String alg, CertPathConstraintsParameters cpcp) {1463try {1464CERTPATH_DISABLED_CHECK.permits(alg, cpcp);1465} catch (CertPathValidatorException e) {1466return String.format(rb.getString("with.disabled"), alg);1467}1468try {1469LEGACY_CHECK.permits(alg, cpcp);1470return alg;1471} catch (CertPathValidatorException e) {1472return String.format(rb.getString("with.weak"), alg);1473}1474}14751476private static MessageFormat validityTimeForm = null;1477private static MessageFormat notYetTimeForm = null;1478private static MessageFormat expiredTimeForm = null;1479private static MessageFormat expiringTimeForm = null;14801481/**1482* Returns a string about a certificate:1483*1484* [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]1485* [<validity-period> | <expiry-warning>]1486* [<key-usage-warning>]1487*1488* Note: no newline character at the end.1489*1490* This method sets global flags like hasExpiringCert, hasExpiredCert,1491* notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType,1492* hasExpiringTsaCert, hasExpiredTsaCert.1493*1494* @param isTsCert true if c is in the TSA cert chain, false otherwise.1495* @param checkUsage true to check code signer keyUsage1496*/1497String printCert(boolean isTsCert, String tab, Certificate c,1498Date timestamp, boolean checkUsage, CertPathConstraintsParameters cpcp) throws Exception {14991500StringBuilder certStr = new StringBuilder();1501String space = rb.getString("SPACE");1502X509Certificate x509Cert = null;15031504if (c instanceof X509Certificate) {1505x509Cert = (X509Certificate) c;1506certStr.append(tab).append(x509Cert.getType())1507.append(rb.getString("COMMA"))1508.append(x509Cert.getSubjectX500Principal().toString());1509} else {1510certStr.append(tab).append(c.getType());1511}15121513String alias = storeHash.get(c);1514if (alias != null) {1515certStr.append(space).append("(").append(alias).append(")");1516}15171518if (x509Cert != null) {1519PublicKey key = x509Cert.getPublicKey();1520String sigalg = x509Cert.getSigAlgName();15211522// Process the certificate in the signer's cert chain to see if1523// weak algorithms are used, and provide warnings as needed.1524if (trustedCerts.contains(x509Cert)) {1525// If the cert is trusted, only check its key size, but not its1526// signature algorithm.1527certStr.append("\n").append(tab)1528.append("Signature algorithm: ")1529.append(sigalg)1530.append(rb.getString("COMMA"))1531.append(checkWeakKey(key, cpcp));15321533certStr.append("\n").append(tab).append("[");1534certStr.append(rb.getString("trusted.certificate"));1535} else {1536certStr.append("\n").append(tab)1537.append("Signature algorithm: ")1538.append(checkWeakAlg(sigalg, cpcp))1539.append(rb.getString("COMMA"))1540.append(checkWeakKey(key, cpcp));15411542certStr.append("\n").append(tab).append("[");15431544Date notAfter = x509Cert.getNotAfter();1545try {1546boolean printValidity = true;1547if (isTsCert) {1548if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) {1549tsaExpireDate = notAfter;1550}1551} else {1552if (expireDate == null || expireDate.after(notAfter)) {1553expireDate = notAfter;1554}1555}1556if (timestamp == null) {1557x509Cert.checkValidity();1558// test if cert will expire within six months (or one year for tsa)1559long age = isTsCert ? ONE_YEAR : SIX_MONTHS;1560if (notAfter.getTime() < System.currentTimeMillis() + age) {1561if (isTsCert) {1562hasExpiringTsaCert = true;1563} else {1564hasExpiringCert = true;1565}1566if (expiringTimeForm == null) {1567expiringTimeForm = new MessageFormat(1568rb.getString("certificate.will.expire.on"));1569}1570Object[] source = {notAfter};1571certStr.append(expiringTimeForm.format(source));1572printValidity = false;1573}1574} else {1575x509Cert.checkValidity(timestamp);1576}1577if (printValidity) {1578if (validityTimeForm == null) {1579validityTimeForm = new MessageFormat(1580rb.getString("certificate.is.valid.from"));1581}1582Object[] source = {x509Cert.getNotBefore(), notAfter};1583certStr.append(validityTimeForm.format(source));1584}1585} catch (CertificateExpiredException cee) {1586if (isTsCert) {1587hasExpiredTsaCert = true;1588} else {1589hasExpiredCert = true;1590}15911592if (expiredTimeForm == null) {1593expiredTimeForm = new MessageFormat(1594rb.getString("certificate.expired.on"));1595}1596Object[] source = {notAfter};1597certStr.append(expiredTimeForm.format(source));15981599} catch (CertificateNotYetValidException cnyve) {1600if (!isTsCert) notYetValidCert = true;16011602if (notYetTimeForm == null) {1603notYetTimeForm = new MessageFormat(1604rb.getString("certificate.is.not.valid.until"));1605}1606Object[] source = {x509Cert.getNotBefore()};1607certStr.append(notYetTimeForm.format(source));1608}1609}1610certStr.append("]");16111612if (checkUsage) {1613boolean[] bad = new boolean[3];1614checkCertUsage(x509Cert, bad);1615if (bad[0] || bad[1] || bad[2]) {1616String x = "";1617if (bad[0]) {1618x ="KeyUsage";1619}1620if (bad[1]) {1621if (x.length() > 0) x = x + ", ";1622x = x + "ExtendedKeyUsage";1623}1624if (bad[2]) {1625if (x.length() > 0) x = x + ", ";1626x = x + "NetscapeCertType";1627}1628certStr.append("\n").append(tab)1629.append(MessageFormat.format(rb.getString(1630".{0}.extension.does.not.support.code.signing."), x));1631}1632}1633}1634return certStr.toString();1635}16361637private static MessageFormat signTimeForm = null;16381639private String printTimestamp(String tab, Timestamp timestamp) {16401641if (signTimeForm == null) {1642signTimeForm =1643new MessageFormat(rb.getString("entry.was.signed.on"));1644}1645Object[] source = { timestamp.getTimestamp() };16461647return new StringBuilder().append(tab).append("[")1648.append(signTimeForm.format(source)).append("]").toString();1649}16501651private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>();16521653private int inKeyStoreForOneSigner(CodeSigner signer) {1654if (cacheForInKS.containsKey(signer)) {1655return cacheForInKS.get(signer);1656}16571658int result = 0;1659if (store != null) {1660try {1661List<? extends Certificate> certs =1662signer.getSignerCertPath().getCertificates();1663for (Certificate c : certs) {1664String alias = storeHash.get(c);1665if (alias == null) {1666alias = store.getCertificateAlias(c);1667if (alias != null) {1668storeHash.put(c, alias);1669}1670}1671if (alias != null) {1672result |= IN_KEYSTORE;1673}1674for (String ckalias : ckaliases) {1675if (c.equals(store.getCertificate(ckalias))) {1676result |= SIGNED_BY_ALIAS;1677// must continue with next certificate c and cannot1678// return or break outer loop because has to fill1679// storeHash for printCert1680break;1681}1682}1683}1684} catch (KeyStoreException kse) {1685// never happens, because keystore has been loaded1686}1687}1688cacheForInKS.put(signer, result);1689return result;1690}16911692/**1693* Maps certificates (as keys) to alias names associated in the keystore1694* {@link #keystore} (as values).1695*/1696Hashtable<Certificate, String> storeHash = new Hashtable<>();16971698int inKeyStore(CodeSigner[] signers) {16991700if (signers == null)1701return 0;17021703int output = 0;17041705for (CodeSigner signer: signers) {1706int result = inKeyStoreForOneSigner(signer);1707output |= result;1708}1709if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {1710output |= NOT_ALIAS;1711}1712return output;1713}17141715void signJar(String jarName, String alias)1716throws Exception {17171718if (digestalg == null) {1719digestalg = JarSigner.Builder.getDefaultDigestAlgorithm();1720}1721JarConstraintsParameters jcp =1722new JarConstraintsParameters(Arrays.asList(certChain), null);1723checkWeakSign(digestalg, DIGEST_PRIMITIVE_SET, false, jcp);17241725if (tSADigestAlg == null) {1726tSADigestAlg = JarSigner.Builder.getDefaultDigestAlgorithm();1727}1728checkWeakSign(tSADigestAlg, DIGEST_PRIMITIVE_SET, true, jcp);17291730if (sigalg == null) {1731sigalg = JarSigner.Builder.getDefaultSignatureAlgorithm(privateKey);1732}1733checkWeakSign(sigalg, SIG_PRIMITIVE_SET, false, jcp);17341735checkWeakSign(privateKey, jcp);17361737boolean aliasUsed = false;1738X509Certificate tsaCert = null;17391740if (sigfile == null) {1741sigfile = alias;1742aliasUsed = true;1743}17441745if (sigfile.length() > 8) {1746sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH);1747} else {1748sigfile = sigfile.toUpperCase(Locale.ENGLISH);1749}17501751StringBuilder tmpSigFile = new StringBuilder(sigfile.length());1752for (int j = 0; j < sigfile.length(); j++) {1753char c = sigfile.charAt(j);1754if (!1755((c>= 'A' && c<= 'Z') ||1756(c>= '0' && c<= '9') ||1757(c == '-') ||1758(c == '_'))) {1759if (aliasUsed) {1760// convert illegal characters from the alias to be _'s1761c = '_';1762} else {1763throw new1764RuntimeException(rb.getString1765("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));1766}1767}1768tmpSigFile.append(c);1769}17701771sigfile = tmpSigFile.toString();17721773String tmpJarName;1774if (signedjar == null) tmpJarName = jarName+".sig";1775else tmpJarName = signedjar;17761777File jarFile = new File(jarName);1778File signedJarFile = new File(tmpJarName);17791780// Open the jar (zip) file1781try {1782zipFile = new ZipFile(jarName);1783} catch (IOException ioe) {1784error(rb.getString("unable.to.open.jar.file.")+jarName, ioe);1785}17861787CertPath cp = CertificateFactory.getInstance("X.509")1788.generateCertPath(Arrays.asList(certChain));1789JarSigner.Builder builder = new JarSigner.Builder(privateKey, cp);17901791if (verbose != null) {1792builder.eventHandler((action, file) -> {1793switch (action) {1794case "signing":1795System.out.println(rb.getString(".signing.") + file);1796break;1797case "adding":1798System.out.println(rb.getString(".adding.") + file);1799break;1800case "updating":1801System.out.println(rb.getString(".updating.") + file);1802break;1803default:1804throw new IllegalArgumentException("unknown action: "1805+ action);1806}1807});1808}18091810if (digestalg != null) {1811builder.digestAlgorithm(digestalg);1812}1813if (sigalg != null) {1814builder.signatureAlgorithm(sigalg);1815}18161817URI tsaURI = null;18181819if (tsaUrl != null) {1820tsaURI = new URI(tsaUrl);1821} else if (tsaAlias != null) {1822tsaCert = getTsaCert(tsaAlias);1823tsaURI = PKCS7.getTimestampingURI(tsaCert);1824}18251826if (tsaURI != null) {1827if (verbose != null) {1828System.out.println(1829rb.getString("requesting.a.signature.timestamp"));1830if (tsaUrl != null) {1831System.out.println(rb.getString("TSA.location.") + tsaUrl);1832} else if (tsaCert != null) {1833CertPathConstraintsParameters cpcp =1834new CertPathConstraintsParameters(tsaCert, Validator.VAR_TSA_SERVER, null, null);1835System.out.println(rb.getString("TSA.certificate.") +1836printCert(true, "", tsaCert, null, false, cpcp));1837}1838}1839builder.tsa(tsaURI);1840if (tSADigestAlg != null) {1841builder.setProperty("tsaDigestAlg", tSADigestAlg);1842}18431844if (tSAPolicyID != null) {1845builder.setProperty("tsaPolicyId", tSAPolicyID);1846}1847}18481849if (altSignerClass != null) {1850builder.setProperty("altSigner", altSignerClass);1851if (verbose != null) {1852System.out.println(1853rb.getString("using.an.alternative.signing.mechanism"));1854}1855}18561857if (altSignerClasspath != null) {1858builder.setProperty("altSignerPath", altSignerClasspath);1859}18601861builder.signerName(sigfile);18621863builder.setProperty("sectionsOnly", Boolean.toString(!signManifest));1864builder.setProperty("internalSF", Boolean.toString(!externalSF));18651866FileOutputStream fos = null;1867try {1868fos = new FileOutputStream(signedJarFile);1869} catch (IOException ioe) {1870error(rb.getString("unable.to.create.")+tmpJarName, ioe);1871}18721873Throwable failedCause = null;1874String failedMessage = null;18751876try {1877Event.setReportListener(Event.ReporterCategory.ZIPFILEATTRS,1878(t, o) -> extraAttrsDetected = true);1879builder.build().sign(zipFile, fos);1880} catch (JarSignerException e) {1881failedCause = e.getCause();1882if (failedCause instanceof SocketTimeoutException1883|| failedCause instanceof UnknownHostException) {1884// Provide a helpful message when TSA is beyond a firewall1885failedMessage = rb.getString("unable.to.sign.jar.") +1886rb.getString("no.response.from.the.Timestamping.Authority.") +1887"\n -J-Dhttp.proxyHost=<hostname>" +1888"\n -J-Dhttp.proxyPort=<portnumber>\n" +1889rb.getString("or") +1890"\n -J-Dhttps.proxyHost=<hostname> " +1891"\n -J-Dhttps.proxyPort=<portnumber> ";1892} else {1893// JarSignerException might have a null cause1894if (failedCause == null) {1895failedCause = e;1896}1897failedMessage = rb.getString("unable.to.sign.jar.") + failedCause;1898}1899} catch (Exception e) {1900failedCause = e;1901failedMessage = rb.getString("unable.to.sign.jar.") + failedCause;1902} finally {1903// close the resources1904if (zipFile != null) {1905zipFile.close();1906zipFile = null;1907}19081909if (fos != null) {1910fos.close();1911}19121913Event.clearReportListener(Event.ReporterCategory.ZIPFILEATTRS);1914}19151916if (failedCause != null) {1917signedJarFile.delete();1918error(failedMessage, failedCause);1919}19201921if (verbose != null) {1922System.out.println();1923}19241925// The JarSigner API always accepts the timestamp received.1926// We need to extract the certs from the signed jar to1927// validate it.1928try (JarFile check = new JarFile(signedJarFile)) {1929PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry(1930"META-INF/" + sigfile + "."1931+ SignatureFileVerifier.getBlockExtension(privateKey))));1932Timestamp ts = null;1933try {1934SignerInfo si = p7.getSignerInfos()[0];1935if (si.getTsToken() != null) {1936hasTimestampBlock = true;1937}1938ts = si.getTimestamp();1939} catch (Exception e) {1940tsaChainNotValidated = true;1941tsaChainNotValidatedReason = e;1942}1943// Spaces before the ">>> Signer" and other lines are different1944String result = certsAndTSInfo("", " ", Arrays.asList(certChain), ts);1945if (verbose != null) {1946System.out.println(result);1947}1948} catch (Exception e) {1949if (debug) {1950e.printStackTrace();1951}1952}19531954if (signedjar == null) {1955// attempt an atomic rename. If that fails,1956// rename the original jar file, then the signed1957// one, then delete the original.1958if (!signedJarFile.renameTo(jarFile)) {1959File origJar = new File(jarName+".orig");19601961if (jarFile.renameTo(origJar)) {1962if (signedJarFile.renameTo(jarFile)) {1963origJar.delete();1964} else {1965MessageFormat form = new MessageFormat(rb.getString1966("attempt.to.rename.signedJarFile.to.jarFile.failed"));1967Object[] source = {signedJarFile, jarFile};1968error(form.format(source));1969}1970} else {1971MessageFormat form = new MessageFormat(rb.getString1972("attempt.to.rename.jarFile.to.origJar.failed"));1973Object[] source = {jarFile, origJar};1974error(form.format(source));1975}1976}1977}1978displayMessagesAndResult(true);1979}19801981/**1982* signature-related files include:1983* . META-INF/MANIFEST.MF1984* . META-INF/SIG-*1985* . META-INF/*.SF1986* . META-INF/*.DSA1987* . META-INF/*.RSA1988* . META-INF/*.EC1989*/1990private boolean signatureRelated(String name) {1991return SignatureFileVerifier.isSigningRelated(name);1992}19931994Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();19951996/**1997* Returns a string of signer info, with a newline at the end.1998* Called by verifyJar().1999*/2000private String signerInfo(CodeSigner signer, String tab) throws Exception {2001if (cacheForSignerInfo.containsKey(signer)) {2002return cacheForSignerInfo.get(signer);2003}2004List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();2005// signing time is only displayed on verification2006Timestamp ts = signer.getTimestamp();2007String tsLine = "";2008if (ts != null) {2009tsLine = printTimestamp(tab, ts) + "\n";2010}2011// Spaces before the ">>> Signer" and other lines are the same.20122013String result = certsAndTSInfo(tab, tab, certs, ts);2014cacheForSignerInfo.put(signer, tsLine + result);2015return result;2016}20172018/**2019* Fills info on certs and timestamp into a StringBuilder, sets2020* warning flags (through printCert) and validates cert chains.2021*2022* @param tab1 spaces before the ">>> Signer" line2023* @param tab2 spaces before the other lines2024* @param certs the signer cert2025* @param ts the timestamp, can be null2026* @return the info as a string2027*/2028private String certsAndTSInfo(2029String tab1,2030String tab2,2031List<? extends Certificate> certs, Timestamp ts)2032throws Exception {20332034Date timestamp;2035if (ts != null) {2036timestamp = ts.getTimestamp();2037noTimestamp = false;2038} else {2039timestamp = null;2040}2041// display the certificate(sb). The first one is end-entity cert and2042// its KeyUsage should be checked.2043boolean first = true;2044StringBuilder sb = new StringBuilder();2045sb.append(tab1).append(rb.getString("...Signer")).append('\n');2046@SuppressWarnings("unchecked")2047List<X509Certificate> chain = (List<X509Certificate>)certs;2048TrustAnchor anchor = findTrustAnchor(chain);2049for (Certificate c : certs) {2050CertPathConstraintsParameters cpcp =2051new CertPathConstraintsParameters((X509Certificate)c, Validator.VAR_CODE_SIGNING, anchor, timestamp);2052sb.append(printCert(false, tab2, c, timestamp, first, cpcp));2053sb.append('\n');2054first = false;2055}2056try {2057validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts);2058} catch (Exception e) {2059chainNotValidated = true;2060chainNotValidatedReason = e;2061sb.append(tab2).append(rb.getString(".Invalid.certificate.chain."))2062.append(e.getLocalizedMessage()).append("]\n");2063}2064if (ts != null) {2065List<? extends Certificate> tscerts = ts.getSignerCertPath().getCertificates();2066@SuppressWarnings("unchecked")2067List<X509Certificate> tschain = (List<X509Certificate>)tscerts;2068anchor = findTrustAnchor(chain);2069sb.append(tab1).append(rb.getString("...TSA")).append('\n');2070for (Certificate c : tschain) {2071CertPathConstraintsParameters cpcp =2072new CertPathConstraintsParameters((X509Certificate)c, Validator.VAR_TSA_SERVER, anchor, timestamp);2073sb.append(printCert(true, tab2, c, null, false, cpcp));2074sb.append('\n');2075}2076try {2077validateCertChain(Validator.VAR_TSA_SERVER,2078ts.getSignerCertPath().getCertificates(), null);2079} catch (Exception e) {2080tsaChainNotValidated = true;2081tsaChainNotValidatedReason = e;2082sb.append(tab2).append(rb.getString(".Invalid.TSA.certificate.chain."))2083.append(e.getLocalizedMessage()).append("]\n");2084}2085}2086if (certs.size() == 12087&& KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) {2088signerSelfSigned = true;2089}20902091return sb.toString();2092}20932094private TrustAnchor findTrustAnchor(List<X509Certificate> chain) {2095X509Certificate last = chain.get(chain.size() - 1);2096Optional<X509Certificate> trusted =2097trustedCerts.stream()2098.filter(c -> c.getSubjectX500Principal().equals(last.getIssuerX500Principal()))2099.findFirst();2100return trusted.isPresent() ? new TrustAnchor(trusted.get(), null) : null;2101}21022103void loadKeyStore(String keyStoreName, boolean prompt) {21042105if (!nullStream && keyStoreName == null) {2106keyStoreName = System.getProperty("user.home") + File.separator2107+ ".keystore";2108}21092110try {2111try {2112KeyStore caks = KeyStoreUtil.getCacertsKeyStore();2113if (caks != null) {2114Enumeration<String> aliases = caks.aliases();2115while (aliases.hasMoreElements()) {2116String a = aliases.nextElement();2117try {2118trustedCerts.add((X509Certificate)caks.getCertificate(a));2119} catch (Exception e2) {2120// ignore, when a SecretkeyEntry does not include a cert2121}2122}2123}2124} catch (Exception e) {2125// Ignore, if cacerts cannot be loaded2126}21272128if (providerName == null) {2129store = KeyStore.getInstance(storetype);2130} else {2131store = KeyStore.getInstance(storetype, providerName);2132}21332134// Get pass phrase2135// XXX need to disable echo; on UNIX, call getpass(char *prompt)Z2136// and on NT call ??2137if (token && storepass == null && !protectedPath2138&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {2139storepass = getPass2140(rb.getString("Enter.Passphrase.for.keystore."));2141} else if (!token && storepass == null && prompt) {2142storepass = getPass2143(rb.getString("Enter.Passphrase.for.keystore."));2144}21452146try {2147if (nullStream) {2148store.load(null, storepass);2149} else {2150keyStoreName = keyStoreName.replace(File.separatorChar, '/');2151URL url = null;2152try {2153url = new URL(keyStoreName);2154} catch (java.net.MalformedURLException e) {2155// try as file2156url = new File(keyStoreName).toURI().toURL();2157}2158InputStream is = null;2159try {2160is = url.openStream();2161store.load(is, storepass);2162} finally {2163if (is != null) {2164is.close();2165}2166}2167}2168Enumeration<String> aliases = store.aliases();2169while (aliases.hasMoreElements()) {2170String a = aliases.nextElement();2171try {2172X509Certificate c = (X509Certificate)store.getCertificate(a);2173// Only add TrustedCertificateEntry and self-signed2174// PrivateKeyEntry2175if (store.isCertificateEntry(a) ||2176c.getSubjectX500Principal().equals(c.getIssuerX500Principal())) {2177trustedCerts.add(c);2178}2179} catch (Exception e2) {2180// ignore, when a SecretkeyEntry does not include a cert2181}2182}2183} finally {2184try {2185pkixParameters = new PKIXBuilderParameters(2186trustedCerts.stream()2187.map(c -> new TrustAnchor(c, null))2188.collect(Collectors.toSet()),2189null);21902191if (revocationCheck) {2192Security.setProperty("ocsp.enable", "true");2193System.setProperty("com.sun.security.enableCRLDP", "true");2194Event.setReportListener(Event.ReporterCategory.CRLCHECK,2195(t, o) -> System.out.println(String.format(rb.getString(t), o)));2196}2197pkixParameters.setRevocationEnabled(revocationCheck);2198} catch (InvalidAlgorithmParameterException ex) {2199// Only if tas is empty2200}2201}2202} catch (IOException ioe) {2203throw new RuntimeException(rb.getString("keystore.load.") +2204ioe.getMessage());2205} catch (java.security.cert.CertificateException ce) {2206throw new RuntimeException(rb.getString("certificate.exception.") +2207ce.getMessage());2208} catch (NoSuchProviderException pe) {2209throw new RuntimeException(rb.getString("keystore.load.") +2210pe.getMessage());2211} catch (NoSuchAlgorithmException nsae) {2212throw new RuntimeException(rb.getString("keystore.load.") +2213nsae.getMessage());2214} catch (KeyStoreException kse) {2215throw new RuntimeException2216(rb.getString("unable.to.instantiate.keystore.class.") +2217kse.getMessage());2218}2219}22202221X509Certificate getTsaCert(String alias) {22222223java.security.cert.Certificate cs = null;22242225try {2226cs = store.getCertificate(alias);2227} catch (KeyStoreException kse) {2228// this never happens, because keystore has been loaded2229}2230if (cs == null || (!(cs instanceof X509Certificate))) {2231MessageFormat form = new MessageFormat(rb.getString2232("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the"));2233Object[] source = {alias, alias};2234error(form.format(source));2235}2236return (X509Certificate) cs;2237}22382239/**2240* Check if userCert is designed to be a code signer2241* @param userCert the certificate to be examined2242* @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,2243* NetscapeCertType has codeSigning flag turned on.2244* If null, the class field badKeyUsage, badExtendedKeyUsage,2245* badNetscapeCertType will be set.2246*/2247void checkCertUsage(X509Certificate userCert, boolean[] bad) {22482249// Can act as a signer?2250// 1. if KeyUsage, then [0:digitalSignature] or2251// [1:nonRepudiation] should be true2252// 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING2253// 3. if NetscapeCertType, then should contains OBJECT_SIGNING2254// 1,2,3 must be true22552256if (bad != null) {2257bad[0] = bad[1] = bad[2] = false;2258}22592260boolean[] keyUsage = userCert.getKeyUsage();2261if (keyUsage != null) {2262keyUsage = Arrays.copyOf(keyUsage, 9);2263if (!keyUsage[0] && !keyUsage[1]) {2264if (bad != null) {2265bad[0] = true;2266badKeyUsage = true;2267}2268}2269}22702271try {2272List<String> xKeyUsage = userCert.getExtendedKeyUsage();2273if (xKeyUsage != null) {2274if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage2275&& !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning2276if (bad != null) {2277bad[1] = true;2278badExtendedKeyUsage = true;2279}2280}2281}2282} catch (java.security.cert.CertificateParsingException e) {2283// shouldn't happen2284}22852286try {2287// OID_NETSCAPE_CERT_TYPE2288byte[] netscapeEx = userCert.getExtensionValue2289("2.16.840.1.113730.1.1");2290if (netscapeEx != null) {2291DerInputStream in = new DerInputStream(netscapeEx);2292byte[] encoded = in.getOctetString();2293encoded = new DerValue(encoded).getUnalignedBitString()2294.toByteArray();22952296NetscapeCertTypeExtension extn =2297new NetscapeCertTypeExtension(encoded);22982299Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING);2300if (!val) {2301if (bad != null) {2302bad[2] = true;2303badNetscapeCertType = true;2304}2305}2306}2307} catch (IOException e) {2308//2309}2310}23112312// Called by signJar().2313void getAliasInfo(String alias) throws Exception {23142315Key key = null;23162317try {2318java.security.cert.Certificate[] cs = null;2319if (altCertChain != null) {2320try (FileInputStream fis = new FileInputStream(altCertChain)) {2321cs = CertificateFactory.getInstance("X.509").2322generateCertificates(fis).2323toArray(new Certificate[0]);2324} catch (FileNotFoundException ex) {2325error(rb.getString("File.specified.by.certchain.does.not.exist"));2326} catch (CertificateException | IOException ex) {2327error(rb.getString("Cannot.restore.certchain.from.file.specified"));2328}2329} else {2330try {2331cs = store.getCertificateChain(alias);2332} catch (KeyStoreException kse) {2333// this never happens, because keystore has been loaded2334}2335}2336if (cs == null || cs.length == 0) {2337if (altCertChain != null) {2338error(rb.getString2339("Certificate.chain.not.found.in.the.file.specified."));2340} else {2341MessageFormat form = new MessageFormat(rb.getString2342("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and"));2343Object[] source = {alias, alias};2344error(form.format(source));2345}2346}23472348certChain = new X509Certificate[cs.length];2349for (int i=0; i<cs.length; i++) {2350if (!(cs[i] instanceof X509Certificate)) {2351error(rb.getString2352("found.non.X.509.certificate.in.signer.s.chain"));2353}2354certChain[i] = (X509Certificate)cs[i];2355}23562357try {2358if (!token && keypass == null)2359key = store.getKey(alias, storepass);2360else2361key = store.getKey(alias, keypass);2362} catch (UnrecoverableKeyException e) {2363if (token) {2364throw e;2365} else if (keypass == null) {2366// Did not work out, so prompt user for key password2367MessageFormat form = new MessageFormat(rb.getString2368("Enter.key.password.for.alias."));2369Object[] source = {alias};2370keypass = getPass(form.format(source));2371key = store.getKey(alias, keypass);2372}2373}2374} catch (NoSuchAlgorithmException e) {2375error(e.getMessage());2376} catch (UnrecoverableKeyException e) {2377error(rb.getString("unable.to.recover.key.from.keystore"));2378} catch (KeyStoreException kse) {2379// this never happens, because keystore has been loaded2380}23812382if (!(key instanceof PrivateKey)) {2383MessageFormat form = new MessageFormat(rb.getString2384("key.associated.with.alias.not.a.private.key"));2385Object[] source = {alias};2386error(form.format(source));2387} else {2388privateKey = (PrivateKey)key;2389}2390}23912392void error(String message) {2393System.out.println(rb.getString("jarsigner.")+message);2394System.exit(1);2395}239623972398void error(String message, Throwable e) {2399System.out.println(rb.getString("jarsigner.")+message);2400if (debug) {2401e.printStackTrace();2402}2403System.exit(1);2404}24052406/**2407* Validates a cert chain.2408*2409* @param parameter this might be a timestamp2410*/2411void validateCertChain(String variant, List<? extends Certificate> certs,2412Timestamp parameter)2413throws Exception {2414try {2415Validator.getInstance(Validator.TYPE_PKIX,2416variant,2417pkixParameters)2418.validate(certs.toArray(new X509Certificate[certs.size()]),2419null, parameter);2420} catch (Exception e) {2421if (debug) {2422e.printStackTrace();2423}24242425// Exception might be dismissed if another warning flag2426// is already set by printCert.24272428if (variant.equals(Validator.VAR_TSA_SERVER) &&2429e instanceof ValidatorException) {2430// Throw cause if it's CertPathValidatorException,2431if (e.getCause() != null &&2432e.getCause() instanceof CertPathValidatorException) {2433e = (Exception) e.getCause();2434Throwable t = e.getCause();2435if ((t instanceof CertificateExpiredException &&2436hasExpiredTsaCert)) {2437// we already have hasExpiredTsaCert2438return;2439}2440}2441}24422443if (variant.equals(Validator.VAR_CODE_SIGNING) &&2444e instanceof ValidatorException) {2445// Throw cause if it's CertPathValidatorException,2446if (e.getCause() != null &&2447e.getCause() instanceof CertPathValidatorException) {2448e = (Exception) e.getCause();2449Throwable t = e.getCause();2450if ((t instanceof CertificateExpiredException &&2451hasExpiredCert) ||2452(t instanceof CertificateNotYetValidException &&2453notYetValidCert)) {2454// we already have hasExpiredCert and notYetValidCert2455return;2456}2457}2458if (e instanceof ValidatorException) {2459ValidatorException ve = (ValidatorException)e;2460if (ve.getErrorType() == ValidatorException.T_EE_EXTENSIONS &&2461(badKeyUsage || badExtendedKeyUsage || badNetscapeCertType)) {2462// We already have badKeyUsage, badExtendedKeyUsage2463// and badNetscapeCertType2464return;2465}2466}2467}2468throw e;2469}2470}24712472char[] getPass(String prompt) {2473System.err.print(prompt);2474System.err.flush();2475try {2476char[] pass = Password.readPassword(System.in);24772478if (pass == null) {2479error(rb.getString("you.must.enter.key.password"));2480} else {2481return pass;2482}2483} catch (IOException ioe) {2484error(rb.getString("unable.to.read.password.")+ioe.getMessage());2485}2486// this shouldn't happen2487return null;2488}2489}249024912492