0N/A/*
3659N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.tools;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.util.*;
0N/Aimport java.util.zip.*;
0N/Aimport java.util.jar.*;
0N/Aimport java.math.BigInteger;
0N/Aimport java.net.URI;
0N/Aimport java.net.URISyntaxException;
0N/Aimport java.text.Collator;
0N/Aimport java.text.MessageFormat;
0N/Aimport java.security.cert.Certificate;
0N/Aimport java.security.cert.X509Certificate;
0N/Aimport java.security.cert.CertificateException;
0N/Aimport java.security.*;
0N/Aimport java.lang.reflect.Constructor;
0N/A
0N/Aimport com.sun.jarsigner.ContentSigner;
0N/Aimport com.sun.jarsigner.ContentSignerParameters;
1026N/Aimport java.net.SocketTimeoutException;
1026N/Aimport java.net.URL;
1026N/Aimport java.net.URLClassLoader;
1026N/Aimport java.security.cert.CertPath;
1026N/Aimport java.security.cert.CertPathValidator;
1026N/Aimport java.security.cert.CertificateExpiredException;
1026N/Aimport java.security.cert.CertificateFactory;
1026N/Aimport java.security.cert.CertificateNotYetValidException;
1026N/Aimport java.security.cert.PKIXParameters;
1026N/Aimport java.security.cert.TrustAnchor;
1026N/Aimport java.util.Map.Entry;
0N/Aimport sun.security.x509.*;
0N/Aimport sun.security.util.*;
0N/Aimport sun.misc.BASE64Encoder;
0N/A
1026N/A
0N/A/**
0N/A * <p>The jarsigner utility.
0N/A *
1026N/A * The exit codes for the main method are:
1026N/A *
1026N/A * 0: success
1026N/A * 1: any error that the jar cannot be signed or verified, including:
1026N/A * keystore loading error
5663N/A * TSP communication error
1026N/A * jarsigner command line error...
1026N/A * otherwise: error codes from -strict
1026N/A *
0N/A * @author Roland Schemers
0N/A * @author Jan Luehe
0N/A */
0N/A
0N/Apublic class JarSigner {
0N/A
0N/A // for i18n
0N/A private static final java.util.ResourceBundle rb =
0N/A java.util.ResourceBundle.getBundle
0N/A ("sun.security.tools.JarSignerResources");
0N/A private static final Collator collator = Collator.getInstance();
0N/A static {
0N/A // this is for case insensitive string comparisions
0N/A collator.setStrength(Collator.PRIMARY);
0N/A }
0N/A
0N/A private static final String META_INF = "META-INF/";
0N/A
0N/A // prefix for new signature-related files in META-INF directory
0N/A private static final String SIG_PREFIX = META_INF + "SIG-";
0N/A
0N/A private static final Class[] PARAM_STRING = { String.class };
0N/A
0N/A private static final String NONE = "NONE";
0N/A private static final String P11KEYSTORE = "PKCS11";
0N/A
0N/A private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
0N/A
0N/A // Attention:
0N/A // This is the entry that get launched by the security tool jarsigner.
0N/A public static void main(String args[]) throws Exception {
0N/A JarSigner js = new JarSigner();
0N/A js.run(args);
0N/A }
0N/A
0N/A static final String VERSION = "1.0";
0N/A
1026N/A static final int IN_KEYSTORE = 0x01; // signer is in keystore
0N/A static final int IN_SCOPE = 0x02;
1026N/A static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
1026N/A // signer is not in alias list
1026N/A static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
0N/A
1026N/A X509Certificate[] certChain; // signer's cert chain (when composing)
1026N/A PrivateKey privateKey; // private key
1026N/A KeyStore store; // the keystore specified by -keystore
1026N/A // or the default keystore, never null
0N/A
0N/A String keystore; // key store file
0N/A boolean nullStream = false; // null keystore input stream (NONE)
0N/A boolean token = false; // token-based keystore
2346N/A String jarfile; // jar files to sign or verify
0N/A String alias; // alias to sign jar with
3388N/A List<String> ckaliases = new ArrayList<>(); // aliases in -verify
0N/A char[] storepass; // keystore password
0N/A boolean protectedPath; // protected authentication path
0N/A String storetype; // keystore type
0N/A String providerName; // provider name
0N/A Vector<String> providers = null; // list of providers
1026N/A // arguments for provider constructors
3388N/A HashMap<String,String> providerArgs = new HashMap<>();
0N/A char[] keypass; // private key password
0N/A String sigfile; // name of .SF file
0N/A String sigalg; // name of signature algorithm
1425N/A String digestalg = "SHA-256"; // name of digest algorithm
0N/A String signedjar; // output filename
0N/A String tsaUrl; // location of the Timestamping Authority
0N/A String tsaAlias; // alias for the Timestamping Authority's certificate
1026N/A String altCertChain; // file to read alternative cert chain from
0N/A boolean verify = false; // verify the jar
1026N/A String verbose = null; // verbose output when signing/verifying
0N/A boolean showcerts = false; // show certs when verifying
0N/A boolean debug = false; // debug
0N/A boolean signManifest = true; // "sign" the whole manifest
0N/A boolean externalSF = true; // leave the .SF out of the PKCS7 block
1026N/A boolean strict = false; // treat warnings as error
0N/A
0N/A // read zip entry raw bytes
0N/A private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
0N/A private byte[] buffer = new byte[8192];
0N/A private ContentSigner signingMechanism = null;
0N/A private String altSignerClass = null;
0N/A private String altSignerClasspath = null;
0N/A private ZipFile zipFile = null;
1026N/A
0N/A private boolean hasExpiredCert = false;
0N/A private boolean hasExpiringCert = false;
0N/A private boolean notYetValidCert = false;
1026N/A private boolean chainNotValidated = false;
1026N/A private boolean notSignedByAlias = false;
1026N/A private boolean aliasNotInStore = false;
1026N/A private boolean hasUnsignedEntry = false;
0N/A private boolean badKeyUsage = false;
0N/A private boolean badExtendedKeyUsage = false;
0N/A private boolean badNetscapeCertType = false;
0N/A
1026N/A CertificateFactory certificateFactory;
1026N/A CertPathValidator validator;
1026N/A PKIXParameters pkixParameters;
1026N/A
0N/A public void run(String args[]) {
0N/A try {
0N/A parseArgs(args);
0N/A
0N/A // Try to load and install the specified providers
0N/A if (providers != null) {
0N/A ClassLoader cl = ClassLoader.getSystemClassLoader();
0N/A Enumeration<String> e = providers.elements();
0N/A while (e.hasMoreElements()) {
0N/A String provName = e.nextElement();
0N/A Class<?> provClass;
0N/A if (cl != null) {
0N/A provClass = cl.loadClass(provName);
0N/A } else {
0N/A provClass = Class.forName(provName);
0N/A }
0N/A
0N/A String provArg = providerArgs.get(provName);
0N/A Object obj;
0N/A if (provArg == null) {
0N/A obj = provClass.newInstance();
0N/A } else {
0N/A Constructor<?> c =
0N/A provClass.getConstructor(PARAM_STRING);
0N/A obj = c.newInstance(provArg);
0N/A }
0N/A
0N/A if (!(obj instanceof Provider)) {
0N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("provName.not.a.provider"));
0N/A Object[] source = {provName};
0N/A throw new Exception(form.format(source));
0N/A }
0N/A Security.addProvider((Provider)obj);
0N/A }
0N/A }
0N/A
0N/A if (verify) {
0N/A try {
0N/A loadKeyStore(keystore, false);
0N/A } catch (Exception e) {
0N/A if ((keystore != null) || (storepass != null)) {
3050N/A System.out.println(rb.getString("jarsigner.error.") +
0N/A e.getMessage());
0N/A System.exit(1);
0N/A }
0N/A }
0N/A /* if (debug) {
0N/A SignatureFileVerifier.setDebug(true);
0N/A ManifestEntryVerifier.setDebug(true);
0N/A }
0N/A */
0N/A verifyJar(jarfile);
0N/A } else {
0N/A loadKeyStore(keystore, true);
0N/A getAliasInfo(alias);
0N/A
0N/A // load the alternative signing mechanism
0N/A if (altSignerClass != null) {
0N/A signingMechanism = loadSigningMechanism(altSignerClass,
0N/A altSignerClasspath);
0N/A }
0N/A signJar(jarfile, alias, args);
0N/A }
0N/A } catch (Exception e) {
3050N/A System.out.println(rb.getString("jarsigner.error.") + e);
0N/A if (debug) {
0N/A e.printStackTrace();
0N/A }
0N/A System.exit(1);
0N/A } finally {
0N/A // zero-out private key password
0N/A if (keypass != null) {
0N/A Arrays.fill(keypass, ' ');
0N/A keypass = null;
0N/A }
0N/A // zero-out keystore password
0N/A if (storepass != null) {
0N/A Arrays.fill(storepass, ' ');
0N/A storepass = null;
0N/A }
0N/A }
1026N/A
1026N/A if (strict) {
1026N/A int exitCode = 0;
1026N/A if (hasExpiringCert) {
1026N/A exitCode |= 2;
1026N/A }
5663N/A if (chainNotValidated || hasExpiredCert || notYetValidCert) {
1026N/A exitCode |= 4;
1026N/A }
1026N/A if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
1026N/A exitCode |= 8;
1026N/A }
1026N/A if (hasUnsignedEntry) {
1026N/A exitCode |= 16;
1026N/A }
1026N/A if (notSignedByAlias || aliasNotInStore) {
1026N/A exitCode |= 32;
1026N/A }
1026N/A if (exitCode != 0) {
1026N/A System.exit(exitCode);
1026N/A }
1026N/A }
0N/A }
0N/A
0N/A /*
0N/A * Parse command line arguments.
0N/A */
0N/A void parseArgs(String args[]) {
0N/A /* parse flags */
0N/A int n = 0;
0N/A
1026N/A if (args.length == 0) fullusage();
1026N/A for (n=0; n < args.length; n++) {
0N/A
0N/A String flags = args[n];
1709N/A String modifier = null;
1709N/A if (flags.charAt(0) == '-') {
1709N/A int pos = flags.indexOf(':');
1709N/A if (pos > 0) {
1709N/A modifier = flags.substring(pos+1);
1709N/A flags = flags.substring(0, pos);
1709N/A }
1709N/A }
0N/A
0N/A if (collator.compare(flags, "-keystore") == 0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A keystore = args[n];
0N/A } else if (collator.compare(flags, "-storepass") ==0) {
1026N/A if (++n == args.length) usageNoArg();
1709N/A storepass = getPass(modifier, args[n]);
0N/A } else if (collator.compare(flags, "-storetype") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A storetype = args[n];
0N/A } else if (collator.compare(flags, "-providerName") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A providerName = args[n];
0N/A } else if ((collator.compare(flags, "-provider") == 0) ||
0N/A (collator.compare(flags, "-providerClass") == 0)) {
1026N/A if (++n == args.length) usageNoArg();
0N/A if (providers == null) {
0N/A providers = new Vector<String>(3);
0N/A }
0N/A providers.add(args[n]);
0N/A
0N/A if (args.length > (n+1)) {
0N/A flags = args[n+1];
0N/A if (collator.compare(flags, "-providerArg") == 0) {
1026N/A if (args.length == (n+2)) usageNoArg();
0N/A providerArgs.put(args[n], args[n+2]);
0N/A n += 2;
0N/A }
0N/A }
0N/A } else if (collator.compare(flags, "-protected") ==0) {
0N/A protectedPath = true;
1026N/A } else if (collator.compare(flags, "-certchain") ==0) {
1026N/A if (++n == args.length) usageNoArg();
1026N/A altCertChain = args[n];
0N/A } else if (collator.compare(flags, "-debug") ==0) {
0N/A debug = true;
0N/A } else if (collator.compare(flags, "-keypass") ==0) {
1026N/A if (++n == args.length) usageNoArg();
1709N/A keypass = getPass(modifier, args[n]);
0N/A } else if (collator.compare(flags, "-sigfile") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A sigfile = args[n];
0N/A } else if (collator.compare(flags, "-signedjar") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A signedjar = args[n];
0N/A } else if (collator.compare(flags, "-tsa") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A tsaUrl = args[n];
0N/A } else if (collator.compare(flags, "-tsacert") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A tsaAlias = args[n];
0N/A } else if (collator.compare(flags, "-altsigner") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A altSignerClass = args[n];
0N/A } else if (collator.compare(flags, "-altsignerpath") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A altSignerClasspath = args[n];
0N/A } else if (collator.compare(flags, "-sectionsonly") ==0) {
0N/A signManifest = false;
0N/A } else if (collator.compare(flags, "-internalsf") ==0) {
0N/A externalSF = false;
0N/A } else if (collator.compare(flags, "-verify") ==0) {
0N/A verify = true;
0N/A } else if (collator.compare(flags, "-verbose") ==0) {
1709N/A verbose = (modifier != null) ? modifier : "all";
0N/A } else if (collator.compare(flags, "-sigalg") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A sigalg = args[n];
0N/A } else if (collator.compare(flags, "-digestalg") ==0) {
1026N/A if (++n == args.length) usageNoArg();
0N/A digestalg = args[n];
0N/A } else if (collator.compare(flags, "-certs") ==0) {
0N/A showcerts = true;
1026N/A } else if (collator.compare(flags, "-strict") ==0) {
1026N/A strict = true;
0N/A } else if (collator.compare(flags, "-h") == 0 ||
0N/A collator.compare(flags, "-help") == 0) {
1026N/A fullusage();
0N/A } else {
1026N/A if (!flags.startsWith("-")) {
1026N/A if (jarfile == null) {
1026N/A jarfile = flags;
1026N/A } else {
1026N/A alias = flags;
1026N/A ckaliases.add(alias);
1026N/A }
1026N/A } else {
1026N/A System.err.println(
3050N/A rb.getString("Illegal.option.") + flags);
1026N/A usage();
1026N/A }
0N/A }
0N/A }
0N/A
1026N/A // -certs must always be specified with -verbose
1026N/A if (verbose == null) showcerts = false;
0N/A
1026N/A if (jarfile == null) {
3050N/A System.err.println(rb.getString("Please.specify.jarfile.name"));
1026N/A usage();
1026N/A }
1026N/A if (!verify && alias == null) {
3050N/A System.err.println(rb.getString("Please.specify.alias.name"));
1026N/A usage();
1026N/A }
1026N/A if (!verify && ckaliases.size() > 1) {
3050N/A System.err.println(rb.getString("Only.one.alias.can.be.specified"));
1026N/A usage();
0N/A }
0N/A
0N/A if (storetype == null) {
0N/A storetype = KeyStore.getDefaultType();
0N/A }
0N/A storetype = KeyStoreUtil.niceStoreTypeName(storetype);
0N/A
1534N/A try {
1534N/A if (signedjar != null && new File(signedjar).getCanonicalPath().equals(
1534N/A new File(jarfile).getCanonicalPath())) {
1534N/A signedjar = null;
1534N/A }
1534N/A } catch (IOException ioe) {
1534N/A // File system error?
1534N/A // Just ignore it.
1534N/A }
1534N/A
0N/A if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
0N/A KeyStoreUtil.isWindowsKeyStore(storetype)) {
0N/A token = true;
0N/A if (keystore == null) {
0N/A keystore = NONE;
0N/A }
0N/A }
0N/A
0N/A if (NONE.equals(keystore)) {
0N/A nullStream = true;
0N/A }
0N/A
0N/A if (token && !nullStream) {
0N/A System.err.println(MessageFormat.format(rb.getString
3050N/A (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
0N/A usage();
0N/A }
0N/A
0N/A if (token && keypass != null) {
0N/A System.err.println(MessageFormat.format(rb.getString
3050N/A (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));
0N/A usage();
0N/A }
0N/A
0N/A if (protectedPath) {
0N/A if (storepass != null || keypass != null) {
0N/A System.err.println(rb.getString
3050N/A ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));
0N/A usage();
0N/A }
0N/A }
0N/A if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
0N/A if (storepass != null || keypass != null) {
0N/A System.err.println(rb.getString
3050N/A ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));
0N/A usage();
0N/A }
0N/A }
0N/A }
0N/A
1709N/A static char[] getPass(String modifier, String arg) {
1709N/A char[] output = KeyTool.getPassWithModifier(modifier, arg);
1709N/A if (output != null) return output;
1709N/A usage();
1709N/A return null; // Useless, usage() already exit
1709N/A }
1709N/A
1709N/A static void usageNoArg() {
3050N/A System.out.println(rb.getString("Option.lacks.argument"));
1026N/A usage();
1026N/A }
1026N/A
1709N/A static void usage() {
1026N/A System.out.println();
3050N/A System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
1026N/A System.exit(1);
1026N/A }
1026N/A
1709N/A static void fullusage() {
0N/A System.out.println(rb.getString
3050N/A ("Usage.jarsigner.options.jar.file.alias"));
0N/A System.out.println(rb.getString
3050N/A (".jarsigner.verify.options.jar.file.alias."));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".keystore.url.keystore.location"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".storepass.password.password.for.keystore.integrity"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".storetype.type.keystore.type"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".keypass.password.password.for.private.key.if.different."));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".certchain.file.name.of.alternative.certchain.file"));
1026N/A System.out.println();
1026N/A System.out.println(rb.getString
3050N/A (".sigfile.file.name.of.SF.DSA.file"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".signedjar.file.name.of.signed.JAR.file"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".digestalg.algorithm.name.of.digest.algorithm"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".sigalg.algorithm.name.of.signature.algorithm"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".verify.verify.a.signed.JAR.file"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".verbose.suboptions.verbose.output.when.signing.verifying."));
1026N/A System.out.println(rb.getString
3050N/A (".suboptions.can.be.all.grouped.or.summary"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".certs.display.certificates.when.verbose.and.verifying"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".tsa.url.location.of.the.Timestamping.Authority"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".internalsf.include.the.SF.file.inside.the.signature.block"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".sectionsonly.don.t.compute.hash.of.entire.manifest"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".protected.keystore.has.protected.authentication.path"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".providerName.name.provider.name"));
0N/A System.out.println();
0N/A System.out.println(rb.getString
3050N/A (".providerClass.class.name.of.cryptographic.service.provider.s"));
0N/A System.out.println(rb.getString
3050N/A (".providerArg.arg.master.class.file.and.constructor.argument"));
0N/A System.out.println();
1026N/A System.out.println(rb.getString
3050N/A (".strict.treat.warnings.as.errors"));
1026N/A System.out.println();
0N/A
1026N/A System.exit(0);
0N/A }
0N/A
0N/A void verifyJar(String jarName)
0N/A throws Exception
0N/A {
1026N/A boolean anySigned = false; // if there exists entry inside jar signed
0N/A JarFile jf = null;
0N/A
0N/A try {
0N/A jf = new JarFile(jarName, true);
3388N/A Vector<JarEntry> entriesVec = new Vector<>();
0N/A byte[] buffer = new byte[8192];
0N/A
0N/A Enumeration<JarEntry> entries = jf.entries();
0N/A while (entries.hasMoreElements()) {
0N/A JarEntry je = entries.nextElement();
0N/A entriesVec.addElement(je);
0N/A InputStream is = null;
0N/A try {
0N/A is = jf.getInputStream(je);
0N/A int n;
0N/A while ((n = is.read(buffer, 0, buffer.length)) != -1) {
0N/A // we just read. this will throw a SecurityException
0N/A // if a signature/digest check fails.
0N/A }
0N/A } finally {
0N/A if (is != null) {
0N/A is.close();
0N/A }
0N/A }
0N/A }
0N/A
0N/A Manifest man = jf.getManifest();
0N/A
1026N/A // The map to record display info, only used when -verbose provided
1026N/A // key: signer info string
1026N/A // value: the list of files with common key
3388N/A Map<String,List<String>> output = new LinkedHashMap<>();
1026N/A
0N/A if (man != null) {
1026N/A if (verbose != null) System.out.println();
0N/A Enumeration<JarEntry> e = entriesVec.elements();
0N/A
3050N/A String tab = rb.getString("6SPACE");
0N/A
0N/A while (e.hasMoreElements()) {
0N/A JarEntry je = e.nextElement();
0N/A String name = je.getName();
0N/A CodeSigner[] signers = je.getCodeSigners();
0N/A boolean isSigned = (signers != null);
0N/A anySigned |= isSigned;
0N/A hasUnsignedEntry |= !je.isDirectory() && !isSigned
0N/A && !signatureRelated(name);
0N/A
1026N/A int inStoreOrScope = inKeyStore(signers);
1026N/A
1026N/A boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
1026N/A boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
1026N/A
1026N/A notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;
3210N/A if (keystore != null) {
3210N/A aliasNotInStore |= isSigned && (!inStore && !inScope);
3210N/A }
1026N/A
1026N/A // Only used when -verbose provided
1026N/A StringBuffer sb = null;
1026N/A if (verbose != null) {
1026N/A sb = new StringBuffer();
0N/A boolean inManifest =
0N/A ((man.getAttributes(name) != null) ||
0N/A (man.getAttributes("./"+name) != null) ||
0N/A (man.getAttributes("/"+name) != null));
1026N/A sb.append(
3050N/A (isSigned ? rb.getString("s") : rb.getString("SPACE")) +
3050N/A (inManifest ? rb.getString("m") : rb.getString("SPACE")) +
3050N/A (inStore ? rb.getString("k") : rb.getString("SPACE")) +
3050N/A (inScope ? rb.getString("i") : rb.getString("SPACE")) +
1026N/A ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +
3050N/A rb.getString("SPACE"));
1026N/A sb.append("|");
1026N/A }
0N/A
1026N/A // When -certs provided, display info has extra empty
1026N/A // lines at the beginning and end.
1026N/A if (isSigned) {
1026N/A if (showcerts) sb.append('\n');
1026N/A for (CodeSigner signer: signers) {
1026N/A // signerInfo() must be called even if -verbose
1026N/A // not provided. The method updates various
1026N/A // warning flags.
5663N/A String si = signerInfo(signer, tab);
1026N/A if (showcerts) {
1026N/A sb.append(si);
1026N/A sb.append('\n');
0N/A }
0N/A }
1026N/A } else if (showcerts && !verbose.equals("all")) {
1026N/A // Print no info for unsigned entries when -verbose:all,
1026N/A // to be consistent with old behavior.
1026N/A if (signatureRelated(name)) {
1026N/A sb.append("\n" + tab + rb.getString(
3050N/A ".Signature.related.entries.") + "\n\n");
1026N/A } else {
1026N/A sb.append("\n" + tab + rb.getString(
3050N/A ".Unsigned.entries.") + "\n\n");
0N/A }
0N/A }
0N/A
1026N/A if (verbose != null) {
1026N/A String label = sb.toString();
1026N/A if (signatureRelated(name)) {
1026N/A // Entries inside META-INF and other unsigned
1026N/A // entries are grouped separately.
3209N/A label = "-" + label;
1026N/A }
1026N/A
1026N/A // The label finally contains 2 parts separated by '|':
1026N/A // The legend displayed before the entry names, and
1026N/A // the cert info (if -certs specfied).
1026N/A
1026N/A if (!output.containsKey(label)) {
1026N/A output.put(label, new ArrayList<String>());
1026N/A }
1026N/A
1026N/A StringBuffer fb = new StringBuffer();
1026N/A String s = Long.toString(je.getSize());
1026N/A for (int i = 6 - s.length(); i > 0; --i) {
1026N/A fb.append(' ');
1026N/A }
1026N/A fb.append(s).append(' ').
1026N/A append(new Date(je.getTime()).toString());
1026N/A fb.append(' ').append(name);
1026N/A
1026N/A output.get(label).add(fb.toString());
1026N/A }
0N/A }
0N/A }
1026N/A if (verbose != null) {
1026N/A for (Entry<String,List<String>> s: output.entrySet()) {
1026N/A List<String> files = s.getValue();
1026N/A String key = s.getKey();
1026N/A if (key.charAt(0) == '-') { // the signature-related group
3209N/A key = key.substring(1);
1026N/A }
1026N/A int pipe = key.indexOf('|');
1026N/A if (verbose.equals("all")) {
1026N/A for (String f: files) {
1026N/A System.out.println(key.substring(0, pipe) + f);
1026N/A System.out.printf(key.substring(pipe+1));
1026N/A }
1026N/A } else {
1026N/A if (verbose.equals("grouped")) {
1026N/A for (String f: files) {
1026N/A System.out.println(key.substring(0, pipe) + f);
1026N/A }
1026N/A } else if (verbose.equals("summary")) {
1026N/A System.out.print(key.substring(0, pipe));
1026N/A if (files.size() > 1) {
1026N/A System.out.println(files.get(0) + " " +
1026N/A String.format(rb.getString(
3050N/A ".and.d.more."), files.size()-1));
1026N/A } else {
1026N/A System.out.println(files.get(0));
1026N/A }
1026N/A }
1026N/A System.out.printf(key.substring(pipe+1));
1026N/A }
1026N/A }
0N/A System.out.println();
0N/A System.out.println(rb.getString(
3050N/A ".s.signature.was.verified."));
0N/A System.out.println(rb.getString(
3050N/A ".m.entry.is.listed.in.manifest"));
0N/A System.out.println(rb.getString(
3050N/A ".k.at.least.one.certificate.was.found.in.keystore"));
0N/A System.out.println(rb.getString(
3050N/A ".i.at.least.one.certificate.was.found.in.identity.scope"));
1026N/A if (ckaliases.size() > 0) {
3050N/A System.out.println(rb.getString(
3050N/A ".X.not.signed.by.specified.alias.es."));
1026N/A }
0N/A System.out.println();
0N/A }
0N/A if (man == null)
3050N/A System.out.println(rb.getString("no.manifest."));
0N/A
0N/A if (!anySigned) {
0N/A System.out.println(rb.getString(
3050N/A "jar.is.unsigned.signatures.missing.or.not.parsable."));
0N/A } else {
3050N/A System.out.println(rb.getString("jar.verified."));
0N/A if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
0N/A badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
1026N/A notYetValidCert || chainNotValidated ||
1026N/A aliasNotInStore || notSignedByAlias) {
0N/A
0N/A System.out.println();
3050N/A System.out.println(rb.getString("Warning."));
0N/A if (badKeyUsage) {
0N/A System.out.println(
3050N/A rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
0N/A }
0N/A
0N/A if (badExtendedKeyUsage) {
0N/A System.out.println(
3050N/A rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
0N/A }
0N/A
0N/A if (badNetscapeCertType) {
0N/A System.out.println(
3050N/A rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
0N/A }
0N/A
0N/A if (hasUnsignedEntry) {
0N/A System.out.println(rb.getString(
3050N/A "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
0N/A }
0N/A if (hasExpiredCert) {
0N/A System.out.println(rb.getString(
3050N/A "This.jar.contains.entries.whose.signer.certificate.has.expired."));
0N/A }
0N/A if (hasExpiringCert) {
0N/A System.out.println(rb.getString(
3050N/A "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
0N/A }
0N/A if (notYetValidCert) {
0N/A System.out.println(rb.getString(
3050N/A "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
0N/A }
0N/A
1026N/A if (chainNotValidated) {
1026N/A System.out.println(
3050N/A rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated."));
1026N/A }
1026N/A
1026N/A if (notSignedByAlias) {
1026N/A System.out.println(
3050N/A rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
1026N/A }
1026N/A
1026N/A if (aliasNotInStore) {
3050N/A System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
1026N/A }
1026N/A if (! (verbose != null && showcerts)) {
0N/A System.out.println();
0N/A System.out.println(rb.getString(
3050N/A "Re.run.with.the.verbose.and.certs.options.for.more.details."));
0N/A }
0N/A }
0N/A }
1026N/A return;
0N/A } catch (Exception e) {
3050N/A System.out.println(rb.getString("jarsigner.") + e);
0N/A if (debug) {
0N/A e.printStackTrace();
0N/A }
0N/A } finally { // close the resource
0N/A if (jf != null) {
0N/A jf.close();
0N/A }
0N/A }
0N/A
0N/A System.exit(1);
0N/A }
0N/A
0N/A private static MessageFormat validityTimeForm = null;
0N/A private static MessageFormat notYetTimeForm = null;
0N/A private static MessageFormat expiredTimeForm = null;
0N/A private static MessageFormat expiringTimeForm = null;
0N/A
0N/A /*
0N/A * Display some details about a certificate:
0N/A *
0N/A * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
0N/A * [<validity-period> | <expiry-warning>]
1026N/A *
1026N/A * Note: no newline character at the end
0N/A */
0N/A String printCert(String tab, Certificate c, boolean checkValidityPeriod,
5663N/A Date timestamp, boolean checkUsage) {
0N/A
0N/A StringBuilder certStr = new StringBuilder();
3050N/A String space = rb.getString("SPACE");
0N/A X509Certificate x509Cert = null;
0N/A
0N/A if (c instanceof X509Certificate) {
0N/A x509Cert = (X509Certificate) c;
0N/A certStr.append(tab).append(x509Cert.getType())
3050N/A .append(rb.getString("COMMA"))
0N/A .append(x509Cert.getSubjectDN().getName());
0N/A } else {
0N/A certStr.append(tab).append(c.getType());
0N/A }
0N/A
0N/A String alias = storeHash.get(c);
0N/A if (alias != null) {
0N/A certStr.append(space).append(alias);
0N/A }
0N/A
0N/A if (checkValidityPeriod && x509Cert != null) {
0N/A
0N/A certStr.append("\n").append(tab).append("[");
0N/A Date notAfter = x509Cert.getNotAfter();
0N/A try {
5663N/A boolean printValidity = true;
5663N/A if (timestamp == null) {
5663N/A x509Cert.checkValidity();
5663N/A // test if cert will expire within six months
5663N/A if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) {
5663N/A hasExpiringCert = true;
5663N/A if (expiringTimeForm == null) {
5663N/A expiringTimeForm = new MessageFormat(
5663N/A rb.getString("certificate.will.expire.on"));
5663N/A }
5663N/A Object[] source = { notAfter };
5663N/A certStr.append(expiringTimeForm.format(source));
5663N/A printValidity = false;
5663N/A }
5663N/A } else {
5663N/A x509Cert.checkValidity(timestamp);
0N/A }
5663N/A if (printValidity) {
0N/A if (validityTimeForm == null) {
0N/A validityTimeForm = new MessageFormat(
3050N/A rb.getString("certificate.is.valid.from"));
0N/A }
0N/A Object[] source = { x509Cert.getNotBefore(), notAfter };
0N/A certStr.append(validityTimeForm.format(source));
0N/A }
0N/A } catch (CertificateExpiredException cee) {
0N/A hasExpiredCert = true;
0N/A
0N/A if (expiredTimeForm == null) {
0N/A expiredTimeForm = new MessageFormat(
3050N/A rb.getString("certificate.expired.on"));
0N/A }
0N/A Object[] source = { notAfter };
0N/A certStr.append(expiredTimeForm.format(source));
0N/A
0N/A } catch (CertificateNotYetValidException cnyve) {
0N/A notYetValidCert = true;
0N/A
0N/A if (notYetTimeForm == null) {
0N/A notYetTimeForm = new MessageFormat(
3050N/A rb.getString("certificate.is.not.valid.until"));
0N/A }
0N/A Object[] source = { x509Cert.getNotBefore() };
0N/A certStr.append(notYetTimeForm.format(source));
0N/A }
0N/A certStr.append("]");
0N/A
3210N/A if (checkUsage) {
3210N/A boolean[] bad = new boolean[3];
3210N/A checkCertUsage(x509Cert, bad);
3210N/A if (bad[0] || bad[1] || bad[2]) {
3210N/A String x = "";
3210N/A if (bad[0]) {
3210N/A x ="KeyUsage";
3210N/A }
3210N/A if (bad[1]) {
3210N/A if (x.length() > 0) x = x + ", ";
3210N/A x = x + "ExtendedKeyUsage";
3210N/A }
3210N/A if (bad[2]) {
3210N/A if (x.length() > 0) x = x + ", ";
3210N/A x = x + "NetscapeCertType";
3210N/A }
3210N/A certStr.append("\n").append(tab)
0N/A .append(MessageFormat.format(rb.getString(
3050N/A ".{0}.extension.does.not.support.code.signing."), x));
3210N/A }
0N/A }
0N/A }
0N/A return certStr.toString();
0N/A }
0N/A
0N/A private static MessageFormat signTimeForm = null;
0N/A
0N/A private String printTimestamp(String tab, Timestamp timestamp) {
0N/A
0N/A if (signTimeForm == null) {
0N/A signTimeForm =
3050N/A new MessageFormat(rb.getString("entry.was.signed.on"));
0N/A }
0N/A Object[] source = { timestamp.getTimestamp() };
0N/A
0N/A return new StringBuilder().append(tab).append("[")
0N/A .append(signTimeForm.format(source)).append("]").toString();
0N/A }
0N/A
3388N/A private Map<CodeSigner,Integer> cacheForInKS = new IdentityHashMap<>();
1026N/A
1026N/A private int inKeyStoreForOneSigner(CodeSigner signer) {
1026N/A if (cacheForInKS.containsKey(signer)) {
1026N/A return cacheForInKS.get(signer);
1026N/A }
1026N/A
1026N/A boolean found = false;
1026N/A int result = 0;
1026N/A List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
1026N/A for (Certificate c : certs) {
1026N/A String alias = storeHash.get(c);
1026N/A if (alias != null) {
1026N/A if (alias.startsWith("(")) {
1026N/A result |= IN_KEYSTORE;
1026N/A } else if (alias.startsWith("[")) {
1026N/A result |= IN_SCOPE;
1026N/A }
1026N/A if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
1026N/A result |= SIGNED_BY_ALIAS;
1026N/A }
1026N/A } else {
1026N/A if (store != null) {
1026N/A try {
1026N/A alias = store.getCertificateAlias(c);
1026N/A } catch (KeyStoreException kse) {
1026N/A // never happens, because keystore has been loaded
1026N/A }
1026N/A if (alias != null) {
1026N/A storeHash.put(c, "(" + alias + ")");
1026N/A found = true;
1026N/A result |= IN_KEYSTORE;
1026N/A }
1026N/A }
1026N/A if (ckaliases.contains(alias)) {
1026N/A result |= SIGNED_BY_ALIAS;
1026N/A }
1026N/A }
1026N/A }
1026N/A cacheForInKS.put(signer, result);
1026N/A return result;
1026N/A }
1026N/A
3388N/A Hashtable<Certificate, String> storeHash = new Hashtable<>();
0N/A
0N/A int inKeyStore(CodeSigner[] signers) {
0N/A
0N/A if (signers == null)
0N/A return 0;
0N/A
1026N/A int output = 0;
0N/A
1026N/A for (CodeSigner signer: signers) {
1026N/A int result = inKeyStoreForOneSigner(signer);
1026N/A output |= result;
0N/A }
1026N/A if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
1026N/A output |= NOT_ALIAS;
1026N/A }
1026N/A return output;
0N/A }
0N/A
0N/A void signJar(String jarName, String alias, String[] args)
0N/A throws Exception {
0N/A boolean aliasUsed = false;
0N/A X509Certificate tsaCert = null;
0N/A
0N/A if (sigfile == null) {
0N/A sigfile = alias;
0N/A aliasUsed = true;
0N/A }
0N/A
0N/A if (sigfile.length() > 8) {
1786N/A sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH);
0N/A } else {
1786N/A sigfile = sigfile.toUpperCase(Locale.ENGLISH);
0N/A }
0N/A
0N/A StringBuilder tmpSigFile = new StringBuilder(sigfile.length());
0N/A for (int j = 0; j < sigfile.length(); j++) {
0N/A char c = sigfile.charAt(j);
0N/A if (!
0N/A ((c>= 'A' && c<= 'Z') ||
0N/A (c>= '0' && c<= '9') ||
0N/A (c == '-') ||
0N/A (c == '_'))) {
0N/A if (aliasUsed) {
0N/A // convert illegal characters from the alias to be _'s
0N/A c = '_';
0N/A } else {
0N/A throw new
0N/A RuntimeException(rb.getString
3050N/A ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));
0N/A }
0N/A }
0N/A tmpSigFile.append(c);
0N/A }
0N/A
0N/A sigfile = tmpSigFile.toString();
0N/A
0N/A String tmpJarName;
0N/A if (signedjar == null) tmpJarName = jarName+".sig";
0N/A else tmpJarName = signedjar;
0N/A
0N/A File jarFile = new File(jarName);
0N/A File signedJarFile = new File(tmpJarName);
0N/A
0N/A // Open the jar (zip) file
0N/A try {
0N/A zipFile = new ZipFile(jarName);
0N/A } catch (IOException ioe) {
3050N/A error(rb.getString("unable.to.open.jar.file.")+jarName, ioe);
0N/A }
0N/A
0N/A FileOutputStream fos = null;
0N/A try {
0N/A fos = new FileOutputStream(signedJarFile);
0N/A } catch (IOException ioe) {
3050N/A error(rb.getString("unable.to.create.")+tmpJarName, ioe);
0N/A }
0N/A
0N/A PrintStream ps = new PrintStream(fos);
0N/A ZipOutputStream zos = new ZipOutputStream(ps);
0N/A
0N/A /* First guess at what they might be - we don't xclude RSA ones. */
1786N/A String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH);
1786N/A String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH);
0N/A
0N/A Manifest manifest = new Manifest();
0N/A Map<String,Attributes> mfEntries = manifest.getEntries();
0N/A
0N/A // The Attributes of manifest before updating
0N/A Attributes oldAttr = null;
0N/A
0N/A boolean mfModified = false;
0N/A boolean mfCreated = false;
0N/A byte[] mfRawBytes = null;
0N/A
0N/A try {
0N/A MessageDigest digests[] = { MessageDigest.getInstance(digestalg) };
0N/A
0N/A // Check if manifest exists
0N/A ZipEntry mfFile;
0N/A if ((mfFile = getManifestFile(zipFile)) != null) {
0N/A // Manifest exists. Read its raw bytes.
0N/A mfRawBytes = getBytes(zipFile, mfFile);
0N/A manifest.read(new ByteArrayInputStream(mfRawBytes));
0N/A oldAttr = (Attributes)(manifest.getMainAttributes().clone());
0N/A } else {
0N/A // Create new manifest
0N/A Attributes mattr = manifest.getMainAttributes();
0N/A mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
0N/A "1.0");
0N/A String javaVendor = System.getProperty("java.vendor");
0N/A String jdkVersion = System.getProperty("java.version");
0N/A mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
0N/A + ")");
0N/A mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
0N/A mfCreated = true;
0N/A }
0N/A
0N/A /*
0N/A * For each entry in jar
0N/A * (except for signature-related META-INF entries),
0N/A * do the following:
0N/A *
0N/A * - if entry is not contained in manifest, add it to manifest;
0N/A * - if entry is contained in manifest, calculate its hash and
0N/A * compare it with the one in the manifest; if they are
0N/A * different, replace the hash in the manifest with the newly
0N/A * generated one. (This may invalidate existing signatures!)
0N/A */
0N/A BASE64Encoder encoder = new JarBASE64Encoder();
3388N/A Vector<ZipEntry> mfFiles = new Vector<>();
0N/A
2345N/A boolean wasSigned = false;
2345N/A
0N/A for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
0N/A enum_.hasMoreElements();) {
0N/A ZipEntry ze = enum_.nextElement();
0N/A
0N/A if (ze.getName().startsWith(META_INF)) {
0N/A // Store META-INF files in vector, so they can be written
0N/A // out first
0N/A mfFiles.addElement(ze);
0N/A
2345N/A if (SignatureFileVerifier.isBlockOrSF(
2345N/A ze.getName().toUpperCase(Locale.ENGLISH))) {
2345N/A wasSigned = true;
2345N/A }
2345N/A
0N/A if (signatureRelated(ze.getName())) {
0N/A // ignore signature-related and manifest files
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A if (manifest.getAttributes(ze.getName()) != null) {
0N/A // jar entry is contained in manifest, check and
0N/A // possibly update its digest attributes
0N/A if (updateDigests(ze, zipFile, digests, encoder,
0N/A manifest) == true) {
0N/A mfModified = true;
0N/A }
0N/A } else if (!ze.isDirectory()) {
0N/A // Add entry to manifest
0N/A Attributes attrs = getDigestAttributes(ze, zipFile,
0N/A digests,
0N/A encoder);
0N/A mfEntries.put(ze.getName(), attrs);
0N/A mfModified = true;
0N/A }
0N/A }
0N/A
0N/A // Recalculate the manifest raw bytes if necessary
0N/A if (mfModified) {
0N/A ByteArrayOutputStream baos = new ByteArrayOutputStream();
0N/A manifest.write(baos);
2345N/A if (wasSigned) {
2345N/A byte[] newBytes = baos.toByteArray();
2345N/A if (mfRawBytes != null
2345N/A && oldAttr.equals(manifest.getMainAttributes())) {
0N/A
2345N/A /*
2345N/A * Note:
2345N/A *
2345N/A * The Attributes object is based on HashMap and can handle
2345N/A * continuation columns. Therefore, even if the contents are
2345N/A * not changed (in a Map view), the bytes that it write()
2345N/A * may be different from the original bytes that it read()
2345N/A * from. Since the signature on the main attributes is based
2345N/A * on raw bytes, we must retain the exact bytes.
2345N/A */
0N/A
2345N/A int newPos = findHeaderEnd(newBytes);
2345N/A int oldPos = findHeaderEnd(mfRawBytes);
0N/A
2345N/A if (newPos == oldPos) {
2345N/A System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
2345N/A } else {
2345N/A // cat oldHead newTail > newBytes
2345N/A byte[] lastBytes = new byte[oldPos +
2345N/A newBytes.length - newPos];
2345N/A System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
2345N/A System.arraycopy(newBytes, newPos, lastBytes, oldPos,
2345N/A newBytes.length - newPos);
2345N/A newBytes = lastBytes;
2345N/A }
0N/A }
2345N/A mfRawBytes = newBytes;
2345N/A } else {
2345N/A mfRawBytes = baos.toByteArray();
0N/A }
0N/A }
0N/A
0N/A // Write out the manifest
0N/A if (mfModified) {
0N/A // manifest file has new length
0N/A mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
0N/A }
1026N/A if (verbose != null) {
0N/A if (mfCreated) {
3050N/A System.out.println(rb.getString(".adding.") +
0N/A mfFile.getName());
0N/A } else if (mfModified) {
3050N/A System.out.println(rb.getString(".updating.") +
0N/A mfFile.getName());
0N/A }
0N/A }
0N/A zos.putNextEntry(mfFile);
0N/A zos.write(mfRawBytes);
0N/A
0N/A // Calculate SignatureFile (".SF") and SignatureBlockFile
0N/A ManifestDigester manDig = new ManifestDigester(mfRawBytes);
0N/A SignatureFile sf = new SignatureFile(digests, manifest, manDig,
0N/A sigfile, signManifest);
0N/A
0N/A if (tsaAlias != null) {
0N/A tsaCert = getTsaCert(tsaAlias);
0N/A }
0N/A
0N/A SignatureFile.Block block = null;
0N/A
0N/A try {
0N/A block =
3659N/A sf.generateBlock(privateKey, sigalg, certChain,
0N/A externalSF, tsaUrl, tsaCert, signingMechanism, args,
0N/A zipFile);
0N/A } catch (SocketTimeoutException e) {
0N/A // Provide a helpful message when TSA is beyond a firewall
3050N/A error(rb.getString("unable.to.sign.jar.") +
3050N/A rb.getString("no.response.from.the.Timestamping.Authority.") +
3868N/A "\n -J-Dhttp.proxyHost=<hostname>" +
3868N/A "\n -J-Dhttp.proxyPort=<portnumber>\n" +
3868N/A rb.getString("or") +
3868N/A "\n -J-Dhttps.proxyHost=<hostname> " +
3868N/A "\n -J-Dhttps.proxyPort=<portnumber> ", e);
0N/A }
0N/A
0N/A sfFilename = sf.getMetaName();
0N/A bkFilename = block.getMetaName();
0N/A
0N/A ZipEntry sfFile = new ZipEntry(sfFilename);
0N/A ZipEntry bkFile = new ZipEntry(bkFilename);
0N/A
0N/A long time = System.currentTimeMillis();
0N/A sfFile.setTime(time);
0N/A bkFile.setTime(time);
0N/A
0N/A // signature file
0N/A zos.putNextEntry(sfFile);
0N/A sf.write(zos);
1026N/A if (verbose != null) {
0N/A if (zipFile.getEntry(sfFilename) != null) {
3050N/A System.out.println(rb.getString(".updating.") +
0N/A sfFilename);
0N/A } else {
3050N/A System.out.println(rb.getString(".adding.") +
0N/A sfFilename);
0N/A }
0N/A }
0N/A
1026N/A if (verbose != null) {
0N/A if (tsaUrl != null || tsaCert != null) {
0N/A System.out.println(
3050N/A rb.getString("requesting.a.signature.timestamp"));
0N/A }
0N/A if (tsaUrl != null) {
3050N/A System.out.println(rb.getString("TSA.location.") + tsaUrl);
0N/A }
0N/A if (tsaCert != null) {
0N/A String certUrl =
0N/A TimestampedSigner.getTimestampingUrl(tsaCert);
0N/A if (certUrl != null) {
3050N/A System.out.println(rb.getString("TSA.location.") +
0N/A certUrl);
0N/A }
3050N/A System.out.println(rb.getString("TSA.certificate.") +
5663N/A printCert("", tsaCert, false, null, false));
0N/A }
0N/A if (signingMechanism != null) {
0N/A System.out.println(
3050N/A rb.getString("using.an.alternative.signing.mechanism"));
0N/A }
0N/A }
0N/A
0N/A // signature block file
0N/A zos.putNextEntry(bkFile);
0N/A block.write(zos);
1026N/A if (verbose != null) {
0N/A if (zipFile.getEntry(bkFilename) != null) {
3050N/A System.out.println(rb.getString(".updating.") +
0N/A bkFilename);
0N/A } else {
3050N/A System.out.println(rb.getString(".adding.") +
0N/A bkFilename);
0N/A }
0N/A }
0N/A
0N/A // Write out all other META-INF files that we stored in the
0N/A // vector
0N/A for (int i=0; i<mfFiles.size(); i++) {
0N/A ZipEntry ze = mfFiles.elementAt(i);
0N/A if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
0N/A && !ze.getName().equalsIgnoreCase(sfFilename)
0N/A && !ze.getName().equalsIgnoreCase(bkFilename)) {
0N/A writeEntry(zipFile, zos, ze);
0N/A }
0N/A }
0N/A
0N/A // Write out all other files
0N/A for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
0N/A enum_.hasMoreElements();) {
0N/A ZipEntry ze = enum_.nextElement();
0N/A
0N/A if (!ze.getName().startsWith(META_INF)) {
1026N/A if (verbose != null) {
0N/A if (manifest.getAttributes(ze.getName()) != null)
3050N/A System.out.println(rb.getString(".signing.") +
0N/A ze.getName());
0N/A else
3050N/A System.out.println(rb.getString(".adding.") +
0N/A ze.getName());
0N/A }
0N/A writeEntry(zipFile, zos, ze);
0N/A }
0N/A }
0N/A } catch(IOException ioe) {
3050N/A error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
0N/A } finally {
0N/A // close the resouces
0N/A if (zipFile != null) {
0N/A zipFile.close();
0N/A zipFile = null;
0N/A }
0N/A
0N/A if (zos != null) {
0N/A zos.close();
0N/A }
0N/A }
0N/A
0N/A // no IOException thrown in the follow try clause, so disable
0N/A // the try clause.
0N/A // try {
0N/A if (signedjar == null) {
0N/A // attempt an atomic rename. If that fails,
0N/A // rename the original jar file, then the signed
0N/A // one, then delete the original.
0N/A if (!signedJarFile.renameTo(jarFile)) {
0N/A File origJar = new File(jarName+".orig");
0N/A
0N/A if (jarFile.renameTo(origJar)) {
0N/A if (signedJarFile.renameTo(jarFile)) {
0N/A origJar.delete();
0N/A } else {
0N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("attempt.to.rename.signedJarFile.to.jarFile.failed"));
0N/A Object[] source = {signedJarFile, jarFile};
0N/A error(form.format(source));
0N/A }
0N/A } else {
0N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("attempt.to.rename.jarFile.to.origJar.failed"));
0N/A Object[] source = {jarFile, origJar};
0N/A error(form.format(source));
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (hasExpiredCert || hasExpiringCert || notYetValidCert
1026N/A || badKeyUsage || badExtendedKeyUsage
1026N/A || badNetscapeCertType || chainNotValidated) {
0N/A System.out.println();
0N/A
3050N/A System.out.println(rb.getString("Warning."));
0N/A if (badKeyUsage) {
0N/A System.out.println(
3050N/A rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
0N/A }
0N/A
0N/A if (badExtendedKeyUsage) {
0N/A System.out.println(
3050N/A rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
0N/A }
0N/A
0N/A if (badNetscapeCertType) {
0N/A System.out.println(
3050N/A rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
0N/A }
0N/A
0N/A if (hasExpiredCert) {
0N/A System.out.println(
3050N/A rb.getString("The.signer.certificate.has.expired."));
0N/A } else if (hasExpiringCert) {
0N/A System.out.println(
3050N/A rb.getString("The.signer.certificate.will.expire.within.six.months."));
0N/A } else if (notYetValidCert) {
0N/A System.out.println(
3050N/A rb.getString("The.signer.certificate.is.not.yet.valid."));
0N/A }
1026N/A
1026N/A if (chainNotValidated) {
1026N/A System.out.println(
3050N/A rb.getString("The.signer.s.certificate.chain.is.not.validated."));
1026N/A }
0N/A }
0N/A
0N/A // no IOException thrown in the above try clause, so disable
0N/A // the catch clause.
0N/A // } catch(IOException ioe) {
3050N/A // error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
0N/A // }
0N/A }
0N/A
0N/A /**
2345N/A * Find the length of header inside bs. The header is a multiple (>=0)
2345N/A * lines of attributes plus an empty line. The empty line is included
2345N/A * in the header.
0N/A */
0N/A private int findHeaderEnd(byte[] bs) {
2345N/A // Initial state true to deal with empty header
2345N/A boolean newline = true; // just met a newline
2345N/A int len = bs.length;
2345N/A for (int i=0; i<len; i++) {
2345N/A switch (bs[i]) {
2345N/A case '\r':
2405N/A if (i < len - 1 && bs[i+1] == '\n') i++;
2345N/A // fallthrough
2345N/A case '\n':
2345N/A if (newline) return i+1; //+1 to get length
2345N/A newline = true;
2345N/A break;
2345N/A default:
2345N/A newline = false;
0N/A }
0N/A }
2345N/A // If header end is not found, it means the MANIFEST.MF has only
2345N/A // the main attributes section and it does not end with 2 newlines.
2345N/A // Returns the whole length so that it can be completely replaced.
2345N/A return len;
0N/A }
0N/A
0N/A /**
0N/A * signature-related files include:
0N/A * . META-INF/MANIFEST.MF
0N/A * . META-INF/SIG-*
0N/A * . META-INF/*.SF
0N/A * . META-INF/*.DSA
0N/A * . META-INF/*.RSA
1786N/A * . META-INF/*.EC
0N/A */
0N/A private boolean signatureRelated(String name) {
1786N/A String ucName = name.toUpperCase(Locale.ENGLISH);
0N/A if (ucName.equals(JarFile.MANIFEST_NAME) ||
0N/A ucName.equals(META_INF) ||
0N/A (ucName.startsWith(SIG_PREFIX) &&
0N/A ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
0N/A return true;
0N/A }
0N/A
0N/A if (ucName.startsWith(META_INF) &&
0N/A SignatureFileVerifier.isBlockOrSF(ucName)) {
1786N/A // .SF/.DSA/.RSA/.EC files in META-INF subdirs
0N/A // are not considered signature-related
0N/A return (ucName.indexOf("/") == ucName.lastIndexOf("/"));
0N/A }
0N/A
0N/A return false;
0N/A }
0N/A
3388N/A Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();
1026N/A
1026N/A /**
1026N/A * Returns a string of singer info, with a newline at the end
1026N/A */
5663N/A private String signerInfo(CodeSigner signer, String tab) {
1026N/A if (cacheForSignerInfo.containsKey(signer)) {
1026N/A return cacheForSignerInfo.get(signer);
1026N/A }
1026N/A StringBuffer s = new StringBuffer();
1026N/A List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
1026N/A // display the signature timestamp, if present
5663N/A Date timestamp;
5663N/A Timestamp ts = signer.getTimestamp();
5663N/A if (ts != null) {
5663N/A s.append(printTimestamp(tab, ts));
1803N/A s.append('\n');
5663N/A timestamp = ts.getTimestamp();
5663N/A } else {
5663N/A timestamp = null;
1026N/A }
5663N/A // display the certificate(s). The first one is end-entity cert and
3210N/A // its KeyUsage should be checked.
3210N/A boolean first = true;
1026N/A for (Certificate c : certs) {
5663N/A s.append(printCert(tab, c, true, timestamp, first));
1026N/A s.append('\n');
3210N/A first = false;
1026N/A }
1026N/A try {
1026N/A CertPath cp = certificateFactory.generateCertPath(certs);
1026N/A validator.validate(cp, pkixParameters);
1026N/A } catch (Exception e) {
4411N/A if (debug) {
4411N/A e.printStackTrace();
4411N/A }
5663N/A if (e.getCause() != null &&
5663N/A (e.getCause() instanceof CertificateExpiredException ||
5663N/A e.getCause() instanceof CertificateNotYetValidException)) {
5663N/A // No more warning, we alreay have hasExpiredCert or notYetValidCert
5663N/A } else {
5663N/A chainNotValidated = true;
5663N/A s.append(tab + rb.getString(".CertPath.not.validated.") +
5663N/A e.getLocalizedMessage() + "]\n"); // TODO
5663N/A }
1026N/A }
1026N/A String result = s.toString();
1026N/A cacheForSignerInfo.put(signer, result);
1026N/A return result;
1026N/A }
1026N/A
0N/A private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
0N/A throws IOException
0N/A {
0N/A ZipEntry ze2 = new ZipEntry(ze.getName());
0N/A ze2.setMethod(ze.getMethod());
0N/A ze2.setTime(ze.getTime());
0N/A ze2.setComment(ze.getComment());
0N/A ze2.setExtra(ze.getExtra());
0N/A if (ze.getMethod() == ZipEntry.STORED) {
0N/A ze2.setSize(ze.getSize());
0N/A ze2.setCrc(ze.getCrc());
0N/A }
0N/A os.putNextEntry(ze2);
0N/A writeBytes(zf, ze, os);
0N/A }
0N/A
0N/A /**
0N/A * Writes all the bytes for a given entry to the specified output stream.
0N/A */
0N/A private synchronized void writeBytes
0N/A (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
0N/A int n;
0N/A
0N/A InputStream is = null;
0N/A try {
0N/A is = zf.getInputStream(ze);
0N/A long left = ze.getSize();
0N/A
0N/A while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
0N/A os.write(buffer, 0, n);
0N/A left -= n;
0N/A }
0N/A } finally {
0N/A if (is != null) {
0N/A is.close();
0N/A }
0N/A }
0N/A }
0N/A
0N/A void loadKeyStore(String keyStoreName, boolean prompt) {
0N/A
0N/A if (!nullStream && keyStoreName == null) {
0N/A keyStoreName = System.getProperty("user.home") + File.separator
0N/A + ".keystore";
0N/A }
0N/A
0N/A try {
4411N/A
4411N/A certificateFactory = CertificateFactory.getInstance("X.509");
4411N/A validator = CertPathValidator.getInstance("PKIX");
4411N/A Set<TrustAnchor> tas = new HashSet<>();
4411N/A try {
4411N/A KeyStore caks = KeyTool.getCacertsKeyStore();
4411N/A if (caks != null) {
4411N/A Enumeration<String> aliases = caks.aliases();
4411N/A while (aliases.hasMoreElements()) {
4411N/A String a = aliases.nextElement();
4411N/A try {
4411N/A tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
4411N/A } catch (Exception e2) {
4411N/A // ignore, when a SecretkeyEntry does not include a cert
4411N/A }
4411N/A }
4411N/A }
4411N/A } catch (Exception e) {
4411N/A // Ignore, if cacerts cannot be loaded
4411N/A }
4411N/A
0N/A if (providerName == null) {
0N/A store = KeyStore.getInstance(storetype);
0N/A } else {
0N/A store = KeyStore.getInstance(storetype, providerName);
0N/A }
0N/A
0N/A // Get pass phrase
0N/A // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z
0N/A // and on NT call ??
0N/A if (token && storepass == null && !protectedPath
0N/A && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
0N/A storepass = getPass
3050N/A (rb.getString("Enter.Passphrase.for.keystore."));
0N/A } else if (!token && storepass == null && prompt) {
0N/A storepass = getPass
3050N/A (rb.getString("Enter.Passphrase.for.keystore."));
0N/A }
0N/A
4411N/A try {
4411N/A if (nullStream) {
4411N/A store.load(null, storepass);
4411N/A } else {
4411N/A keyStoreName = keyStoreName.replace(File.separatorChar, '/');
4411N/A URL url = null;
4411N/A try {
4411N/A url = new URL(keyStoreName);
4411N/A } catch (java.net.MalformedURLException e) {
4411N/A // try as file
4411N/A url = new File(keyStoreName).toURI().toURL();
0N/A }
4411N/A InputStream is = null;
4411N/A try {
4411N/A is = url.openStream();
4411N/A store.load(is, storepass);
4411N/A } finally {
4411N/A if (is != null) {
4411N/A is.close();
1026N/A }
1026N/A }
1026N/A }
1026N/A Enumeration<String> aliases = store.aliases();
1026N/A while (aliases.hasMoreElements()) {
1026N/A String a = aliases.nextElement();
1026N/A try {
1026N/A X509Certificate c = (X509Certificate)store.getCertificate(a);
1026N/A // Only add TrustedCertificateEntry and self-signed
1026N/A // PrivateKeyEntry
1026N/A if (store.isCertificateEntry(a) ||
1026N/A c.getSubjectDN().equals(c.getIssuerDN())) {
1026N/A tas.add(new TrustAnchor(c, null));
1026N/A }
1026N/A } catch (Exception e2) {
1026N/A // ignore, when a SecretkeyEntry does not include a cert
1026N/A }
1026N/A }
4411N/A } finally {
4411N/A try {
4411N/A pkixParameters = new PKIXParameters(tas);
4411N/A pkixParameters.setRevocationEnabled(false);
4411N/A } catch (InvalidAlgorithmParameterException ex) {
4411N/A // Only if tas is empty
4411N/A }
1026N/A }
0N/A } catch (IOException ioe) {
3050N/A throw new RuntimeException(rb.getString("keystore.load.") +
0N/A ioe.getMessage());
0N/A } catch (java.security.cert.CertificateException ce) {
3050N/A throw new RuntimeException(rb.getString("certificate.exception.") +
0N/A ce.getMessage());
0N/A } catch (NoSuchProviderException pe) {
3050N/A throw new RuntimeException(rb.getString("keystore.load.") +
0N/A pe.getMessage());
0N/A } catch (NoSuchAlgorithmException nsae) {
3050N/A throw new RuntimeException(rb.getString("keystore.load.") +
0N/A nsae.getMessage());
0N/A } catch (KeyStoreException kse) {
0N/A throw new RuntimeException
3050N/A (rb.getString("unable.to.instantiate.keystore.class.") +
0N/A kse.getMessage());
0N/A }
0N/A }
0N/A
0N/A X509Certificate getTsaCert(String alias) {
0N/A
0N/A java.security.cert.Certificate cs = null;
0N/A
0N/A try {
0N/A cs = store.getCertificate(alias);
0N/A } catch (KeyStoreException kse) {
0N/A // this never happens, because keystore has been loaded
0N/A }
0N/A if (cs == null || (!(cs instanceof X509Certificate))) {
0N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the"));
0N/A Object[] source = {alias, alias};
0N/A error(form.format(source));
0N/A }
0N/A return (X509Certificate) cs;
0N/A }
0N/A
0N/A /**
0N/A * Check if userCert is designed to be a code signer
0N/A * @param userCert the certificate to be examined
0N/A * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
0N/A * NetscapeCertType has codeSigning flag turned on.
0N/A * If null, the class field badKeyUsage, badExtendedKeyUsage,
0N/A * badNetscapeCertType will be set.
0N/A */
0N/A void checkCertUsage(X509Certificate userCert, boolean[] bad) {
0N/A
0N/A // Can act as a signer?
1026N/A // 1. if KeyUsage, then [0:digitalSignature] or
1026N/A // [1:nonRepudiation] should be true
0N/A // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
0N/A // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
0N/A // 1,2,3 must be true
0N/A
0N/A if (bad != null) {
0N/A bad[0] = bad[1] = bad[2] = false;
0N/A }
0N/A
0N/A boolean[] keyUsage = userCert.getKeyUsage();
0N/A if (keyUsage != null) {
1026N/A keyUsage = Arrays.copyOf(keyUsage, 9);
1026N/A if (!keyUsage[0] && !keyUsage[1]) {
0N/A if (bad != null) {
0N/A bad[0] = true;
0N/A badKeyUsage = true;
0N/A }
0N/A }
0N/A }
0N/A
0N/A try {
0N/A List<String> xKeyUsage = userCert.getExtendedKeyUsage();
0N/A if (xKeyUsage != null) {
0N/A if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
0N/A && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
0N/A if (bad != null) {
0N/A bad[1] = true;
0N/A badExtendedKeyUsage = true;
0N/A }
0N/A }
0N/A }
0N/A } catch (java.security.cert.CertificateParsingException e) {
0N/A // shouldn't happen
0N/A }
0N/A
0N/A try {
0N/A // OID_NETSCAPE_CERT_TYPE
0N/A byte[] netscapeEx = userCert.getExtensionValue
0N/A ("2.16.840.1.113730.1.1");
0N/A if (netscapeEx != null) {
0N/A DerInputStream in = new DerInputStream(netscapeEx);
0N/A byte[] encoded = in.getOctetString();
0N/A encoded = new DerValue(encoded).getUnalignedBitString()
0N/A .toByteArray();
0N/A
0N/A NetscapeCertTypeExtension extn =
0N/A new NetscapeCertTypeExtension(encoded);
0N/A
0N/A Boolean val = (Boolean)extn.get(
0N/A NetscapeCertTypeExtension.OBJECT_SIGNING);
0N/A if (!val) {
0N/A if (bad != null) {
0N/A bad[2] = true;
0N/A badNetscapeCertType = true;
0N/A }
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A //
0N/A }
0N/A }
0N/A
0N/A void getAliasInfo(String alias) {
0N/A
0N/A Key key = null;
0N/A
0N/A try {
0N/A java.security.cert.Certificate[] cs = null;
1026N/A if (altCertChain != null) {
1026N/A try {
1026N/A cs = CertificateFactory.getInstance("X.509").
1026N/A generateCertificates(new FileInputStream(altCertChain)).
1026N/A toArray(new Certificate[0]);
1026N/A } catch (CertificateException ex) {
3050N/A error(rb.getString("Cannot.restore.certchain.from.file.specified"));
1026N/A } catch (FileNotFoundException ex) {
3050N/A error(rb.getString("File.specified.by.certchain.does.not.exist"));
1026N/A }
1026N/A } else {
1026N/A try {
1026N/A cs = store.getCertificateChain(alias);
1026N/A } catch (KeyStoreException kse) {
1026N/A // this never happens, because keystore has been loaded
1026N/A }
0N/A }
1026N/A if (cs == null || cs.length == 0) {
1026N/A if (altCertChain != null) {
1026N/A error(rb.getString
3050N/A ("Certificate.chain.not.found.in.the.file.specified."));
1026N/A } else {
1026N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and"));
1026N/A Object[] source = {alias, alias};
1026N/A error(form.format(source));
1026N/A }
0N/A }
0N/A
0N/A certChain = new X509Certificate[cs.length];
0N/A for (int i=0; i<cs.length; i++) {
0N/A if (!(cs[i] instanceof X509Certificate)) {
0N/A error(rb.getString
3050N/A ("found.non.X.509.certificate.in.signer.s.chain"));
0N/A }
0N/A certChain[i] = (X509Certificate)cs[i];
0N/A }
0N/A
1026N/A // We don't meant to print anything, the next call
1026N/A // checks validity and keyUsage etc
5663N/A printCert("", certChain[0], true, null, true);
0N/A
1026N/A try {
1026N/A CertPath cp = certificateFactory.generateCertPath(Arrays.asList(certChain));
1026N/A validator.validate(cp, pkixParameters);
1026N/A } catch (Exception e) {
4411N/A if (debug) {
4411N/A e.printStackTrace();
4411N/A }
5663N/A if (e.getCause() != null &&
5663N/A (e.getCause() instanceof CertificateExpiredException ||
5663N/A e.getCause() instanceof CertificateNotYetValidException)) {
5663N/A // No more warning, we alreay have hasExpiredCert or notYetValidCert
5663N/A } else {
5663N/A chainNotValidated = true;
5663N/A }
0N/A }
0N/A
0N/A try {
0N/A if (!token && keypass == null)
0N/A key = store.getKey(alias, storepass);
0N/A else
0N/A key = store.getKey(alias, keypass);
0N/A } catch (UnrecoverableKeyException e) {
0N/A if (token) {
0N/A throw e;
0N/A } else if (keypass == null) {
0N/A // Did not work out, so prompt user for key password
0N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("Enter.key.password.for.alias."));
0N/A Object[] source = {alias};
0N/A keypass = getPass(form.format(source));
0N/A key = store.getKey(alias, keypass);
0N/A }
0N/A }
0N/A } catch (NoSuchAlgorithmException e) {
0N/A error(e.getMessage());
0N/A } catch (UnrecoverableKeyException e) {
3050N/A error(rb.getString("unable.to.recover.key.from.keystore"));
0N/A } catch (KeyStoreException kse) {
0N/A // this never happens, because keystore has been loaded
0N/A }
0N/A
0N/A if (!(key instanceof PrivateKey)) {
0N/A MessageFormat form = new MessageFormat(rb.getString
3050N/A ("key.associated.with.alias.not.a.private.key"));
0N/A Object[] source = {alias};
0N/A error(form.format(source));
0N/A } else {
0N/A privateKey = (PrivateKey)key;
0N/A }
0N/A }
0N/A
0N/A void error(String message)
0N/A {
3050N/A System.out.println(rb.getString("jarsigner.")+message);
0N/A System.exit(1);
0N/A }
0N/A
0N/A
0N/A void error(String message, Exception e)
0N/A {
3050N/A System.out.println(rb.getString("jarsigner.")+message);
0N/A if (debug) {
0N/A e.printStackTrace();
0N/A }
0N/A System.exit(1);
0N/A }
0N/A
0N/A char[] getPass(String prompt)
0N/A {
0N/A System.err.print(prompt);
0N/A System.err.flush();
0N/A try {
0N/A char[] pass = Password.readPassword(System.in);
0N/A
0N/A if (pass == null) {
3050N/A error(rb.getString("you.must.enter.key.password"));
0N/A } else {
0N/A return pass;
0N/A }
0N/A } catch (IOException ioe) {
3050N/A error(rb.getString("unable.to.read.password.")+ioe.getMessage());
0N/A }
0N/A // this shouldn't happen
0N/A return null;
0N/A }
0N/A
0N/A /*
0N/A * Reads all the bytes for a given zip entry.
0N/A */
0N/A private synchronized byte[] getBytes(ZipFile zf,
0N/A ZipEntry ze) throws IOException {
0N/A int n;
0N/A
0N/A InputStream is = null;
0N/A try {
0N/A is = zf.getInputStream(ze);
0N/A baos.reset();
0N/A long left = ze.getSize();
0N/A
0N/A while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
0N/A baos.write(buffer, 0, n);
0N/A left -= n;
0N/A }
0N/A } finally {
0N/A if (is != null) {
0N/A is.close();
0N/A }
0N/A }
0N/A
0N/A return baos.toByteArray();
0N/A }
0N/A
0N/A /*
0N/A * Returns manifest entry from given jar file, or null if given jar file
0N/A * does not have a manifest entry.
0N/A */
0N/A private ZipEntry getManifestFile(ZipFile zf) {
0N/A ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
0N/A if (ze == null) {
0N/A // Check all entries for matching name
0N/A Enumeration<? extends ZipEntry> enum_ = zf.entries();
0N/A while (enum_.hasMoreElements() && ze == null) {
0N/A ze = enum_.nextElement();
0N/A if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
0N/A (ze.getName())) {
0N/A ze = null;
0N/A }
0N/A }
0N/A }
0N/A return ze;
0N/A }
0N/A
0N/A /*
0N/A * Computes the digests of a zip entry, and returns them as an array
0N/A * of base64-encoded strings.
0N/A */
0N/A private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
0N/A MessageDigest[] digests,
0N/A BASE64Encoder encoder)
0N/A throws IOException {
0N/A
0N/A int n, i;
0N/A InputStream is = null;
0N/A try {
0N/A is = zf.getInputStream(ze);
0N/A long left = ze.getSize();
0N/A while((left > 0)
0N/A && (n = is.read(buffer, 0, buffer.length)) != -1) {
0N/A for (i=0; i<digests.length; i++) {
0N/A digests[i].update(buffer, 0, n);
0N/A }
0N/A left -= n;
0N/A }
0N/A } finally {
0N/A if (is != null) {
0N/A is.close();
0N/A }
0N/A }
0N/A
0N/A // complete the digests
0N/A String[] base64Digests = new String[digests.length];
0N/A for (i=0; i<digests.length; i++) {
0N/A base64Digests[i] = encoder.encode(digests[i].digest());
0N/A }
0N/A return base64Digests;
0N/A }
0N/A
0N/A /*
0N/A * Computes the digests of a zip entry, and returns them as a list of
0N/A * attributes
0N/A */
0N/A private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
0N/A MessageDigest[] digests,
0N/A BASE64Encoder encoder)
0N/A throws IOException {
0N/A
0N/A String[] base64Digests = getDigests(ze, zf, digests, encoder);
0N/A Attributes attrs = new Attributes();
0N/A
0N/A for (int i=0; i<digests.length; i++) {
0N/A attrs.putValue(digests[i].getAlgorithm()+"-Digest",
0N/A base64Digests[i]);
0N/A }
0N/A return attrs;
0N/A }
0N/A
0N/A /*
0N/A * Updates the digest attributes of a manifest entry, by adding or
0N/A * replacing digest values.
0N/A * A digest value is added if the manifest entry does not contain a digest
0N/A * for that particular algorithm.
0N/A * A digest value is replaced if it is obsolete.
0N/A *
0N/A * Returns true if the manifest entry has been changed, and false
0N/A * otherwise.
0N/A */
0N/A private boolean updateDigests(ZipEntry ze, ZipFile zf,
0N/A MessageDigest[] digests,
0N/A BASE64Encoder encoder,
0N/A Manifest mf) throws IOException {
0N/A boolean update = false;
0N/A
0N/A Attributes attrs = mf.getAttributes(ze.getName());
0N/A String[] base64Digests = getDigests(ze, zf, digests, encoder);
0N/A
0N/A for (int i=0; i<digests.length; i++) {
1604N/A // The entry name to be written into attrs
1604N/A String name = null;
1604N/A try {
1604N/A // Find if the digest already exists
1604N/A AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
1604N/A for (Object key: attrs.keySet()) {
1604N/A if (key instanceof Attributes.Name) {
1604N/A String n = ((Attributes.Name)key).toString();
1604N/A if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
1604N/A String tmp = n.substring(0, n.length() - 7);
1604N/A if (AlgorithmId.get(tmp).equals(aid)) {
1604N/A name = n;
1604N/A break;
1604N/A }
1604N/A }
1604N/A }
1604N/A }
1604N/A } catch (NoSuchAlgorithmException nsae) {
1604N/A // Ignored. Writing new digest entry.
0N/A }
1604N/A
1604N/A if (name == null) {
1604N/A name = digests[i].getAlgorithm()+"-Digest";
0N/A attrs.putValue(name, base64Digests[i]);
0N/A update=true;
0N/A } else {
0N/A // compare digests, and replace the one in the manifest
0N/A // if they are different
1604N/A String mfDigest = attrs.getValue(name);
0N/A if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
0N/A attrs.putValue(name, base64Digests[i]);
0N/A update=true;
0N/A }
0N/A }
0N/A }
0N/A return update;
0N/A }
0N/A
0N/A /*
0N/A * Try to load the specified signing mechanism.
0N/A * The URL class loader is used.
0N/A */
0N/A private ContentSigner loadSigningMechanism(String signerClassName,
0N/A String signerClassPath) throws Exception {
0N/A
0N/A // construct class loader
0N/A String cpString = null; // make sure env.class.path defaults to dot
0N/A
0N/A // do prepends to get correct ordering
0N/A cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);
0N/A cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
0N/A cpString = PathList.appendPath(signerClassPath, cpString);
0N/A URL[] urls = PathList.pathToURLs(cpString);
0N/A ClassLoader appClassLoader = new URLClassLoader(urls);
0N/A
0N/A // attempt to find signer
0N/A Class signerClass = appClassLoader.loadClass(signerClassName);
0N/A
0N/A // Check that it implements ContentSigner
0N/A Object signer = signerClass.newInstance();
0N/A if (!(signer instanceof ContentSigner)) {
0N/A MessageFormat form = new MessageFormat(
3050N/A rb.getString("signerClass.is.not.a.signing.mechanism"));
0N/A Object[] source = {signerClass.getName()};
0N/A throw new IllegalArgumentException(form.format(source));
0N/A }
0N/A return (ContentSigner)signer;
0N/A }
0N/A}
0N/A
0N/A/**
0N/A * This is a BASE64Encoder that does not insert a default newline at the end of
0N/A * every output line. This is necessary because java.util.jar does its own
0N/A * line management (see Manifest.make72Safe()). Inserting additional new lines
0N/A * can cause line-wrapping problems (see CR 6219522).
0N/A */
0N/Aclass JarBASE64Encoder extends BASE64Encoder {
0N/A /**
0N/A * Encode the suffix that ends every output line.
0N/A */
0N/A protected void encodeLineSuffix(OutputStream aStream) throws IOException { }
0N/A}
0N/A
0N/Aclass SignatureFile {
0N/A
0N/A /** SignatureFile */
0N/A Manifest sf;
0N/A
0N/A /** .SF base name */
0N/A String baseName;
0N/A
0N/A public SignatureFile(MessageDigest digests[],
0N/A Manifest mf,
0N/A ManifestDigester md,
0N/A String baseName,
0N/A boolean signManifest)
0N/A
0N/A {
0N/A this.baseName = baseName;
0N/A
0N/A String version = System.getProperty("java.version");
0N/A String javaVendor = System.getProperty("java.vendor");
0N/A
0N/A sf = new Manifest();
0N/A Attributes mattr = sf.getMainAttributes();
0N/A BASE64Encoder encoder = new JarBASE64Encoder();
0N/A
0N/A mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
0N/A mattr.putValue("Created-By", version + " (" + javaVendor + ")");
0N/A
0N/A if (signManifest) {
0N/A // sign the whole manifest
0N/A for (int i=0; i < digests.length; i++) {
0N/A mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
0N/A encoder.encode(md.manifestDigest(digests[i])));
0N/A }
0N/A }
0N/A
0N/A // create digest of the manifest main attributes
0N/A ManifestDigester.Entry mde =
0N/A md.get(ManifestDigester.MF_MAIN_ATTRS, false);
0N/A if (mde != null) {
0N/A for (int i=0; i < digests.length; i++) {
0N/A mattr.putValue(digests[i].getAlgorithm() +
0N/A "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
0N/A encoder.encode(mde.digest(digests[i])));
0N/A }
0N/A } else {
0N/A throw new IllegalStateException
0N/A ("ManifestDigester failed to create " +
0N/A "Manifest-Main-Attribute entry");
0N/A }
0N/A
0N/A /* go through the manifest entries and create the digests */
0N/A
0N/A Map<String,Attributes> entries = sf.getEntries();
0N/A Iterator<Map.Entry<String,Attributes>> mit =
0N/A mf.getEntries().entrySet().iterator();
0N/A while(mit.hasNext()) {
0N/A Map.Entry<String,Attributes> e = mit.next();
0N/A String name = e.getKey();
0N/A mde = md.get(name, false);
0N/A if (mde != null) {
0N/A Attributes attr = new Attributes();
0N/A for (int i=0; i < digests.length; i++) {
0N/A attr.putValue(digests[i].getAlgorithm()+"-Digest",
0N/A encoder.encode(mde.digest(digests[i])));
0N/A }
0N/A entries.put(name, attr);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Writes the SignatureFile to the specified OutputStream.
0N/A *
0N/A * @param out the output stream
0N/A * @exception IOException if an I/O error has occurred
0N/A */
0N/A
0N/A public void write(OutputStream out) throws IOException
0N/A {
0N/A sf.write(out);
0N/A }
0N/A
0N/A /**
0N/A * get .SF file name
0N/A */
0N/A public String getMetaName()
0N/A {
0N/A return "META-INF/"+ baseName + ".SF";
0N/A }
0N/A
0N/A /**
0N/A * get base file name
0N/A */
0N/A public String getBaseName()
0N/A {
0N/A return baseName;
0N/A }
0N/A
0N/A /*
0N/A * Generate a signed data block.
0N/A * If a URL or a certificate (containing a URL) for a Timestamping
0N/A * Authority is supplied then a signature timestamp is generated and
0N/A * inserted into the signed data block.
0N/A *
0N/A * @param sigalg signature algorithm to use, or null to use default
0N/A * @param tsaUrl The location of the Timestamping Authority. If null
0N/A * then no timestamp is requested.
0N/A * @param tsaCert The certificate for the Timestamping Authority. If null
0N/A * then no timestamp is requested.
0N/A * @param signingMechanism The signing mechanism to use.
0N/A * @param args The command-line arguments to jarsigner.
0N/A * @param zipFile The original source Zip file.
0N/A */
0N/A public Block generateBlock(PrivateKey privateKey,
0N/A String sigalg,
0N/A X509Certificate[] certChain,
0N/A boolean externalSF, String tsaUrl,
0N/A X509Certificate tsaCert,
0N/A ContentSigner signingMechanism,
0N/A String[] args, ZipFile zipFile)
0N/A throws NoSuchAlgorithmException, InvalidKeyException, IOException,
0N/A SignatureException, CertificateException
0N/A {
3659N/A return new Block(this, privateKey, sigalg, certChain, externalSF,
0N/A tsaUrl, tsaCert, signingMechanism, args, zipFile);
0N/A }
0N/A
0N/A
0N/A public static class Block {
0N/A
0N/A private byte[] block;
0N/A private String blockFileName;
0N/A
0N/A /*
0N/A * Construct a new signature block.
0N/A */
0N/A Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
3659N/A X509Certificate[] certChain, boolean externalSF, String tsaUrl,
0N/A X509Certificate tsaCert, ContentSigner signingMechanism,
0N/A String[] args, ZipFile zipFile)
0N/A throws NoSuchAlgorithmException, InvalidKeyException, IOException,
0N/A SignatureException, CertificateException {
0N/A
0N/A Principal issuerName = certChain[0].getIssuerDN();
0N/A if (!(issuerName instanceof X500Name)) {
0N/A // must extract the original encoded form of DN for subsequent
0N/A // name comparison checks (converting to a String and back to
0N/A // an encoded DN could cause the types of String attribute
0N/A // values to be changed)
0N/A X509CertInfo tbsCert = new
0N/A X509CertInfo(certChain[0].getTBSCertificate());
0N/A issuerName = (Principal)
0N/A tbsCert.get(CertificateIssuerName.NAME + "." +
0N/A CertificateIssuerName.DN_NAME);
0N/A }
0N/A BigInteger serial = certChain[0].getSerialNumber();
0N/A
0N/A String signatureAlgorithm;
0N/A String keyAlgorithm = privateKey.getAlgorithm();
0N/A /*
0N/A * If no signature algorithm was specified, we choose a
0N/A * default that is compatible with the private key algorithm.
0N/A */
0N/A if (sigalg == null) {
0N/A
0N/A if (keyAlgorithm.equalsIgnoreCase("DSA"))
1786N/A signatureAlgorithm = "SHA1withDSA";
0N/A else if (keyAlgorithm.equalsIgnoreCase("RSA"))
1786N/A signatureAlgorithm = "SHA256withRSA";
1786N/A else if (keyAlgorithm.equalsIgnoreCase("EC"))
1786N/A signatureAlgorithm = "SHA256withECDSA";
1786N/A else
0N/A throw new RuntimeException("private key is not a DSA or "
0N/A + "RSA key");
0N/A } else {
0N/A signatureAlgorithm = sigalg;
0N/A }
0N/A
0N/A // check common invalid key/signature algorithm combinations
1786N/A String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
0N/A if ((sigAlgUpperCase.endsWith("WITHRSA") &&
0N/A !keyAlgorithm.equalsIgnoreCase("RSA")) ||
1786N/A (sigAlgUpperCase.endsWith("WITHECDSA") &&
1786N/A !keyAlgorithm.equalsIgnoreCase("EC")) ||
0N/A (sigAlgUpperCase.endsWith("WITHDSA") &&
0N/A !keyAlgorithm.equalsIgnoreCase("DSA"))) {
0N/A throw new SignatureException
0N/A ("private key algorithm is not compatible with signature algorithm");
0N/A }
0N/A
0N/A blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;
0N/A
0N/A AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);
0N/A AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);
0N/A
0N/A Signature sig = Signature.getInstance(signatureAlgorithm);
0N/A sig.initSign(privateKey);
0N/A
0N/A ByteArrayOutputStream baos = new ByteArrayOutputStream();
0N/A sfg.write(baos);
0N/A
0N/A byte[] content = baos.toByteArray();
0N/A
0N/A sig.update(content);
0N/A byte[] signature = sig.sign();
0N/A
0N/A // Timestamp the signature and generate the signature block file
0N/A if (signingMechanism == null) {
0N/A signingMechanism = new TimestampedSigner();
0N/A }
0N/A URI tsaUri = null;
0N/A try {
0N/A if (tsaUrl != null) {
0N/A tsaUri = new URI(tsaUrl);
0N/A }
0N/A } catch (URISyntaxException e) {
0N/A IOException ioe = new IOException();
0N/A ioe.initCause(e);
0N/A throw ioe;
0N/A }
0N/A
0N/A // Assemble parameters for the signing mechanism
0N/A ContentSignerParameters params =
0N/A new JarSignerParameters(args, tsaUri, tsaCert, signature,
3659N/A signatureAlgorithm, certChain, content, zipFile);
0N/A
0N/A // Generate the signature block
0N/A block = signingMechanism.generateSignedData(
0N/A params, externalSF, (tsaUrl != null || tsaCert != null));
0N/A }
0N/A
0N/A /*
0N/A * get block file name.
0N/A */
0N/A public String getMetaName()
0N/A {
0N/A return blockFileName;
0N/A }
0N/A
0N/A /**
0N/A * Writes the block file to the specified OutputStream.
0N/A *
0N/A * @param out the output stream
0N/A * @exception IOException if an I/O error has occurred
0N/A */
0N/A
0N/A public void write(OutputStream out) throws IOException
0N/A {
0N/A out.write(block);
0N/A }
0N/A }
0N/A}
0N/A
0N/A
0N/A/*
0N/A * This object encapsulates the parameters used to perform content signing.
0N/A */
0N/Aclass JarSignerParameters implements ContentSignerParameters {
0N/A
0N/A private String[] args;
0N/A private URI tsa;
0N/A private X509Certificate tsaCertificate;
0N/A private byte[] signature;
0N/A private String signatureAlgorithm;
0N/A private X509Certificate[] signerCertificateChain;
0N/A private byte[] content;
0N/A private ZipFile source;
0N/A
0N/A /**
0N/A * Create a new object.
0N/A */
0N/A JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
0N/A byte[] signature, String signatureAlgorithm,
3659N/A X509Certificate[] signerCertificateChain, byte[] content,
0N/A ZipFile source) {
0N/A
0N/A if (signature == null || signatureAlgorithm == null ||
0N/A signerCertificateChain == null) {
0N/A throw new NullPointerException();
0N/A }
0N/A this.args = args;
0N/A this.tsa = tsa;
0N/A this.tsaCertificate = tsaCertificate;
0N/A this.signature = signature;
0N/A this.signatureAlgorithm = signatureAlgorithm;
0N/A this.signerCertificateChain = signerCertificateChain;
0N/A this.content = content;
0N/A this.source = source;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the command-line arguments.
0N/A *
0N/A * @return The command-line arguments. May be null.
0N/A */
0N/A public String[] getCommandLine() {
0N/A return args;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the identifier for a Timestamping Authority (TSA).
0N/A *
0N/A * @return The TSA identifier. May be null.
0N/A */
0N/A public URI getTimestampingAuthority() {
0N/A return tsa;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the certificate for a Timestamping Authority (TSA).
0N/A *
0N/A * @return The TSA certificate. May be null.
0N/A */
0N/A public X509Certificate getTimestampingAuthorityCertificate() {
0N/A return tsaCertificate;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the signature.
0N/A *
0N/A * @return The non-null signature bytes.
0N/A */
0N/A public byte[] getSignature() {
0N/A return signature;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the name of the signature algorithm.
0N/A *
0N/A * @return The non-null string name of the signature algorithm.
0N/A */
0N/A public String getSignatureAlgorithm() {
0N/A return signatureAlgorithm;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the signer's X.509 certificate chain.
0N/A *
0N/A * @return The non-null array of X.509 public-key certificates.
0N/A */
0N/A public X509Certificate[] getSignerCertificateChain() {
0N/A return signerCertificateChain;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the content that was signed.
0N/A *
0N/A * @return The content bytes. May be null.
0N/A */
0N/A public byte[] getContent() {
0N/A return content;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the original source ZIP file before it was signed.
0N/A *
0N/A * @return The original ZIP file. May be null.
0N/A */
0N/A public ZipFile getSource() {
0N/A return source;
0N/A }
0N/A}