Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java
38923 views
/*1* Copyright (c) 1997, 2017, 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.security.cert.CertPathValidatorException;29import java.security.cert.PKIXBuilderParameters;30import java.util.*;31import java.util.stream.Collectors;32import java.util.zip.*;33import java.util.jar.*;34import java.math.BigInteger;35import java.net.URI;36import java.net.URISyntaxException;37import java.text.Collator;38import java.text.MessageFormat;39import java.security.cert.Certificate;40import java.security.cert.X509Certificate;41import java.security.cert.CertificateException;42import java.security.*;43import java.lang.reflect.Constructor;4445import com.sun.jarsigner.ContentSigner;46import com.sun.jarsigner.ContentSignerParameters;47import java.net.SocketTimeoutException;48import java.net.URL;49import java.net.URLClassLoader;50import java.security.cert.CertPath;51import java.security.cert.CertificateExpiredException;52import java.security.cert.CertificateFactory;53import java.security.cert.CertificateNotYetValidException;54import java.security.cert.TrustAnchor;55import java.util.Map.Entry;56import sun.security.pkcs.PKCS7;57import sun.security.pkcs.SignerInfo;58import sun.security.timestamp.TimestampToken;59import sun.security.tools.KeyStoreUtil;60import sun.security.tools.PathList;61import sun.security.validator.Validator;62import sun.security.validator.ValidatorException;63import sun.security.x509.*;64import sun.security.util.*;65import java.util.Base64;666768/**69* <p>The jarsigner utility.70*71* The exit codes for the main method are:72*73* 0: success74* 1: any error that the jar cannot be signed or verified, including:75* keystore loading error76* TSP communication error77* jarsigner command line error...78* otherwise: error codes from -strict79*80* @author Roland Schemers81* @author Jan Luehe82*/8384public class Main {8586// for i18n87private static final java.util.ResourceBundle rb =88java.util.ResourceBundle.getBundle89("sun.security.tools.jarsigner.Resources");90private static final Collator collator = Collator.getInstance();91static {92// this is for case insensitive string comparisions93collator.setStrength(Collator.PRIMARY);94}9596private static final String META_INF = "META-INF/";9798private static final Class<?>[] PARAM_STRING = { String.class };99100private static final String NONE = "NONE";101private static final String P11KEYSTORE = "PKCS11";102103private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds104private static final long ONE_YEAR = 366*24*60*60*1000L;105106private static final DisabledAlgorithmConstraints DISABLED_CHECK =107new DisabledAlgorithmConstraints(108DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);109110private static final DisabledAlgorithmConstraints LEGACY_CHECK =111new DisabledAlgorithmConstraints(112DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);113114private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections115.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));116private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections117.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));118119static final String VERSION = "1.0";120121static final int IN_KEYSTORE = 0x01; // signer is in keystore122static final int IN_SCOPE = 0x02;123static final int NOT_ALIAS = 0x04; // alias list is NOT empty and124// signer is not in alias list125static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list126127// Attention:128// This is the entry that get launched by the security tool jarsigner.129public static void main(String args[]) throws Exception {130Main js = new Main();131js.run(args);132}133134X509Certificate[] certChain; // signer's cert chain (when composing)135PrivateKey privateKey; // private key136KeyStore store; // the keystore specified by -keystore137// or the default keystore, never null138139String keystore; // key store file140boolean nullStream = false; // null keystore input stream (NONE)141boolean token = false; // token-based keystore142String jarfile; // jar files to sign or verify143String alias; // alias to sign jar with144List<String> ckaliases = new ArrayList<>(); // aliases in -verify145char[] storepass; // keystore password146boolean protectedPath; // protected authentication path147String storetype; // keystore type148String providerName; // provider name149Vector<String> providers = null; // list of providers150// arguments for provider constructors151HashMap<String,String> providerArgs = new HashMap<>();152char[] keypass; // private key password153String sigfile; // name of .SF file154String sigalg; // name of signature algorithm155String digestalg = "SHA-256"; // name of digest algorithm156String signedjar; // output filename157String tsaUrl; // location of the Timestamping Authority158String tsaAlias; // alias for the Timestamping Authority's certificate159String altCertChain; // file to read alternative cert chain from160String tSAPolicyID;161String tSADigestAlg = "SHA-256";162boolean verify = false; // verify the jar163String verbose = null; // verbose output when signing/verifying164boolean showcerts = false; // show certs when verifying165boolean debug = false; // debug166boolean signManifest = true; // "sign" the whole manifest167boolean externalSF = true; // leave the .SF out of the PKCS7 block168boolean strict = false; // treat warnings as error169170// read zip entry raw bytes171private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);172private byte[] buffer = new byte[8192];173private ContentSigner signingMechanism = null;174private 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 {228parseArgs(args);229230// Try to load and install the specified providers231if (providers != null) {232ClassLoader cl = ClassLoader.getSystemClassLoader();233Enumeration<String> e = providers.elements();234while (e.hasMoreElements()) {235String provName = e.nextElement();236Class<?> provClass;237if (cl != null) {238provClass = cl.loadClass(provName);239} else {240provClass = Class.forName(provName);241}242243String provArg = providerArgs.get(provName);244Object obj;245if (provArg == null) {246obj = provClass.newInstance();247} else {248Constructor<?> c =249provClass.getConstructor(PARAM_STRING);250obj = c.newInstance(provArg);251}252253if (!(obj instanceof Provider)) {254MessageFormat form = new MessageFormat(rb.getString255("provName.not.a.provider"));256Object[] source = {provName};257throw new Exception(form.format(source));258}259Security.addProvider((Provider)obj);260}261}262263if (verify) {264try {265loadKeyStore(keystore, false);266} catch (Exception e) {267if ((keystore != null) || (storepass != null)) {268System.out.println(rb.getString("jarsigner.error.") +269e.getMessage());270System.exit(1);271}272}273/* if (debug) {274SignatureFileVerifier.setDebug(true);275ManifestEntryVerifier.setDebug(true);276}277*/278verifyJar(jarfile);279} else {280loadKeyStore(keystore, true);281getAliasInfo(alias);282283// load the alternative signing mechanism284if (altSignerClass != null) {285signingMechanism = loadSigningMechanism(altSignerClass,286altSignerClasspath);287}288signJar(jarfile, alias, args);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}307}308309if (strict) {310int exitCode = 0;311if (disabledAlg != 0 || chainNotValidated || hasExpiredCert312|| hasExpiredTsaCert || notYetValidCert || signerSelfSigned) {313exitCode |= 4;314}315if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {316exitCode |= 8;317}318if (hasUnsignedEntry) {319exitCode |= 16;320}321if (notSignedByAlias || aliasNotInStore) {322exitCode |= 32;323}324if (tsaChainNotValidated) {325exitCode |= 64;326}327if (exitCode != 0) {328System.exit(exitCode);329}330}331}332333/*334* Parse command line arguments.335*/336void parseArgs(String args[]) {337/* parse flags */338int n = 0;339340if (args.length == 0) fullusage();341for (n=0; n < args.length; n++) {342343String flags = args[n];344String modifier = null;345346if (flags.startsWith("-")) {347int pos = flags.indexOf(':');348if (pos > 0) {349modifier = flags.substring(pos+1);350flags = flags.substring(0, pos);351}352}353354if (!flags.startsWith("-")) {355if (jarfile == null) {356jarfile = flags;357} else {358alias = flags;359ckaliases.add(alias);360}361} else if (collator.compare(flags, "-keystore") == 0) {362if (++n == args.length) usageNoArg();363keystore = args[n];364} else if (collator.compare(flags, "-storepass") ==0) {365if (++n == args.length) usageNoArg();366storepass = getPass(modifier, args[n]);367} else if (collator.compare(flags, "-storetype") ==0) {368if (++n == args.length) usageNoArg();369storetype = args[n];370} else if (collator.compare(flags, "-providerName") ==0) {371if (++n == args.length) usageNoArg();372providerName = args[n];373} else if ((collator.compare(flags, "-provider") == 0) ||374(collator.compare(flags, "-providerClass") == 0)) {375if (++n == args.length) usageNoArg();376if (providers == null) {377providers = new Vector<String>(3);378}379providers.add(args[n]);380381if (args.length > (n+1)) {382flags = args[n+1];383if (collator.compare(flags, "-providerArg") == 0) {384if (args.length == (n+2)) usageNoArg();385providerArgs.put(args[n], args[n+2]);386n += 2;387}388}389} else if (collator.compare(flags, "-protected") ==0) {390protectedPath = true;391} else if (collator.compare(flags, "-certchain") ==0) {392if (++n == args.length) usageNoArg();393altCertChain = args[n];394} else if (collator.compare(flags, "-tsapolicyid") ==0) {395if (++n == args.length) usageNoArg();396tSAPolicyID = args[n];397} else if (collator.compare(flags, "-tsadigestalg") ==0) {398if (++n == args.length) usageNoArg();399tSADigestAlg = args[n];400} else if (collator.compare(flags, "-debug") ==0) {401debug = true;402} else if (collator.compare(flags, "-keypass") ==0) {403if (++n == args.length) usageNoArg();404keypass = getPass(modifier, args[n]);405} else if (collator.compare(flags, "-sigfile") ==0) {406if (++n == args.length) usageNoArg();407sigfile = args[n];408} else if (collator.compare(flags, "-signedjar") ==0) {409if (++n == args.length) usageNoArg();410signedjar = args[n];411} else if (collator.compare(flags, "-tsa") ==0) {412if (++n == args.length) usageNoArg();413tsaUrl = args[n];414} else if (collator.compare(flags, "-tsacert") ==0) {415if (++n == args.length) usageNoArg();416tsaAlias = args[n];417} else if (collator.compare(flags, "-altsigner") ==0) {418if (++n == args.length) usageNoArg();419altSignerClass = args[n];420} else if (collator.compare(flags, "-altsignerpath") ==0) {421if (++n == args.length) usageNoArg();422altSignerClasspath = args[n];423} else if (collator.compare(flags, "-sectionsonly") ==0) {424signManifest = false;425} else if (collator.compare(flags, "-internalsf") ==0) {426externalSF = false;427} else if (collator.compare(flags, "-verify") ==0) {428verify = true;429} else if (collator.compare(flags, "-verbose") ==0) {430verbose = (modifier != null) ? modifier : "all";431} else if (collator.compare(flags, "-sigalg") ==0) {432if (++n == args.length) usageNoArg();433sigalg = args[n];434} else if (collator.compare(flags, "-digestalg") ==0) {435if (++n == args.length) usageNoArg();436digestalg = args[n];437} else if (collator.compare(flags, "-certs") ==0) {438showcerts = true;439} else if (collator.compare(flags, "-strict") ==0) {440strict = true;441} else if (collator.compare(flags, "-h") == 0 ||442collator.compare(flags, "-help") == 0) {443fullusage();444} else {445System.err.println(446rb.getString("Illegal.option.") + flags);447usage();448}449}450451// -certs must always be specified with -verbose452if (verbose == null) showcerts = false;453454if (jarfile == null) {455System.err.println(rb.getString("Please.specify.jarfile.name"));456usage();457}458if (!verify && alias == null) {459System.err.println(rb.getString("Please.specify.alias.name"));460usage();461}462if (!verify && ckaliases.size() > 1) {463System.err.println(rb.getString("Only.one.alias.can.be.specified"));464usage();465}466467if (storetype == null) {468storetype = KeyStore.getDefaultType();469}470storetype = KeyStoreUtil.niceStoreTypeName(storetype);471472try {473if (signedjar != null && new File(signedjar).getCanonicalPath().equals(474new File(jarfile).getCanonicalPath())) {475signedjar = null;476}477} catch (IOException ioe) {478// File system error?479// Just ignore it.480}481482if (P11KEYSTORE.equalsIgnoreCase(storetype) ||483KeyStoreUtil.isWindowsKeyStore(storetype)) {484token = true;485if (keystore == null) {486keystore = NONE;487}488}489490if (NONE.equals(keystore)) {491nullStream = true;492}493494if (token && !nullStream) {495System.err.println(MessageFormat.format(rb.getString496(".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));497usage();498}499500if (token && keypass != null) {501System.err.println(MessageFormat.format(rb.getString502(".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));503usage();504}505506if (protectedPath) {507if (storepass != null || keypass != null) {508System.err.println(rb.getString509("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));510usage();511}512}513if (KeyStoreUtil.isWindowsKeyStore(storetype)) {514if (storepass != null || keypass != null) {515System.err.println(rb.getString516("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));517usage();518}519}520}521522static char[] getPass(String modifier, String arg) {523char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);524if (output != null) return output;525usage();526return null; // Useless, usage() already exit527}528529static void usageNoArg() {530System.out.println(rb.getString("Option.lacks.argument"));531usage();532}533534static void usage() {535System.out.println();536System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));537System.exit(1);538}539540static void fullusage() {541System.out.println(rb.getString542("Usage.jarsigner.options.jar.file.alias"));543System.out.println(rb.getString544(".jarsigner.verify.options.jar.file.alias."));545System.out.println();546System.out.println(rb.getString547(".keystore.url.keystore.location"));548System.out.println();549System.out.println(rb.getString550(".storepass.password.password.for.keystore.integrity"));551System.out.println();552System.out.println(rb.getString553(".storetype.type.keystore.type"));554System.out.println();555System.out.println(rb.getString556(".keypass.password.password.for.private.key.if.different."));557System.out.println();558System.out.println(rb.getString559(".certchain.file.name.of.alternative.certchain.file"));560System.out.println();561System.out.println(rb.getString562(".sigfile.file.name.of.SF.DSA.file"));563System.out.println();564System.out.println(rb.getString565(".signedjar.file.name.of.signed.JAR.file"));566System.out.println();567System.out.println(rb.getString568(".digestalg.algorithm.name.of.digest.algorithm"));569System.out.println();570System.out.println(rb.getString571(".sigalg.algorithm.name.of.signature.algorithm"));572System.out.println();573System.out.println(rb.getString574(".verify.verify.a.signed.JAR.file"));575System.out.println();576System.out.println(rb.getString577(".verbose.suboptions.verbose.output.when.signing.verifying."));578System.out.println(rb.getString579(".suboptions.can.be.all.grouped.or.summary"));580System.out.println();581System.out.println(rb.getString582(".certs.display.certificates.when.verbose.and.verifying"));583System.out.println();584System.out.println(rb.getString585(".tsa.url.location.of.the.Timestamping.Authority"));586System.out.println();587System.out.println(rb.getString588(".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));589System.out.println();590System.out.println(rb.getString591(".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));592System.out.println();593System.out.println(rb.getString594(".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));595System.out.println();596System.out.println(rb.getString597(".altsigner.class.class.name.of.an.alternative.signing.mechanism"));598System.out.println();599System.out.println(rb.getString600(".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));601System.out.println();602System.out.println(rb.getString603(".internalsf.include.the.SF.file.inside.the.signature.block"));604System.out.println();605System.out.println(rb.getString606(".sectionsonly.don.t.compute.hash.of.entire.manifest"));607System.out.println();608System.out.println(rb.getString609(".protected.keystore.has.protected.authentication.path"));610System.out.println();611System.out.println(rb.getString612(".providerName.name.provider.name"));613System.out.println();614System.out.println(rb.getString615(".providerClass.class.name.of.cryptographic.service.provider.s"));616System.out.println(rb.getString617(".providerArg.arg.master.class.file.and.constructor.argument"));618System.out.println();619System.out.println(rb.getString620(".strict.treat.warnings.as.errors"));621System.out.println();622623System.exit(0);624}625626void verifyJar(String jarName)627throws Exception628{629boolean anySigned = false; // if there exists entry inside jar signed630JarFile jf = null;631Map<String,String> digestMap = new HashMap<>();632Map<String,PKCS7> sigMap = new HashMap<>();633Map<String,String> sigNameMap = new HashMap<>();634Map<String,String> unparsableSignatures = new HashMap<>();635636try {637jf = new JarFile(jarName, true);638Vector<JarEntry> entriesVec = new Vector<>();639byte[] buffer = new byte[8192];640641Enumeration<JarEntry> entries = jf.entries();642while (entries.hasMoreElements()) {643JarEntry je = entries.nextElement();644entriesVec.addElement(je);645try (InputStream is = jf.getInputStream(je)) {646String name = je.getName();647if (signatureRelated(name)648&& SignatureFileVerifier.isBlockOrSF(name)) {649String alias = name.substring(name.lastIndexOf('/') + 1,650name.lastIndexOf('.'));651try {652if (name.endsWith(".SF")) {653Manifest sf = new Manifest(is);654boolean found = false;655for (Object obj : sf.getMainAttributes().keySet()) {656String key = obj.toString();657if (key.endsWith("-Digest-Manifest")) {658digestMap.put(alias,659key.substring(0, key.length() - 16));660found = true;661break;662}663}664if (!found) {665unparsableSignatures.putIfAbsent(alias,666String.format(667rb.getString("history.unparsable"),668name));669}670} else {671sigNameMap.put(alias, name);672sigMap.put(alias, new PKCS7(is));673}674} catch (IOException ioe) {675unparsableSignatures.putIfAbsent(alias, String.format(676rb.getString("history.unparsable"), name));677}678} else {679while (is.read(buffer, 0, buffer.length) != -1) {680// we just read. this will throw a SecurityException681// if a signature/digest check fails.682}683}684}685}686687Manifest man = jf.getManifest();688boolean hasSignature = false;689690// The map to record display info, only used when -verbose provided691// key: signer info string692// value: the list of files with common key693Map<String,List<String>> output = new LinkedHashMap<>();694695if (man != null) {696if (verbose != null) System.out.println();697Enumeration<JarEntry> e = entriesVec.elements();698699String tab = rb.getString("6SPACE");700701while (e.hasMoreElements()) {702JarEntry je = e.nextElement();703String name = je.getName();704705hasSignature = hasSignature706|| SignatureFileVerifier.isBlockOrSF(name);707708CodeSigner[] signers = je.getCodeSigners();709boolean isSigned = (signers != null);710anySigned |= isSigned;711hasUnsignedEntry |= !je.isDirectory() && !isSigned712&& !signatureRelated(name);713714int inStoreOrScope = inKeyStore(signers);715716boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;717boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;718719notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;720if (keystore != null) {721aliasNotInStore |= isSigned && (!inStore && !inScope);722}723724// Only used when -verbose provided725StringBuffer sb = null;726if (verbose != null) {727sb = new StringBuffer();728boolean inManifest =729((man.getAttributes(name) != null) ||730(man.getAttributes("./"+name) != null) ||731(man.getAttributes("/"+name) != null));732sb.append(733(isSigned ? rb.getString("s") : rb.getString("SPACE")) +734(inManifest ? rb.getString("m") : rb.getString("SPACE")) +735(inStore ? rb.getString("k") : rb.getString("SPACE")) +736(inScope ? rb.getString("i") : rb.getString("SPACE")) +737((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +738rb.getString("SPACE"));739sb.append("|");740}741742// When -certs provided, display info has extra empty743// lines at the beginning and end.744if (isSigned) {745if (showcerts) sb.append('\n');746for (CodeSigner signer: signers) {747// signerInfo() must be called even if -verbose748// not provided. The method updates various749// warning flags.750String si = signerInfo(signer, tab);751if (showcerts) {752sb.append(si);753sb.append('\n');754}755}756} else if (showcerts && !verbose.equals("all")) {757// Print no info for unsigned entries when -verbose:all,758// to be consistent with old behavior.759if (signatureRelated(name)) {760sb.append("\n" + tab + rb.getString(761".Signature.related.entries.") + "\n\n");762} else {763sb.append("\n" + tab + rb.getString(764".Unsigned.entries.") + "\n\n");765}766}767768if (verbose != null) {769String label = sb.toString();770if (signatureRelated(name)) {771// Entries inside META-INF and other unsigned772// entries are grouped separately.773label = "-" + label;774}775776// The label finally contains 2 parts separated by '|':777// The legend displayed before the entry names, and778// the cert info (if -certs specified).779780if (!output.containsKey(label)) {781output.put(label, new ArrayList<String>());782}783784StringBuffer fb = new StringBuffer();785String s = Long.toString(je.getSize());786for (int i = 6 - s.length(); i > 0; --i) {787fb.append(' ');788}789fb.append(s).append(' ').790append(new Date(je.getTime()).toString());791fb.append(' ').append(name);792793output.get(label).add(fb.toString());794}795}796}797if (verbose != null) {798for (Entry<String,List<String>> s: output.entrySet()) {799List<String> files = s.getValue();800String key = s.getKey();801if (key.charAt(0) == '-') { // the signature-related group802key = key.substring(1);803}804int pipe = key.indexOf('|');805if (verbose.equals("all")) {806for (String f: files) {807System.out.println(key.substring(0, pipe) + f);808System.out.printf(key.substring(pipe+1));809}810} else {811if (verbose.equals("grouped")) {812for (String f: files) {813System.out.println(key.substring(0, pipe) + f);814}815} else if (verbose.equals("summary")) {816System.out.print(key.substring(0, pipe));817if (files.size() > 1) {818System.out.println(files.get(0) + " " +819String.format(rb.getString(820".and.d.more."), files.size()-1));821} else {822System.out.println(files.get(0));823}824}825System.out.printf(key.substring(pipe+1));826}827}828System.out.println();829System.out.println(rb.getString(830".s.signature.was.verified."));831System.out.println(rb.getString(832".m.entry.is.listed.in.manifest"));833System.out.println(rb.getString(834".k.at.least.one.certificate.was.found.in.keystore"));835System.out.println(rb.getString(836".i.at.least.one.certificate.was.found.in.identity.scope"));837if (ckaliases.size() > 0) {838System.out.println(rb.getString(839".X.not.signed.by.specified.alias.es."));840}841}842if (man == null) {843System.out.println();844System.out.println(rb.getString("no.manifest."));845}846847// Even if the verbose option is not specified, all out strings848// must be generated so disabledAlgFound can be updated.849if (!digestMap.isEmpty()850|| !sigMap.isEmpty()851|| !unparsableSignatures.isEmpty()) {852if (verbose != null) {853System.out.println();854}855for (String s : sigMap.keySet()) {856if (!digestMap.containsKey(s)) {857unparsableSignatures.putIfAbsent(s, String.format(858rb.getString("history.nosf"), s));859}860}861for (String s : digestMap.keySet()) {862PKCS7 p7 = sigMap.get(s);863if (p7 != null) {864String history;865try {866SignerInfo si = p7.getSignerInfos()[0];867X509Certificate signer = si.getCertificate(p7);868String digestAlg = digestMap.get(s);869String sigAlg = AlgorithmId.makeSigAlg(870si.getDigestAlgorithmId().getName(),871si.getDigestEncryptionAlgorithmId().getName());872PublicKey key = signer.getPublicKey();873PKCS7 tsToken = si.getTsToken();874if (tsToken != null) {875hasTimestampBlock = true;876SignerInfo tsSi = tsToken.getSignerInfos()[0];877X509Certificate tsSigner = tsSi.getCertificate(tsToken);878byte[] encTsTokenInfo = tsToken.getContentInfo().getData();879TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);880PublicKey tsKey = tsSigner.getPublicKey();881String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();882String tsSigAlg = AlgorithmId.makeSigAlg(883tsSi.getDigestAlgorithmId().getName(),884tsSi.getDigestEncryptionAlgorithmId().getName());885Calendar c = Calendar.getInstance(886TimeZone.getTimeZone("UTC"),887Locale.getDefault(Locale.Category.FORMAT));888c.setTime(tsTokenInfo.getDate());889history = String.format(890rb.getString("history.with.ts"),891signer.getSubjectX500Principal(),892verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false),893verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false),894verifyWithWeak(key),895c,896tsSigner.getSubjectX500Principal(),897verifyWithWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET, true),898verifyWithWeak(tsSigAlg, SIG_PRIMITIVE_SET, true),899verifyWithWeak(tsKey));900} else {901history = String.format(902rb.getString("history.without.ts"),903signer.getSubjectX500Principal(),904verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false),905verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false),906verifyWithWeak(key));907}908} catch (Exception e) {909// The only usage of sigNameMap, remember the name910// of the block file if it's invalid.911history = String.format(912rb.getString("history.unparsable"),913sigNameMap.get(s));914}915if (verbose != null) {916System.out.println(history);917}918} else {919unparsableSignatures.putIfAbsent(s, String.format(920rb.getString("history.nobk"), s));921}922}923if (verbose != null) {924for (String s : unparsableSignatures.keySet()) {925System.out.println(unparsableSignatures.get(s));926}927}928}929System.out.println();930931// If signer is a trusted cert or private entry in user's own932// keystore, it can be self-signed. Please note aliasNotInStore933// is always false when ~/.keystore is used.934if (!aliasNotInStore && keystore != null) {935signerSelfSigned = false;936}937938if (!anySigned) {939if (disabledAlgFound) {940if (verbose != null) {941System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));942System.out.println("\n " +943DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +944"=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));945} else {946System.out.println(rb.getString("jar.treated.unsigned.see.weak"));947}948} else if (hasSignature) {949System.out.println(rb.getString("jar.treated.unsigned"));950} else {951System.out.println(rb.getString("jar.is.unsigned"));952}953} else {954displayMessagesAndResult(false);955}956return;957} catch (Exception e) {958System.out.println(rb.getString("jarsigner.") + e);959if (debug) {960e.printStackTrace();961}962} finally { // close the resource963if (jf != null) {964jf.close();965}966}967968System.exit(1);969}970971private void displayMessagesAndResult(boolean isSigning) {972String result;973List<String> errors = new ArrayList<>();974List<String> warnings = new ArrayList<>();975List<String> info = new ArrayList<>();976977boolean signerNotExpired = expireDate == null978|| expireDate.after(new Date());979980if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||981notYetValidCert || chainNotValidated || hasExpiredCert ||982hasUnsignedEntry || signerSelfSigned || (legacyAlg != 0) ||983(disabledAlg != 0) || aliasNotInStore || notSignedByAlias ||984tsaChainNotValidated ||985(hasExpiredTsaCert && !signerNotExpired)) {986987if (strict) {988result = rb.getString(isSigning989? "jar.signed.with.signer.errors."990: "jar.verified.with.signer.errors.");991} else {992result = rb.getString(isSigning993? "jar.signed."994: "jar.verified.");995}996997if (badKeyUsage) {998errors.add(rb.getString(isSigning999? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."1000: "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));1001}10021003if (badExtendedKeyUsage) {1004errors.add(rb.getString(isSigning1005? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."1006: "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));1007}10081009if (badNetscapeCertType) {1010errors.add(rb.getString(isSigning1011? "The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."1012: "This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));1013}10141015// only in verifying1016if (hasUnsignedEntry) {1017errors.add(rb.getString(1018"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));1019}1020if (hasExpiredCert) {1021errors.add(rb.getString(isSigning1022? "The.signer.certificate.has.expired."1023: "This.jar.contains.entries.whose.signer.certificate.has.expired."));1024}1025if (notYetValidCert) {1026errors.add(rb.getString(isSigning1027? "The.signer.certificate.is.not.yet.valid."1028: "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));1029}10301031if (chainNotValidated) {1032errors.add(String.format(rb.getString(isSigning1033? "The.signer.s.certificate.chain.is.invalid.reason.1"1034: "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),1035chainNotValidatedReason.getLocalizedMessage()));1036}10371038if (hasExpiredTsaCert) {1039errors.add(rb.getString("The.timestamp.has.expired."));1040}1041if (tsaChainNotValidated) {1042errors.add(String.format(rb.getString(isSigning1043? "The.tsa.certificate.chain.is.invalid.reason.1"1044: "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),1045tsaChainNotValidatedReason.getLocalizedMessage()));1046}10471048// only in verifying1049if (notSignedByAlias) {1050errors.add(1051rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));1052}10531054// only in verifying1055if (aliasNotInStore) {1056errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));1057}10581059if (signerSelfSigned) {1060errors.add(rb.getString(isSigning1061? "The.signer.s.certificate.is.self.signed."1062: "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));1063}10641065if (isSigning) {1066if ((legacyAlg & 1) == 1) {1067warnings.add(String.format(1068rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1069digestalg, "-digestalg"));1070}10711072if ((disabledAlg & 1) == 1) {1073errors.add(String.format(1074rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),1075digestalg, "-digestalg"));1076}10771078if ((legacyAlg & 2) == 2) {1079warnings.add(String.format(1080rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1081sigalg, "-sigalg"));1082}1083if ((disabledAlg & 2) == 2) {1084errors.add(String.format(1085rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),1086sigalg, "-sigalg"));1087}10881089if ((legacyAlg & 4) == 4) {1090warnings.add(String.format(1091rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1092tSADigestAlg, "-tsadigestalg"));1093}1094if ((disabledAlg & 4) == 4) {1095errors.add(String.format(1096rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),1097tSADigestAlg, "-tsadigestalg"));1098}10991100if ((legacyAlg & 8) == 8) {1101warnings.add(String.format(1102rb.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."),1103privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));1104}1105if ((disabledAlg & 8) == 8) {1106errors.add(String.format(1107rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk.and.is.disabled."),1108privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));1109}1110} else {1111if ((legacyAlg & 1) != 0) {1112warnings.add(String.format(1113rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1114legacyDigestAlg));1115}11161117if ((legacyAlg & 2) == 2) {1118warnings.add(String.format(1119rb.getString("The.signature.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1120legacySigAlg));1121}11221123if ((legacyAlg & 4) != 0) {1124warnings.add(String.format(1125rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),1126legacyTsaDigestAlg));1127}11281129if ((legacyAlg & 8) == 8) {1130warnings.add(String.format(1131rb.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."),1132weakPublicKey.getAlgorithm(), KeyUtil.getKeySize(weakPublicKey)));1133}1134}1135} else {1136result = rb.getString(isSigning ? "jar.signed." : "jar.verified.");1137}11381139if (hasExpiredTsaCert) {1140// No need to warn about expiring if already expired1141hasExpiringTsaCert = false;1142}11431144if (hasExpiringCert ||1145(hasExpiringTsaCert && expireDate != null) ||1146(noTimestamp && expireDate != null) ||1147(hasExpiredTsaCert && signerNotExpired)) {11481149if (hasExpiredTsaCert && signerNotExpired) {1150if (expireDate != null) {1151warnings.add(String.format(1152rb.getString("The.timestamp.expired.1.but.usable.2"),1153tsaExpireDate,1154expireDate));1155}1156// Reset the flag so exit code is 01157hasExpiredTsaCert = false;1158}1159if (hasExpiringCert) {1160warnings.add(rb.getString(isSigning1161? "The.signer.certificate.will.expire.within.six.months."1162: "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));1163}1164if (hasExpiringTsaCert && expireDate != null) {1165if (expireDate.after(tsaExpireDate)) {1166warnings.add(String.format(rb.getString(1167"The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate));1168} else {1169warnings.add(String.format(rb.getString(1170"The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate));1171}1172}1173if (noTimestamp && expireDate != null) {1174if (hasTimestampBlock) {1175warnings.add(String.format(rb.getString(isSigning1176? "invalid.timestamp.signing"1177: "bad.timestamp.verifying"), expireDate));1178} else {1179warnings.add(String.format(rb.getString(isSigning1180? "no.timestamp.signing"1181: "no.timestamp.verifying"), expireDate));1182}1183}1184}11851186System.out.println(result);1187if (strict) {1188if (!errors.isEmpty()) {1189System.out.println();1190System.out.println(rb.getString("Error."));1191errors.forEach(System.out::println);1192}1193if (!warnings.isEmpty()) {1194System.out.println();1195System.out.println(rb.getString("Warning."));1196warnings.forEach(System.out::println);1197}1198} else {1199if (!errors.isEmpty() || !warnings.isEmpty()) {1200System.out.println();1201System.out.println(rb.getString("Warning."));1202errors.forEach(System.out::println);1203warnings.forEach(System.out::println);1204}1205}1206if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {1207if (! (verbose != null && showcerts)) {1208System.out.println();1209System.out.println(rb.getString(1210"Re.run.with.the.verbose.and.certs.options.for.more.details."));1211}1212}12131214if (isSigning || verbose != null) {1215// Always print out expireDate, unless expired or expiring.1216if (!hasExpiringCert && !hasExpiredCert1217&& expireDate != null && signerNotExpired) {1218info.add(String.format(rb.getString(1219"The.signer.certificate.will.expire.on.1."), expireDate));1220}1221if (!noTimestamp) {1222if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) {1223if (signerNotExpired) {1224info.add(String.format(rb.getString(1225"The.timestamp.will.expire.on.1."), tsaExpireDate));1226} else {1227info.add(String.format(rb.getString(1228"signer.cert.expired.1.but.timestamp.good.2."),1229expireDate,1230tsaExpireDate));1231}1232}1233}1234}12351236if (!info.isEmpty()) {1237System.out.println();1238info.forEach(System.out::println);1239}1240}12411242private String verifyWithWeak(String alg, Set<CryptoPrimitive> primitiveSet, boolean tsa) {1243if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {1244if (LEGACY_CHECK.permits(primitiveSet, alg, null)) {1245return alg;1246} else {1247if (primitiveSet == SIG_PRIMITIVE_SET) {1248legacyAlg |= 2;1249legacySigAlg = alg;1250} else {1251if (tsa) {1252legacyAlg |= 4;1253legacyTsaDigestAlg = alg;1254} else {1255legacyAlg |= 1;1256legacyDigestAlg = alg;1257}1258}1259return String.format(rb.getString("with.weak"), alg);1260}1261} else {1262disabledAlgFound = true;1263return String.format(rb.getString("with.disabled"), alg);1264}1265}12661267private String verifyWithWeak(PublicKey key) {1268int kLen = KeyUtil.getKeySize(key);1269if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {1270if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {1271if (kLen >= 0) {1272return String.format(rb.getString("key.bit"), kLen);1273} else {1274return rb.getString("unknown.size");1275}1276} else {1277weakPublicKey = key;1278legacyAlg |= 8;1279return String.format(rb.getString("key.bit.weak"), kLen);1280}1281} else {1282disabledAlgFound = true;1283return String.format(rb.getString("key.bit.disabled"), kLen);1284}1285}12861287private void checkWeakSign(String alg, Set<CryptoPrimitive> primitiveSet, boolean tsa) {1288if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {1289if (!LEGACY_CHECK.permits(primitiveSet, alg, null)) {1290if (primitiveSet == SIG_PRIMITIVE_SET) {1291legacyAlg |= 2;1292} else {1293if (tsa) {1294legacyAlg |= 4;1295} else {1296legacyAlg |= 1;1297}1298}1299}1300} else {1301if (primitiveSet == SIG_PRIMITIVE_SET) {1302disabledAlg |= 2;1303} else {1304if (tsa) {1305disabledAlg |= 4;1306} else {1307disabledAlg |= 1;1308}1309}1310}1311}13121313private void checkWeakSign(PrivateKey key) {1314if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {1315if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {1316legacyAlg |= 8;1317}1318} else {1319disabledAlg |= 8;1320}1321}13221323private static MessageFormat validityTimeForm = null;1324private static MessageFormat notYetTimeForm = null;1325private static MessageFormat expiredTimeForm = null;1326private static MessageFormat expiringTimeForm = null;13271328/**1329* Returns a string about a certificate:1330*1331* [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]1332* [<validity-period> | <expiry-warning>]1333* [<key-usage-warning>]1334*1335* Note: no newline character at the end.1336*1337* This method sets global flags like hasExpiringCert, hasExpiredCert,1338* notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType,1339* hasExpiringTsaCert, hasExpiredTsaCert.1340*1341* @param isTsCert true if c is in the TSA cert chain, false otherwise.1342* @param checkUsage true to check code signer keyUsage1343*/1344String printCert(boolean isTsCert, String tab, Certificate c,1345Date timestamp, boolean checkUsage) throws Exception {13461347StringBuilder certStr = new StringBuilder();1348String space = rb.getString("SPACE");1349X509Certificate x509Cert = null;13501351if (c instanceof X509Certificate) {1352x509Cert = (X509Certificate) c;1353certStr.append(tab).append(x509Cert.getType())1354.append(rb.getString("COMMA"))1355.append(x509Cert.getSubjectDN().getName());1356} else {1357certStr.append(tab).append(c.getType());1358}13591360String alias = storeHash.get(c);1361if (alias != null) {1362certStr.append(space).append(alias);1363}13641365if (x509Cert != null) {13661367certStr.append("\n").append(tab).append("[");13681369if (trustedCerts.contains(x509Cert)) {1370certStr.append(rb.getString("trusted.certificate"));1371} else {1372Date notAfter = x509Cert.getNotAfter();1373try {1374boolean printValidity = true;1375if (isTsCert) {1376if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) {1377tsaExpireDate = notAfter;1378}1379} else {1380if (expireDate == null || expireDate.after(notAfter)) {1381expireDate = notAfter;1382}1383}1384if (timestamp == null) {1385x509Cert.checkValidity();1386// test if cert will expire within six months (or one year for tsa)1387long age = isTsCert ? ONE_YEAR : SIX_MONTHS;1388if (notAfter.getTime() < System.currentTimeMillis() + age) {1389if (isTsCert) {1390hasExpiringTsaCert = true;1391} else {1392hasExpiringCert = true;1393}1394if (expiringTimeForm == null) {1395expiringTimeForm = new MessageFormat(1396rb.getString("certificate.will.expire.on"));1397}1398Object[] source = {notAfter};1399certStr.append(expiringTimeForm.format(source));1400printValidity = false;1401}1402} else {1403x509Cert.checkValidity(timestamp);1404}1405if (printValidity) {1406if (validityTimeForm == null) {1407validityTimeForm = new MessageFormat(1408rb.getString("certificate.is.valid.from"));1409}1410Object[] source = {x509Cert.getNotBefore(), notAfter};1411certStr.append(validityTimeForm.format(source));1412}1413} catch (CertificateExpiredException cee) {1414if (isTsCert) {1415hasExpiredTsaCert = true;1416} else {1417hasExpiredCert = true;1418}14191420if (expiredTimeForm == null) {1421expiredTimeForm = new MessageFormat(1422rb.getString("certificate.expired.on"));1423}1424Object[] source = {notAfter};1425certStr.append(expiredTimeForm.format(source));14261427} catch (CertificateNotYetValidException cnyve) {1428if (!isTsCert) notYetValidCert = true;14291430if (notYetTimeForm == null) {1431notYetTimeForm = new MessageFormat(1432rb.getString("certificate.is.not.valid.until"));1433}1434Object[] source = {x509Cert.getNotBefore()};1435certStr.append(notYetTimeForm.format(source));1436}1437}1438certStr.append("]");14391440if (checkUsage) {1441boolean[] bad = new boolean[3];1442checkCertUsage(x509Cert, bad);1443if (bad[0] || bad[1] || bad[2]) {1444String x = "";1445if (bad[0]) {1446x ="KeyUsage";1447}1448if (bad[1]) {1449if (x.length() > 0) x = x + ", ";1450x = x + "ExtendedKeyUsage";1451}1452if (bad[2]) {1453if (x.length() > 0) x = x + ", ";1454x = x + "NetscapeCertType";1455}1456certStr.append("\n").append(tab)1457.append(MessageFormat.format(rb.getString(1458".{0}.extension.does.not.support.code.signing."), x));1459}1460}1461}1462return certStr.toString();1463}14641465private static MessageFormat signTimeForm = null;14661467private String printTimestamp(String tab, Timestamp timestamp) {14681469if (signTimeForm == null) {1470signTimeForm =1471new MessageFormat(rb.getString("entry.was.signed.on"));1472}1473Object[] source = { timestamp.getTimestamp() };14741475return new StringBuilder().append(tab).append("[")1476.append(signTimeForm.format(source)).append("]").toString();1477}14781479private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>();14801481private int inKeyStoreForOneSigner(CodeSigner signer) {1482if (cacheForInKS.containsKey(signer)) {1483return cacheForInKS.get(signer);1484}14851486boolean found = false;1487int result = 0;1488List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();1489for (Certificate c : certs) {1490String alias = storeHash.get(c);1491if (alias != null) {1492if (alias.startsWith("(")) {1493result |= IN_KEYSTORE;1494} else if (alias.startsWith("[")) {1495result |= IN_SCOPE;1496}1497if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {1498result |= SIGNED_BY_ALIAS;1499}1500} else {1501if (store != null) {1502try {1503alias = store.getCertificateAlias(c);1504} catch (KeyStoreException kse) {1505// never happens, because keystore has been loaded1506}1507if (alias != null) {1508storeHash.put(c, "(" + alias + ")");1509found = true;1510result |= IN_KEYSTORE;1511}1512}1513if (ckaliases.contains(alias)) {1514result |= SIGNED_BY_ALIAS;1515}1516}1517}1518cacheForInKS.put(signer, result);1519return result;1520}15211522Hashtable<Certificate, String> storeHash = new Hashtable<>();15231524int inKeyStore(CodeSigner[] signers) {15251526if (signers == null)1527return 0;15281529int output = 0;15301531for (CodeSigner signer: signers) {1532int result = inKeyStoreForOneSigner(signer);1533output |= result;1534}1535if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {1536output |= NOT_ALIAS;1537}1538return output;1539}15401541void signJar(String jarName, String alias, String[] args)1542throws Exception {15431544checkWeakSign(digestalg, DIGEST_PRIMITIVE_SET, false);1545checkWeakSign(tSADigestAlg, DIGEST_PRIMITIVE_SET, true);1546/*1547* If no signature algorithm was specified, we choose a1548* default that is compatible with the private key algorithm.1549*/1550if (sigalg == null) {1551sigalg = getDefaultSignatureAlgorithm(privateKey);1552}1553checkWeakSign(sigalg, SIG_PRIMITIVE_SET, false);15541555checkWeakSign(privateKey);15561557boolean aliasUsed = false;1558X509Certificate tsaCert = null;15591560if (sigfile == null) {1561sigfile = alias;1562aliasUsed = true;1563}15641565if (sigfile.length() > 8) {1566sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH);1567} else {1568sigfile = sigfile.toUpperCase(Locale.ENGLISH);1569}15701571StringBuilder tmpSigFile = new StringBuilder(sigfile.length());1572for (int j = 0; j < sigfile.length(); j++) {1573char c = sigfile.charAt(j);1574if (!1575((c>= 'A' && c<= 'Z') ||1576(c>= '0' && c<= '9') ||1577(c == '-') ||1578(c == '_'))) {1579if (aliasUsed) {1580// convert illegal characters from the alias to be _'s1581c = '_';1582} else {1583throw new1584RuntimeException(rb.getString1585("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));1586}1587}1588tmpSigFile.append(c);1589}15901591sigfile = tmpSigFile.toString();15921593String tmpJarName;1594if (signedjar == null) tmpJarName = jarName+".sig";1595else tmpJarName = signedjar;15961597File jarFile = new File(jarName);1598File signedJarFile = new File(tmpJarName);15991600// Open the jar (zip) file1601try {1602zipFile = new ZipFile(jarName);1603} catch (IOException ioe) {1604error(rb.getString("unable.to.open.jar.file.")+jarName, ioe);1605}16061607FileOutputStream fos = null;1608try {1609fos = new FileOutputStream(signedJarFile);1610} catch (IOException ioe) {1611error(rb.getString("unable.to.create.")+tmpJarName, ioe);1612}16131614PrintStream ps = new PrintStream(fos);1615ZipOutputStream zos = new ZipOutputStream(ps);16161617/* First guess at what they might be - we don't xclude RSA ones. */1618String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH);1619String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH);16201621Manifest manifest = new Manifest();1622Map<String,Attributes> mfEntries = manifest.getEntries();16231624// The Attributes of manifest before updating1625Attributes oldAttr = null;16261627boolean mfModified = false;1628boolean mfCreated = false;1629byte[] mfRawBytes = null;16301631try {1632MessageDigest digests[] = { MessageDigest.getInstance(digestalg) };16331634// Check if manifest exists1635ZipEntry mfFile;1636if ((mfFile = getManifestFile(zipFile)) != null) {1637// Manifest exists. Read its raw bytes.1638mfRawBytes = getBytes(zipFile, mfFile);1639manifest.read(new ByteArrayInputStream(mfRawBytes));1640oldAttr = (Attributes)(manifest.getMainAttributes().clone());1641} else {1642// Create new manifest1643Attributes mattr = manifest.getMainAttributes();1644mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),1645"1.0");1646String javaVendor = System.getProperty("java.vendor");1647String jdkVersion = System.getProperty("java.version");1648mattr.putValue("Created-By", jdkVersion + " (" +javaVendor1649+ ")");1650mfFile = new ZipEntry(JarFile.MANIFEST_NAME);1651mfCreated = true;1652}16531654/*1655* For each entry in jar1656* (except for signature-related META-INF entries),1657* do the following:1658*1659* - if entry is not contained in manifest, add it to manifest;1660* - if entry is contained in manifest, calculate its hash and1661* compare it with the one in the manifest; if they are1662* different, replace the hash in the manifest with the newly1663* generated one. (This may invalidate existing signatures!)1664*/1665Vector<ZipEntry> mfFiles = new Vector<>();16661667boolean wasSigned = false;16681669for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();1670enum_.hasMoreElements();) {1671ZipEntry ze = enum_.nextElement();16721673if (ze.getName().startsWith(META_INF)) {1674// Store META-INF files in vector, so they can be written1675// out first1676mfFiles.addElement(ze);16771678if (SignatureFileVerifier.isBlockOrSF(1679ze.getName().toUpperCase(Locale.ENGLISH))) {1680wasSigned = true;1681}16821683if (signatureRelated(ze.getName())) {1684// ignore signature-related and manifest files1685continue;1686}1687}16881689if (manifest.getAttributes(ze.getName()) != null) {1690// jar entry is contained in manifest, check and1691// possibly update its digest attributes1692if (updateDigests(ze, zipFile, digests,1693manifest) == true) {1694mfModified = true;1695}1696} else if (!ze.isDirectory()) {1697// Add entry to manifest1698Attributes attrs = getDigestAttributes(ze, zipFile,1699digests);1700mfEntries.put(ze.getName(), attrs);1701mfModified = true;1702}1703}17041705// Recalculate the manifest raw bytes if necessary1706if (mfModified) {1707ByteArrayOutputStream baos = new ByteArrayOutputStream();1708manifest.write(baos);1709if (wasSigned) {1710byte[] newBytes = baos.toByteArray();1711if (mfRawBytes != null1712&& oldAttr.equals(manifest.getMainAttributes())) {17131714/*1715* Note:1716*1717* The Attributes object is based on HashMap and can handle1718* continuation columns. Therefore, even if the contents are1719* not changed (in a Map view), the bytes that it write()1720* may be different from the original bytes that it read()1721* from. Since the signature on the main attributes is based1722* on raw bytes, we must retain the exact bytes.1723*/17241725int newPos = findHeaderEnd(newBytes);1726int oldPos = findHeaderEnd(mfRawBytes);17271728if (newPos == oldPos) {1729System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);1730} else {1731// cat oldHead newTail > newBytes1732byte[] lastBytes = new byte[oldPos +1733newBytes.length - newPos];1734System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);1735System.arraycopy(newBytes, newPos, lastBytes, oldPos,1736newBytes.length - newPos);1737newBytes = lastBytes;1738}1739}1740mfRawBytes = newBytes;1741} else {1742mfRawBytes = baos.toByteArray();1743}1744}17451746// Write out the manifest1747if (mfModified) {1748// manifest file has new length1749mfFile = new ZipEntry(JarFile.MANIFEST_NAME);1750}1751if (verbose != null) {1752if (mfCreated) {1753System.out.println(rb.getString(".adding.") +1754mfFile.getName());1755} else if (mfModified) {1756System.out.println(rb.getString(".updating.") +1757mfFile.getName());1758}1759}1760zos.putNextEntry(mfFile);1761zos.write(mfRawBytes);17621763// Calculate SignatureFile (".SF") and SignatureBlockFile1764ManifestDigester manDig = new ManifestDigester(mfRawBytes);1765SignatureFile sf = new SignatureFile(digests, manifest, manDig,1766sigfile, signManifest);17671768if (tsaAlias != null) {1769tsaCert = getTsaCert(tsaAlias);1770}17711772if (tsaUrl == null && tsaCert == null) {1773noTimestamp = true;1774}17751776SignatureFile.Block block = null;17771778try {1779block =1780sf.generateBlock(privateKey, sigalg, certChain,1781externalSF, tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg,1782signingMechanism, args, zipFile);1783} catch (SocketTimeoutException e) {1784// Provide a helpful message when TSA is beyond a firewall1785error(rb.getString("unable.to.sign.jar.") +1786rb.getString("no.response.from.the.Timestamping.Authority.") +1787"\n -J-Dhttp.proxyHost=<hostname>" +1788"\n -J-Dhttp.proxyPort=<portnumber>\n" +1789rb.getString("or") +1790"\n -J-Dhttps.proxyHost=<hostname> " +1791"\n -J-Dhttps.proxyPort=<portnumber> ", e);1792}17931794sfFilename = sf.getMetaName();1795bkFilename = block.getMetaName();17961797ZipEntry sfFile = new ZipEntry(sfFilename);1798ZipEntry bkFile = new ZipEntry(bkFilename);17991800long time = System.currentTimeMillis();1801sfFile.setTime(time);1802bkFile.setTime(time);18031804// signature file1805zos.putNextEntry(sfFile);1806sf.write(zos);1807if (verbose != null) {1808if (zipFile.getEntry(sfFilename) != null) {1809System.out.println(rb.getString(".updating.") +1810sfFilename);1811} else {1812System.out.println(rb.getString(".adding.") +1813sfFilename);1814}1815}18161817if (verbose != null) {1818if (tsaUrl != null || tsaCert != null) {1819System.out.println(1820rb.getString("requesting.a.signature.timestamp"));1821}1822if (tsaUrl != null) {1823System.out.println(rb.getString("TSA.location.") + tsaUrl);1824}1825if (tsaCert != null) {1826URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);1827if (tsaURI != null) {1828System.out.println(rb.getString("TSA.location.") +1829tsaURI);1830}1831System.out.println(rb.getString("TSA.certificate.") +1832printCert(true, "", tsaCert, null, false));1833}1834if (signingMechanism != null) {1835System.out.println(1836rb.getString("using.an.alternative.signing.mechanism"));1837}1838}18391840// signature block file1841zos.putNextEntry(bkFile);1842block.write(zos);1843if (verbose != null) {1844if (zipFile.getEntry(bkFilename) != null) {1845System.out.println(rb.getString(".updating.") +1846bkFilename);1847} else {1848System.out.println(rb.getString(".adding.") +1849bkFilename);1850}1851}18521853// Write out all other META-INF files that we stored in the1854// vector1855for (int i=0; i<mfFiles.size(); i++) {1856ZipEntry ze = mfFiles.elementAt(i);1857if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)1858&& !ze.getName().equalsIgnoreCase(sfFilename)1859&& !ze.getName().equalsIgnoreCase(bkFilename)) {1860writeEntry(zipFile, zos, ze);1861}1862}18631864// Write out all other files1865for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();1866enum_.hasMoreElements();) {1867ZipEntry ze = enum_.nextElement();18681869if (!ze.getName().startsWith(META_INF)) {1870if (verbose != null) {1871if (manifest.getAttributes(ze.getName()) != null)1872System.out.println(rb.getString(".signing.") +1873ze.getName());1874else1875System.out.println(rb.getString(".adding.") +1876ze.getName());1877}1878writeEntry(zipFile, zos, ze);1879}1880}1881} catch(IOException ioe) {1882error(rb.getString("unable.to.sign.jar.")+ioe, ioe);1883} finally {1884// close the resouces1885if (zipFile != null) {1886zipFile.close();1887zipFile = null;1888}18891890if (zos != null) {1891zos.close();1892}1893}18941895// The JarSigner API always accepts the timestamp received.1896// We need to extract the certs from the signed jar to1897// validate it.1898try (JarFile check = new JarFile(signedJarFile)) {1899PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry(1900"META-INF/" + sigfile + "." + privateKey.getAlgorithm())));1901Timestamp ts = null;1902try {1903SignerInfo si = p7.getSignerInfos()[0];1904if (si.getTsToken() != null) {1905hasTimestampBlock = true;1906}1907ts = si.getTimestamp();1908} catch (Exception e) {1909tsaChainNotValidated = true;1910tsaChainNotValidatedReason = e;1911}1912// Spaces before the ">>> Signer" and other lines are different1913String result = certsAndTSInfo("", " ", Arrays.asList(certChain), ts);1914if (verbose != null) {1915System.out.println(result);1916}1917} catch (Exception e) {1918if (debug) {1919e.printStackTrace();1920}1921}19221923if (signedjar == null) {1924// attempt an atomic rename. If that fails,1925// rename the original jar file, then the signed1926// one, then delete the original.1927if (!signedJarFile.renameTo(jarFile)) {1928File origJar = new File(jarName+".orig");19291930if (jarFile.renameTo(origJar)) {1931if (signedJarFile.renameTo(jarFile)) {1932origJar.delete();1933} else {1934MessageFormat form = new MessageFormat(rb.getString1935("attempt.to.rename.signedJarFile.to.jarFile.failed"));1936Object[] source = {signedJarFile, jarFile};1937error(form.format(source));1938}1939} else {1940MessageFormat form = new MessageFormat(rb.getString1941("attempt.to.rename.jarFile.to.origJar.failed"));1942Object[] source = {jarFile, origJar};1943error(form.format(source));1944}1945}1946}1947displayMessagesAndResult(true);1948}19491950private static String getDefaultSignatureAlgorithm(PrivateKey privateKey) {1951String keyAlgorithm = privateKey.getAlgorithm();1952if (keyAlgorithm.equalsIgnoreCase("DSA"))1953return "SHA256withDSA";1954else if (keyAlgorithm.equalsIgnoreCase("RSA"))1955return "SHA256withRSA";1956else if (keyAlgorithm.equalsIgnoreCase("EC"))1957return "SHA256withECDSA";1958throw new RuntimeException("private key is not a DSA or "1959+ "RSA key");1960}196119621963/**1964* Find the length of header inside bs. The header is a multiple (>=0)1965* lines of attributes plus an empty line. The empty line is included1966* in the header.1967*/1968@SuppressWarnings("fallthrough")1969private int findHeaderEnd(byte[] bs) {1970// Initial state true to deal with empty header1971boolean newline = true; // just met a newline1972int len = bs.length;1973for (int i=0; i<len; i++) {1974switch (bs[i]) {1975case '\r':1976if (i < len - 1 && bs[i+1] == '\n') i++;1977// fallthrough1978case '\n':1979if (newline) return i+1; //+1 to get length1980newline = true;1981break;1982default:1983newline = false;1984}1985}1986// If header end is not found, it means the MANIFEST.MF has only1987// the main attributes section and it does not end with 2 newlines.1988// Returns the whole length so that it can be completely replaced.1989return len;1990}19911992/**1993* signature-related files include:1994* . META-INF/MANIFEST.MF1995* . META-INF/SIG-*1996* . META-INF/*.SF1997* . META-INF/*.DSA1998* . META-INF/*.RSA1999* . META-INF/*.EC2000*/2001private boolean signatureRelated(String name) {2002return SignatureFileVerifier.isSigningRelated(name);2003}20042005Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();20062007/**2008* Returns a string of signer info, with a newline at the end.2009* Called by verifyJar().2010*/2011private String signerInfo(CodeSigner signer, String tab) throws Exception {2012if (cacheForSignerInfo.containsKey(signer)) {2013return cacheForSignerInfo.get(signer);2014}2015List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();2016// signing time is only displayed on verification2017Timestamp ts = signer.getTimestamp();2018String tsLine = "";2019if (ts != null) {2020tsLine = printTimestamp(tab, ts) + "\n";2021}2022// Spaces before the ">>> Signer" and other lines are the same.20232024String result = certsAndTSInfo(tab, tab, certs, ts);2025cacheForSignerInfo.put(signer, tsLine + result);2026return result;2027}20282029/**2030* Fills info on certs and timestamp into a StringBuilder, sets2031* warning flags (through printCert) and validates cert chains.2032*2033* @param tab1 spaces before the ">>> Signer" line2034* @param tab2 spaces before the other lines2035* @param certs the signer cert2036* @param ts the timestamp, can be null2037* @return the info as a string2038*/2039private String certsAndTSInfo(2040String tab1,2041String tab2,2042List<? extends Certificate> certs, Timestamp ts)2043throws Exception {20442045Date timestamp;2046if (ts != null) {2047timestamp = ts.getTimestamp();2048noTimestamp = false;2049} else {2050timestamp = null;2051}2052// display the certificate(s). The first one is end-entity cert and2053// its KeyUsage should be checked.2054boolean first = true;2055StringBuilder sb = new StringBuilder();2056sb.append(tab1).append(rb.getString("...Signer")).append('\n');2057for (Certificate c : certs) {2058sb.append(printCert(false, tab2, c, timestamp, first));2059sb.append('\n');2060first = false;2061}2062try {2063validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts);2064} catch (Exception e) {2065chainNotValidated = true;2066chainNotValidatedReason = e;2067sb.append(tab2).append(rb.getString(".Invalid.certificate.chain."))2068.append(e.getLocalizedMessage()).append("]\n");2069}2070if (ts != null) {2071sb.append(tab1).append(rb.getString("...TSA")).append('\n');2072for (Certificate c : ts.getSignerCertPath().getCertificates()) {2073sb.append(printCert(true, tab2, c, null, false));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 void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)2095throws IOException2096{2097ZipEntry ze2 = new ZipEntry(ze.getName());2098ze2.setMethod(ze.getMethod());2099ze2.setTime(ze.getTime());2100ze2.setComment(ze.getComment());2101ze2.setExtra(ze.getExtra());2102if (ze.getMethod() == ZipEntry.STORED) {2103ze2.setSize(ze.getSize());2104ze2.setCrc(ze.getCrc());2105}2106os.putNextEntry(ze2);2107writeBytes(zf, ze, os);2108}21092110/**2111* Writes all the bytes for a given entry to the specified output stream.2112*/2113private synchronized void writeBytes2114(ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {2115int n;21162117InputStream is = null;2118try {2119is = zf.getInputStream(ze);2120long left = ze.getSize();21212122while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {2123os.write(buffer, 0, n);2124left -= n;2125}2126} finally {2127if (is != null) {2128is.close();2129}2130}2131}21322133void loadKeyStore(String keyStoreName, boolean prompt) {21342135if (!nullStream && keyStoreName == null) {2136keyStoreName = System.getProperty("user.home") + File.separator2137+ ".keystore";2138}21392140try {2141try {2142KeyStore caks = KeyStoreUtil.getCacertsKeyStore();2143if (caks != null) {2144Enumeration<String> aliases = caks.aliases();2145while (aliases.hasMoreElements()) {2146String a = aliases.nextElement();2147try {2148trustedCerts.add((X509Certificate)caks.getCertificate(a));2149} catch (Exception e2) {2150// ignore, when a SecretkeyEntry does not include a cert2151}2152}2153}2154} catch (Exception e) {2155// Ignore, if cacerts cannot be loaded2156}21572158if (providerName == null) {2159store = KeyStore.getInstance(storetype);2160} else {2161store = KeyStore.getInstance(storetype, providerName);2162}21632164// Get pass phrase2165// XXX need to disable echo; on UNIX, call getpass(char *prompt)Z2166// and on NT call ??2167if (token && storepass == null && !protectedPath2168&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {2169storepass = getPass2170(rb.getString("Enter.Passphrase.for.keystore."));2171} else if (!token && storepass == null && prompt) {2172storepass = getPass2173(rb.getString("Enter.Passphrase.for.keystore."));2174}21752176try {2177if (nullStream) {2178store.load(null, storepass);2179} else {2180keyStoreName = keyStoreName.replace(File.separatorChar, '/');2181URL url = null;2182try {2183url = new URL(keyStoreName);2184} catch (java.net.MalformedURLException e) {2185// try as file2186url = new File(keyStoreName).toURI().toURL();2187}2188InputStream is = null;2189try {2190is = url.openStream();2191store.load(is, storepass);2192} finally {2193if (is != null) {2194is.close();2195}2196}2197}2198Enumeration<String> aliases = store.aliases();2199while (aliases.hasMoreElements()) {2200String a = aliases.nextElement();2201try {2202X509Certificate c = (X509Certificate)store.getCertificate(a);2203// Only add TrustedCertificateEntry and self-signed2204// PrivateKeyEntry2205if (store.isCertificateEntry(a) ||2206c.getSubjectDN().equals(c.getIssuerDN())) {2207trustedCerts.add(c);2208}2209} catch (Exception e2) {2210// ignore, when a SecretkeyEntry does not include a cert2211}2212}2213} finally {2214try {2215pkixParameters = new PKIXBuilderParameters(2216trustedCerts.stream()2217.map(c -> new TrustAnchor(c, null))2218.collect(Collectors.toSet()),2219null);2220pkixParameters.setRevocationEnabled(false);2221} catch (InvalidAlgorithmParameterException ex) {2222// Only if tas is empty2223}2224}2225} catch (IOException ioe) {2226throw new RuntimeException(rb.getString("keystore.load.") +2227ioe.getMessage());2228} catch (java.security.cert.CertificateException ce) {2229throw new RuntimeException(rb.getString("certificate.exception.") +2230ce.getMessage());2231} catch (NoSuchProviderException pe) {2232throw new RuntimeException(rb.getString("keystore.load.") +2233pe.getMessage());2234} catch (NoSuchAlgorithmException nsae) {2235throw new RuntimeException(rb.getString("keystore.load.") +2236nsae.getMessage());2237} catch (KeyStoreException kse) {2238throw new RuntimeException2239(rb.getString("unable.to.instantiate.keystore.class.") +2240kse.getMessage());2241}2242}22432244X509Certificate getTsaCert(String alias) {22452246java.security.cert.Certificate cs = null;22472248try {2249cs = store.getCertificate(alias);2250} catch (KeyStoreException kse) {2251// this never happens, because keystore has been loaded2252}2253if (cs == null || (!(cs instanceof X509Certificate))) {2254MessageFormat form = new MessageFormat(rb.getString2255("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the"));2256Object[] source = {alias, alias};2257error(form.format(source));2258}2259return (X509Certificate) cs;2260}22612262/**2263* Check if userCert is designed to be a code signer2264* @param userCert the certificate to be examined2265* @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,2266* NetscapeCertType has codeSigning flag turned on.2267* If null, the class field badKeyUsage, badExtendedKeyUsage,2268* badNetscapeCertType will be set.2269*/2270void checkCertUsage(X509Certificate userCert, boolean[] bad) {22712272// Can act as a signer?2273// 1. if KeyUsage, then [0:digitalSignature] or2274// [1:nonRepudiation] should be true2275// 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING2276// 3. if NetscapeCertType, then should contains OBJECT_SIGNING2277// 1,2,3 must be true22782279if (bad != null) {2280bad[0] = bad[1] = bad[2] = false;2281}22822283boolean[] keyUsage = userCert.getKeyUsage();2284if (keyUsage != null) {2285keyUsage = Arrays.copyOf(keyUsage, 9);2286if (!keyUsage[0] && !keyUsage[1]) {2287if (bad != null) {2288bad[0] = true;2289badKeyUsage = true;2290}2291}2292}22932294try {2295List<String> xKeyUsage = userCert.getExtendedKeyUsage();2296if (xKeyUsage != null) {2297if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage2298&& !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning2299if (bad != null) {2300bad[1] = true;2301badExtendedKeyUsage = true;2302}2303}2304}2305} catch (java.security.cert.CertificateParsingException e) {2306// shouldn't happen2307}23082309try {2310// OID_NETSCAPE_CERT_TYPE2311byte[] netscapeEx = userCert.getExtensionValue2312("2.16.840.1.113730.1.1");2313if (netscapeEx != null) {2314DerInputStream in = new DerInputStream(netscapeEx);2315byte[] encoded = in.getOctetString();2316encoded = new DerValue(encoded).getUnalignedBitString()2317.toByteArray();23182319NetscapeCertTypeExtension extn =2320new NetscapeCertTypeExtension(encoded);23212322Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING);2323if (!val) {2324if (bad != null) {2325bad[2] = true;2326badNetscapeCertType = true;2327}2328}2329}2330} catch (IOException e) {2331//2332}2333}23342335// Called by signJar().2336void getAliasInfo(String alias) throws Exception {23372338Key key = null;23392340try {2341java.security.cert.Certificate[] cs = null;2342if (altCertChain != null) {2343try (FileInputStream fis = new FileInputStream(altCertChain)) {2344cs = CertificateFactory.getInstance("X.509").2345generateCertificates(fis).2346toArray(new Certificate[0]);2347} catch (FileNotFoundException ex) {2348error(rb.getString("File.specified.by.certchain.does.not.exist"));2349} catch (CertificateException | IOException ex) {2350error(rb.getString("Cannot.restore.certchain.from.file.specified"));2351}2352} else {2353try {2354cs = store.getCertificateChain(alias);2355} catch (KeyStoreException kse) {2356// this never happens, because keystore has been loaded2357}2358}2359if (cs == null || cs.length == 0) {2360if (altCertChain != null) {2361error(rb.getString2362("Certificate.chain.not.found.in.the.file.specified."));2363} else {2364MessageFormat form = new MessageFormat(rb.getString2365("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and"));2366Object[] source = {alias, alias};2367error(form.format(source));2368}2369}23702371certChain = new X509Certificate[cs.length];2372for (int i=0; i<cs.length; i++) {2373if (!(cs[i] instanceof X509Certificate)) {2374error(rb.getString2375("found.non.X.509.certificate.in.signer.s.chain"));2376}2377certChain[i] = (X509Certificate)cs[i];2378}23792380try {2381if (!token && keypass == null)2382key = store.getKey(alias, storepass);2383else2384key = store.getKey(alias, keypass);2385} catch (UnrecoverableKeyException e) {2386if (token) {2387throw e;2388} else if (keypass == null) {2389// Did not work out, so prompt user for key password2390MessageFormat form = new MessageFormat(rb.getString2391("Enter.key.password.for.alias."));2392Object[] source = {alias};2393keypass = getPass(form.format(source));2394key = store.getKey(alias, keypass);2395}2396}2397} catch (NoSuchAlgorithmException e) {2398error(e.getMessage());2399} catch (UnrecoverableKeyException e) {2400error(rb.getString("unable.to.recover.key.from.keystore"));2401} catch (KeyStoreException kse) {2402// this never happens, because keystore has been loaded2403}24042405if (!(key instanceof PrivateKey)) {2406MessageFormat form = new MessageFormat(rb.getString2407("key.associated.with.alias.not.a.private.key"));2408Object[] source = {alias};2409error(form.format(source));2410} else {2411privateKey = (PrivateKey)key;2412}2413}24142415void error(String message)2416{2417System.out.println(rb.getString("jarsigner.")+message);2418System.exit(1);2419}242024212422void error(String message, Exception e)2423{2424System.out.println(rb.getString("jarsigner.")+message);2425if (debug) {2426e.printStackTrace();2427}2428System.exit(1);2429}24302431/**2432* Validates a cert chain.2433*2434* @param parameter this might be a timestamp2435*/2436void validateCertChain(String variant, List<? extends Certificate> certs,2437Timestamp parameter)2438throws Exception {2439try {2440Validator.getInstance(Validator.TYPE_PKIX,2441variant,2442pkixParameters)2443.validate(certs.toArray(new X509Certificate[certs.size()]),2444null, parameter);2445} catch (Exception e) {2446if (debug) {2447e.printStackTrace();2448}24492450// Exception might be dismissed if another warning flag2451// is already set by printCert.24522453if (variant.equals(Validator.VAR_TSA_SERVER) &&2454e instanceof ValidatorException) {2455// Throw cause if it's CertPathValidatorException,2456if (e.getCause() != null &&2457e.getCause() instanceof CertPathValidatorException) {2458e = (Exception) e.getCause();2459Throwable t = e.getCause();2460if ((t instanceof CertificateExpiredException &&2461hasExpiredTsaCert)) {2462// we already have hasExpiredTsaCert2463return;2464}2465}2466}24672468if (variant.equals(Validator.VAR_CODE_SIGNING) &&2469e instanceof ValidatorException) {2470// Throw cause if it's CertPathValidatorException,2471if (e.getCause() != null &&2472e.getCause() instanceof CertPathValidatorException) {2473e = (Exception) e.getCause();2474Throwable t = e.getCause();2475if ((t instanceof CertificateExpiredException &&2476hasExpiredCert) ||2477(t instanceof CertificateNotYetValidException &&2478notYetValidCert)) {2479// we already have hasExpiredCert and notYetValidCert2480return;2481}2482}2483if (e instanceof ValidatorException) {2484ValidatorException ve = (ValidatorException)e;2485if (ve.getErrorType() == ValidatorException.T_EE_EXTENSIONS &&2486(badKeyUsage || badExtendedKeyUsage || badNetscapeCertType)) {2487// We already have badKeyUsage, badExtendedKeyUsage2488// and badNetscapeCertType2489return;2490}2491}2492}2493throw e;2494}2495}24962497char[] getPass(String prompt)2498{2499System.err.print(prompt);2500System.err.flush();2501try {2502char[] pass = Password.readPassword(System.in);25032504if (pass == null) {2505error(rb.getString("you.must.enter.key.password"));2506} else {2507return pass;2508}2509} catch (IOException ioe) {2510error(rb.getString("unable.to.read.password.")+ioe.getMessage());2511}2512// this shouldn't happen2513return null;2514}25152516/*2517* Reads all the bytes for a given zip entry.2518*/2519private synchronized byte[] getBytes(ZipFile zf,2520ZipEntry ze) throws IOException {2521int n;25222523InputStream is = null;2524try {2525is = zf.getInputStream(ze);2526baos.reset();2527long left = ze.getSize();25282529while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {2530baos.write(buffer, 0, n);2531left -= n;2532}2533} finally {2534if (is != null) {2535is.close();2536}2537}25382539return baos.toByteArray();2540}25412542/*2543* Returns manifest entry from given jar file, or null if given jar file2544* does not have a manifest entry.2545*/2546private ZipEntry getManifestFile(ZipFile zf) {2547ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);2548if (ze == null) {2549// Check all entries for matching name2550Enumeration<? extends ZipEntry> enum_ = zf.entries();2551while (enum_.hasMoreElements() && ze == null) {2552ze = enum_.nextElement();2553if (!JarFile.MANIFEST_NAME.equalsIgnoreCase2554(ze.getName())) {2555ze = null;2556}2557}2558}2559return ze;2560}25612562/*2563* Computes the digests of a zip entry, and returns them as an array2564* of base64-encoded strings.2565*/2566private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,2567MessageDigest[] digests)2568throws IOException {25692570int n, i;2571InputStream is = null;2572try {2573is = zf.getInputStream(ze);2574long left = ze.getSize();2575while((left > 0)2576&& (n = is.read(buffer, 0, buffer.length)) != -1) {2577for (i=0; i<digests.length; i++) {2578digests[i].update(buffer, 0, n);2579}2580left -= n;2581}2582} finally {2583if (is != null) {2584is.close();2585}2586}25872588// complete the digests2589String[] base64Digests = new String[digests.length];2590for (i=0; i<digests.length; i++) {2591base64Digests[i] = Base64.getEncoder().encodeToString(digests[i].digest());2592}2593return base64Digests;2594}25952596/*2597* Computes the digests of a zip entry, and returns them as a list of2598* attributes2599*/2600private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,2601MessageDigest[] digests)2602throws IOException {26032604String[] base64Digests = getDigests(ze, zf, digests);2605Attributes attrs = new Attributes();26062607for (int i=0; i<digests.length; i++) {2608attrs.putValue(digests[i].getAlgorithm()+"-Digest",2609base64Digests[i]);2610}2611return attrs;2612}26132614/*2615* Updates the digest attributes of a manifest entry, by adding or2616* replacing digest values.2617* A digest value is added if the manifest entry does not contain a digest2618* for that particular algorithm.2619* A digest value is replaced if it is obsolete.2620*2621* Returns true if the manifest entry has been changed, and false2622* otherwise.2623*/2624private boolean updateDigests(ZipEntry ze, ZipFile zf,2625MessageDigest[] digests,2626Manifest mf) throws IOException {2627boolean update = false;26282629Attributes attrs = mf.getAttributes(ze.getName());2630String[] base64Digests = getDigests(ze, zf, digests);26312632for (int i=0; i<digests.length; i++) {2633// The entry name to be written into attrs2634String name = null;2635try {2636// Find if the digest already exists2637AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());2638for (Object key: attrs.keySet()) {2639if (key instanceof Attributes.Name) {2640String n = ((Attributes.Name)key).toString();2641if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {2642String tmp = n.substring(0, n.length() - 7);2643if (AlgorithmId.get(tmp).equals(aid)) {2644name = n;2645break;2646}2647}2648}2649}2650} catch (NoSuchAlgorithmException nsae) {2651// Ignored. Writing new digest entry.2652}26532654if (name == null) {2655name = digests[i].getAlgorithm()+"-Digest";2656attrs.putValue(name, base64Digests[i]);2657update=true;2658} else {2659// compare digests, and replace the one in the manifest2660// if they are different2661String mfDigest = attrs.getValue(name);2662if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {2663attrs.putValue(name, base64Digests[i]);2664update=true;2665}2666}2667}2668return update;2669}26702671/*2672* Try to load the specified signing mechanism.2673* The URL class loader is used.2674*/2675private ContentSigner loadSigningMechanism(String signerClassName,2676String signerClassPath) throws Exception {26772678// construct class loader2679String cpString = null; // make sure env.class.path defaults to dot26802681// do prepends to get correct ordering2682cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);2683cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);2684cpString = PathList.appendPath(signerClassPath, cpString);2685URL[] urls = PathList.pathToURLs(cpString);2686ClassLoader appClassLoader = new URLClassLoader(urls);26872688// attempt to find signer2689Class<?> signerClass = appClassLoader.loadClass(signerClassName);26902691// Check that it implements ContentSigner2692Object signer = signerClass.newInstance();2693if (!(signer instanceof ContentSigner)) {2694MessageFormat form = new MessageFormat(2695rb.getString("signerClass.is.not.a.signing.mechanism"));2696Object[] source = {signerClass.getName()};2697throw new IllegalArgumentException(form.format(source));2698}2699return (ContentSigner)signer;2700}2701}27022703class SignatureFile {27042705/** SignatureFile */2706Manifest sf;27072708/** .SF base name */2709String baseName;27102711public SignatureFile(MessageDigest digests[],2712Manifest mf,2713ManifestDigester md,2714String baseName,2715boolean signManifest)27162717{2718this.baseName = baseName;27192720String version = System.getProperty("java.version");2721String javaVendor = System.getProperty("java.vendor");27222723sf = new Manifest();2724Attributes mattr = sf.getMainAttributes();27252726mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");2727mattr.putValue("Created-By", version + " (" + javaVendor + ")");27282729if (signManifest) {2730// sign the whole manifest2731for (int i=0; i < digests.length; i++) {2732mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",2733Base64.getEncoder().encodeToString(md.manifestDigest(digests[i])));2734}2735}27362737// create digest of the manifest main attributes2738ManifestDigester.Entry mde =2739md.get(ManifestDigester.MF_MAIN_ATTRS, false);2740if (mde != null) {2741for (int i=0; i < digests.length; i++) {2742mattr.putValue(digests[i].getAlgorithm() +2743"-Digest-" + ManifestDigester.MF_MAIN_ATTRS,2744Base64.getEncoder().encodeToString(mde.digest(digests[i])));2745}2746} else {2747throw new IllegalStateException2748("ManifestDigester failed to create " +2749"Manifest-Main-Attribute entry");2750}27512752/* go through the manifest entries and create the digests */27532754Map<String,Attributes> entries = sf.getEntries();2755Iterator<Map.Entry<String,Attributes>> mit =2756mf.getEntries().entrySet().iterator();2757while(mit.hasNext()) {2758Map.Entry<String,Attributes> e = mit.next();2759String name = e.getKey();2760mde = md.get(name, false);2761if (mde != null) {2762Attributes attr = new Attributes();2763for (int i=0; i < digests.length; i++) {2764attr.putValue(digests[i].getAlgorithm()+"-Digest",2765Base64.getEncoder().encodeToString(mde.digest(digests[i])));2766}2767entries.put(name, attr);2768}2769}2770}27712772/**2773* Writes the SignatureFile to the specified OutputStream.2774*2775* @param out the output stream2776* @exception IOException if an I/O error has occurred2777*/27782779public void write(OutputStream out) throws IOException2780{2781sf.write(out);2782}27832784/**2785* get .SF file name2786*/2787public String getMetaName()2788{2789return "META-INF/"+ baseName + ".SF";2790}27912792/**2793* get base file name2794*/2795public String getBaseName()2796{2797return baseName;2798}27992800/*2801* Generate a signed data block.2802* If a URL or a certificate (containing a URL) for a Timestamping2803* Authority is supplied then a signature timestamp is generated and2804* inserted into the signed data block.2805*2806* @param sigalg signature algorithm to use, or null to use default2807* @param tsaUrl The location of the Timestamping Authority. If null2808* then no timestamp is requested.2809* @param tsaCert The certificate for the Timestamping Authority. If null2810* then no timestamp is requested.2811* @param signingMechanism The signing mechanism to use.2812* @param args The command-line arguments to jarsigner.2813* @param zipFile The original source Zip file.2814*/2815public Block generateBlock(PrivateKey privateKey,2816String sigalg,2817X509Certificate[] certChain,2818boolean externalSF, String tsaUrl,2819X509Certificate tsaCert,2820String tSAPolicyID,2821String tSADigestAlg,2822ContentSigner signingMechanism,2823String[] args, ZipFile zipFile)2824throws NoSuchAlgorithmException, InvalidKeyException, IOException,2825SignatureException, CertificateException2826{2827return new Block(this, privateKey, sigalg, certChain, externalSF,2828tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg, signingMechanism, args, zipFile);2829}283028312832public static class Block {28332834private byte[] block;2835private String blockFileName;28362837/*2838* Construct a new signature block.2839*/2840Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,2841X509Certificate[] certChain, boolean externalSF, String tsaUrl,2842X509Certificate tsaCert, String tSAPolicyID, String tSADigestAlg,2843ContentSigner signingMechanism, String[] args, ZipFile zipFile)2844throws NoSuchAlgorithmException, InvalidKeyException, IOException,2845SignatureException, CertificateException {28462847Principal issuerName = certChain[0].getIssuerDN();2848if (!(issuerName instanceof X500Name)) {2849// must extract the original encoded form of DN for subsequent2850// name comparison checks (converting to a String and back to2851// an encoded DN could cause the types of String attribute2852// values to be changed)2853X509CertInfo tbsCert = new2854X509CertInfo(certChain[0].getTBSCertificate());2855issuerName = (Principal)2856tbsCert.get(X509CertInfo.ISSUER + "." +2857X509CertInfo.DN_NAME);2858}2859BigInteger serial = certChain[0].getSerialNumber();28602861String signatureAlgorithm = sigalg;2862String keyAlgorithm = privateKey.getAlgorithm();28632864// check common invalid key/signature algorithm combinations2865String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH);2866if ((sigAlgUpperCase.endsWith("WITHRSA") &&2867!keyAlgorithm.equalsIgnoreCase("RSA")) ||2868(sigAlgUpperCase.endsWith("WITHECDSA") &&2869!keyAlgorithm.equalsIgnoreCase("EC")) ||2870(sigAlgUpperCase.endsWith("WITHDSA") &&2871!keyAlgorithm.equalsIgnoreCase("DSA"))) {2872throw new SignatureException2873("private key algorithm is not compatible with signature algorithm");2874}28752876blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;28772878AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);2879AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);28802881Signature sig = Signature.getInstance(signatureAlgorithm);2882sig.initSign(privateKey);28832884ByteArrayOutputStream baos = new ByteArrayOutputStream();2885sfg.write(baos);28862887byte[] content = baos.toByteArray();28882889sig.update(content);2890byte[] signature = sig.sign();28912892// Timestamp the signature and generate the signature block file2893if (signingMechanism == null) {2894signingMechanism = new TimestampedSigner();2895}2896URI tsaUri = null;2897try {2898if (tsaUrl != null) {2899tsaUri = new URI(tsaUrl);2900}2901} catch (URISyntaxException e) {2902throw new IOException(e);2903}29042905// Assemble parameters for the signing mechanism2906ContentSignerParameters params =2907new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID,2908tSADigestAlg, signature,2909signatureAlgorithm, certChain, content, zipFile);29102911// Generate the signature block2912block = signingMechanism.generateSignedData(2913params, externalSF, (tsaUrl != null || tsaCert != null));2914}29152916/*2917* get block file name.2918*/2919public String getMetaName()2920{2921return blockFileName;2922}29232924/**2925* Writes the block file to the specified OutputStream.2926*2927* @param out the output stream2928* @exception IOException if an I/O error has occurred2929*/29302931public void write(OutputStream out) throws IOException2932{2933out.write(block);2934}2935}2936}293729382939