< prev index next >
src/share/classes/sun/security/tools/JarSigner.java
Print this page
rev 1453 : 6561126: keytool should use larger default keysize for keypairs
Reviewed-by: mullan
rev 1455 : 6870812: enhance security tools to use ECC algorithms
Reviewed-by: vinnie, mullan
rev 1456 : 6802846: jarsigner needs enhanced cert validation(options)
Reviewed-by: xuelei
rev 1461 : 6987827: security/util/Resources.java needs improvement
Reviewed-by: valeriep
rev 1465 : 7028490: better suggestion for jarsigner when TSA is not accessible
Reviewed-by: mullan
rev 1466 : 7019937: Translatability bug - Remove Unused String - String ID , read end of file
7019938: Translatability bug - Remove Unused String - String ID can not specify Principal with a
7019940: Translatability bug - Remove unused string - String ID: provided null name
7019942: Translatability bug - String ID: trustedCertEntry,
7019945: Translatability bug - Translatability issue - String ID: * has NOT been verified! In order to veri
7019947: Translatability bug - Translatability issue - String ID: * The integrity of the information stored i
7019949: Translatability bug - Translatability issue - String ID: * you must provide your keystore password.
Reviewed-by: weijun, wetmore
rev 1513 : 7194449: String resources for Key Tool and Policy Tool should be in their respective packages
Reviewed-by: alanb, weijun, mullan
rev 1592 : 8171954: Add stubs for sun.security.tools.KeyTool and sun.security.tools.JarSigner
Summary: Allow sun.security.tools.{keytool,jarsigner}.Main.main to be invoked using their old classes
Reviewed-by: omajid
*** 1,7 ****
/*
! * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2016 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 23,2178 ****
* questions.
*/
package sun.security.tools;
! import java.io.*;
! import java.util.*;
! import java.util.zip.*;
! import java.util.jar.*;
! import java.math.BigInteger;
! import java.net.URI;
! import java.net.URISyntaxException;
! import java.net.URL;
! import java.net.URLClassLoader;
! import java.net.SocketTimeoutException;
! import java.text.Collator;
! import java.text.MessageFormat;
! import java.security.cert.Certificate;
! import java.security.cert.X509Certificate;
! import java.security.cert.CertificateException;
! import java.security.cert.CertificateExpiredException;
! import java.security.cert.CertificateNotYetValidException;
! import java.security.*;
! import java.lang.reflect.Constructor;
!
! import com.sun.jarsigner.ContentSigner;
! import com.sun.jarsigner.ContentSignerParameters;
! import sun.security.x509.*;
! import sun.security.util.*;
! import sun.misc.BASE64Encoder;
/**
! * <p>The jarsigner utility.
! *
! * @author Roland Schemers
! * @author Jan Luehe
! */
!
! public class JarSigner {
!
! // for i18n
! private static final java.util.ResourceBundle rb =
! java.util.ResourceBundle.getBundle
! ("sun.security.tools.JarSignerResources");
! private static final Collator collator = Collator.getInstance();
! static {
! // this is for case insensitive string comparisions
! collator.setStrength(Collator.PRIMARY);
! }
!
! private static final String META_INF = "META-INF/";
!
! private static final Class[] PARAM_STRING = { String.class };
!
! private static final String NONE = "NONE";
! private static final String P11KEYSTORE = "PKCS11";
!
! private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
!
! // Attention:
! // This is the entry that get launched by the security tool jarsigner.
! // It's marked as exported private per AppServer Team's request.
! // See http://ccc.sfbay/6428446
! public static void main(String args[]) throws Exception {
! JarSigner js = new JarSigner();
! js.run(args);
! }
!
! static final String VERSION = "1.0";
!
! static final int IN_KEYSTORE = 0x01;
! static final int IN_SCOPE = 0x02;
!
! // signer's certificate chain (when composing)
! X509Certificate[] certChain;
!
! /*
! * private key
! */
! PrivateKey privateKey;
! KeyStore store;
!
! IdentityScope scope;
!
! String keystore; // key store file
! boolean nullStream = false; // null keystore input stream (NONE)
! boolean token = false; // token-based keystore
! String jarfile; // jar file to sign
! String alias; // alias to sign jar with
! char[] storepass; // keystore password
! boolean protectedPath; // protected authentication path
! String storetype; // keystore type
! String providerName; // provider name
! Vector<String> providers = null; // list of providers
! HashMap<String,String> providerArgs = new HashMap<String, String>(); // arguments for provider constructors
! char[] keypass; // private key password
! String sigfile; // name of .SF file
! String sigalg; // name of signature algorithm
! String digestalg = "SHA1"; // name of digest algorithm
! String signedjar; // output filename
! String tsaUrl; // location of the Timestamping Authority
! String tsaAlias; // alias for the Timestamping Authority's certificate
! boolean verify = false; // verify the jar
! boolean verbose = false; // verbose output when signing/verifying
! boolean showcerts = false; // show certs when verifying
! boolean debug = false; // debug
! boolean signManifest = true; // "sign" the whole manifest
! boolean externalSF = true; // leave the .SF out of the PKCS7 block
!
! // read zip entry raw bytes
! private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
! private byte[] buffer = new byte[8192];
! private ContentSigner signingMechanism = null;
! private String altSignerClass = null;
! private String altSignerClasspath = null;
! private ZipFile zipFile = null;
! private boolean hasExpiredCert = false;
! private boolean hasExpiringCert = false;
! private boolean notYetValidCert = false;
!
! private boolean badKeyUsage = false;
! private boolean badExtendedKeyUsage = false;
! private boolean badNetscapeCertType = false;
!
! public void run(String args[]) {
! try {
! parseArgs(args);
!
! // Try to load and install the specified providers
! if (providers != null) {
! ClassLoader cl = ClassLoader.getSystemClassLoader();
! Enumeration<String> e = providers.elements();
! while (e.hasMoreElements()) {
! String provName = e.nextElement();
! Class<?> provClass;
! if (cl != null) {
! provClass = cl.loadClass(provName);
! } else {
! provClass = Class.forName(provName);
! }
!
! String provArg = providerArgs.get(provName);
! Object obj;
! if (provArg == null) {
! obj = provClass.newInstance();
! } else {
! Constructor<?> c =
! provClass.getConstructor(PARAM_STRING);
! obj = c.newInstance(provArg);
! }
!
! if (!(obj instanceof Provider)) {
! MessageFormat form = new MessageFormat(rb.getString
! ("provName not a provider"));
! Object[] source = {provName};
! throw new Exception(form.format(source));
! }
! Security.addProvider((Provider)obj);
! }
! }
!
! hasExpiredCert = false;
! hasExpiringCert = false;
! notYetValidCert = false;
!
! badKeyUsage = false;
! badExtendedKeyUsage = false;
! badNetscapeCertType = false;
!
! if (verify) {
! try {
! loadKeyStore(keystore, false);
! scope = IdentityScope.getSystemScope();
! } catch (Exception e) {
! if ((keystore != null) || (storepass != null)) {
! System.out.println(rb.getString("jarsigner error: ") +
! e.getMessage());
! System.exit(1);
! }
! }
! /* if (debug) {
! SignatureFileVerifier.setDebug(true);
! ManifestEntryVerifier.setDebug(true);
! }
*/
! verifyJar(jarfile);
! } else {
! loadKeyStore(keystore, true);
! getAliasInfo(alias);
!
! // load the alternative signing mechanism
! if (altSignerClass != null) {
! signingMechanism = loadSigningMechanism(altSignerClass,
! altSignerClasspath);
! }
! signJar(jarfile, alias, args);
! }
! } catch (Exception e) {
! System.out.println(rb.getString("jarsigner error: ") + e);
! if (debug) {
! e.printStackTrace();
! }
! System.exit(1);
! } finally {
! // zero-out private key password
! if (keypass != null) {
! Arrays.fill(keypass, ' ');
! keypass = null;
! }
! // zero-out keystore password
! if (storepass != null) {
! Arrays.fill(storepass, ' ');
! storepass = null;
! }
! }
! }
!
! /*
! * Parse command line arguments.
! */
! void parseArgs(String args[]) {
! /* parse flags */
! int n = 0;
!
! for (n=0; (n < args.length) && args[n].startsWith("-"); n++) {
!
! String flags = args[n];
!
! if (collator.compare(flags, "-keystore") == 0) {
! if (++n == args.length) usage();
! keystore = args[n];
! } else if (collator.compare(flags, "-storepass") ==0) {
! if (++n == args.length) usage();
! storepass = args[n].toCharArray();
! } else if (collator.compare(flags, "-storetype") ==0) {
! if (++n == args.length) usage();
! storetype = args[n];
! } else if (collator.compare(flags, "-providerName") ==0) {
! if (++n == args.length) usage();
! providerName = args[n];
! } else if ((collator.compare(flags, "-provider") == 0) ||
! (collator.compare(flags, "-providerClass") == 0)) {
! if (++n == args.length) usage();
! if (providers == null) {
! providers = new Vector<String>(3);
! }
! providers.add(args[n]);
!
! if (args.length > (n+1)) {
! flags = args[n+1];
! if (collator.compare(flags, "-providerArg") == 0) {
! if (args.length == (n+2)) usage();
! providerArgs.put(args[n], args[n+2]);
! n += 2;
! }
! }
! } else if (collator.compare(flags, "-protected") ==0) {
! protectedPath = true;
! } else if (collator.compare(flags, "-debug") ==0) {
! debug = true;
! } else if (collator.compare(flags, "-keypass") ==0) {
! if (++n == args.length) usage();
! keypass = args[n].toCharArray();
! } else if (collator.compare(flags, "-sigfile") ==0) {
! if (++n == args.length) usage();
! sigfile = args[n];
! } else if (collator.compare(flags, "-signedjar") ==0) {
! if (++n == args.length) usage();
! signedjar = args[n];
! } else if (collator.compare(flags, "-tsa") ==0) {
! if (++n == args.length) usage();
! tsaUrl = args[n];
! } else if (collator.compare(flags, "-tsacert") ==0) {
! if (++n == args.length) usage();
! tsaAlias = args[n];
! } else if (collator.compare(flags, "-altsigner") ==0) {
! if (++n == args.length) usage();
! altSignerClass = args[n];
! } else if (collator.compare(flags, "-altsignerpath") ==0) {
! if (++n == args.length) usage();
! altSignerClasspath = args[n];
! } else if (collator.compare(flags, "-sectionsonly") ==0) {
! signManifest = false;
! } else if (collator.compare(flags, "-internalsf") ==0) {
! externalSF = false;
! } else if (collator.compare(flags, "-verify") ==0) {
! verify = true;
! } else if (collator.compare(flags, "-verbose") ==0) {
! verbose = true;
! } else if (collator.compare(flags, "-sigalg") ==0) {
! if (++n == args.length) usage();
! sigalg = args[n];
! } else if (collator.compare(flags, "-digestalg") ==0) {
! if (++n == args.length) usage();
! digestalg = args[n];
! } else if (collator.compare(flags, "-certs") ==0) {
! showcerts = true;
! } else if (collator.compare(flags, "-h") == 0 ||
! collator.compare(flags, "-help") == 0) {
! usage();
! } else {
! System.err.println(rb.getString("Illegal option: ") + flags);
! usage();
! }
! }
!
! if (n == args.length) usage();
! jarfile = args[n++];
!
! if (!verify) {
! if (n == args.length) usage();
! alias = args[n++];
! }
!
! if (storetype == null) {
! storetype = KeyStore.getDefaultType();
! }
! storetype = KeyStoreUtil.niceStoreTypeName(storetype);
!
! if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
! KeyStoreUtil.isWindowsKeyStore(storetype)) {
! token = true;
! if (keystore == null) {
! keystore = NONE;
! }
! }
!
! if (NONE.equals(keystore)) {
! nullStream = true;
! }
!
! if (token && !nullStream) {
! System.err.println(MessageFormat.format(rb.getString
! ("-keystore must be NONE if -storetype is {0}"), storetype));
! System.err.println();
! usage();
! }
!
! if (token && keypass != null) {
! System.err.println(MessageFormat.format(rb.getString
! ("-keypass can not be specified " +
! "if -storetype is {0}"), storetype));
! System.err.println();
! usage();
! }
!
! if (protectedPath) {
! if (storepass != null || keypass != null) {
! System.err.println(rb.getString
! ("If -protected is specified, " +
! "then -storepass and -keypass must not be specified"));
! System.err.println();
! usage();
! }
! }
! if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
! if (storepass != null || keypass != null) {
! System.err.println(rb.getString
! ("If keystore is not password protected, " +
! "then -storepass and -keypass must not be specified"));
! System.err.println();
! usage();
! }
! }
! }
!
! void usage() {
! System.out.println(rb.getString
! ("Usage: jarsigner [options] jar-file alias"));
! System.out.println(rb.getString
! (" jarsigner -verify [options] jar-file"));
! System.out.println();
! System.out.println(rb.getString
! ("[-keystore <url>] keystore location"));
! System.out.println();
! System.out.println(rb.getString
! ("[-storepass <password>] password for keystore integrity"));
! System.out.println();
! System.out.println(rb.getString
! ("[-storetype <type>] keystore type"));
! System.out.println();
! System.out.println(rb.getString
! ("[-keypass <password>] password for private key (if different)"));
! System.out.println();
! System.out.println(rb.getString
! ("[-sigfile <file>] name of .SF/.DSA file"));
! System.out.println();
! System.out.println(rb.getString
! ("[-signedjar <file>] name of signed JAR file"));
! System.out.println();
! System.out.println(rb.getString
! ("[-digestalg <algorithm>] name of digest algorithm"));
! System.out.println();
! System.out.println(rb.getString
! ("[-sigalg <algorithm>] name of signature algorithm"));
! System.out.println();
! System.out.println(rb.getString
! ("[-verify] verify a signed JAR file"));
! System.out.println();
! System.out.println(rb.getString
! ("[-verbose] verbose output when signing/verifying"));
! System.out.println();
! System.out.println(rb.getString
! ("[-certs] display certificates when verbose and verifying"));
! System.out.println();
! System.out.println(rb.getString
! ("[-tsa <url>] location of the Timestamping Authority"));
! System.out.println();
! System.out.println(rb.getString
! ("[-tsacert <alias>] public key certificate for Timestamping Authority"));
! System.out.println();
! System.out.println(rb.getString
! ("[-altsigner <class>] class name of an alternative signing mechanism"));
! System.out.println();
! System.out.println(rb.getString
! ("[-altsignerpath <pathlist>] location of an alternative signing mechanism"));
! System.out.println();
! System.out.println(rb.getString
! ("[-internalsf] include the .SF file inside the signature block"));
! System.out.println();
! System.out.println(rb.getString
! ("[-sectionsonly] don't compute hash of entire manifest"));
! System.out.println();
! System.out.println(rb.getString
! ("[-protected] keystore has protected authentication path"));
! System.out.println();
! System.out.println(rb.getString
! ("[-providerName <name>] provider name"));
! System.out.println();
! System.out.println(rb.getString
! ("[-providerClass <class> name of cryptographic service provider's"));
! System.out.println(rb.getString
! (" [-providerArg <arg>]] ... master class file and constructor argument"));
! System.out.println();
!
! System.exit(1);
! }
!
! void verifyJar(String jarName)
! throws Exception
! {
! boolean anySigned = false;
! boolean hasUnsignedEntry = false;
! JarFile jf = null;
!
! try {
! jf = new JarFile(jarName, true);
! Vector<JarEntry> entriesVec = new Vector<JarEntry>();
! byte[] buffer = new byte[8192];
!
! Enumeration<JarEntry> entries = jf.entries();
! while (entries.hasMoreElements()) {
! JarEntry je = entries.nextElement();
! entriesVec.addElement(je);
! InputStream is = null;
! try {
! is = jf.getInputStream(je);
! int n;
! while ((n = is.read(buffer, 0, buffer.length)) != -1) {
! // we just read. this will throw a SecurityException
! // if a signature/digest check fails.
! }
! } finally {
! if (is != null) {
! is.close();
! }
! }
! }
!
! Manifest man = jf.getManifest();
!
! if (man != null) {
! if (verbose) System.out.println();
! Enumeration<JarEntry> e = entriesVec.elements();
!
! long now = System.currentTimeMillis();
!
! while (e.hasMoreElements()) {
! JarEntry je = e.nextElement();
! String name = je.getName();
! CodeSigner[] signers = je.getCodeSigners();
! boolean isSigned = (signers != null);
! anySigned |= isSigned;
! hasUnsignedEntry |= !je.isDirectory() && !isSigned
! && !signatureRelated(name);
!
! if (verbose) {
! int inStoreOrScope = inKeyStore(signers);
! boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
! boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
! boolean inManifest =
! ((man.getAttributes(name) != null) ||
! (man.getAttributes("./"+name) != null) ||
! (man.getAttributes("/"+name) != null));
! System.out.print(
! (isSigned ? rb.getString("s") : rb.getString(" ")) +
! (inManifest ? rb.getString("m") : rb.getString(" ")) +
! (inStore ? rb.getString("k") : rb.getString(" ")) +
! (inScope ? rb.getString("i") : rb.getString(" ")) +
! rb.getString(" "));
! StringBuffer sb = new StringBuffer();
! String s = Long.toString(je.getSize());
! for (int i = 6 - s.length(); i > 0; --i) {
! sb.append(' ');
! }
! sb.append(s).append(' ').
! append(new Date(je.getTime()).toString());
! sb.append(' ').append(je.getName());
! System.out.println(sb.toString());
!
! if (signers != null && showcerts) {
! String tab = rb.getString(" ");
! for (int i = 0; i < signers.length; i++) {
! System.out.println();
! List<? extends Certificate> certs =
! signers[i].getSignerCertPath()
! .getCertificates();
! // display the signature timestamp, if present
! Timestamp timestamp = signers[i].getTimestamp();
! if (timestamp != null) {
! System.out.println(
! printTimestamp(tab, timestamp));
! }
! // display the certificate(s)
! for (Certificate c : certs) {
! System.out.println(
! printCert(tab, c, true, now));
! }
! }
! System.out.println();
! }
!
! }
! if (isSigned) {
! for (int i = 0; i < signers.length; i++) {
! Certificate cert =
! signers[i].getSignerCertPath()
! .getCertificates().get(0);
! if (cert instanceof X509Certificate) {
! checkCertUsage((X509Certificate)cert, null);
! if (!showcerts) {
! long notAfter = ((X509Certificate)cert)
! .getNotAfter().getTime();
!
! if (notAfter < now) {
! hasExpiredCert = true;
! } else if (notAfter < now + SIX_MONTHS) {
! hasExpiringCert = true;
! }
! }
! }
! }
! }
!
! }
! }
! if (verbose) {
! System.out.println();
! System.out.println(rb.getString(
! " s = signature was verified "));
! System.out.println(rb.getString(
! " m = entry is listed in manifest"));
! System.out.println(rb.getString(
! " k = at least one certificate was found in keystore"));
! System.out.println(rb.getString(
! " i = at least one certificate was found in identity scope"));
! System.out.println();
! }
!
! if (man == null)
! System.out.println(rb.getString("no manifest."));
!
! if (!anySigned) {
! System.out.println(rb.getString(
! "jar is unsigned. (signatures missing or not parsable)"));
! } else {
! System.out.println(rb.getString("jar verified."));
! if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
! badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
! notYetValidCert) {
!
! System.out.println();
! System.out.println(rb.getString("Warning: "));
! if (badKeyUsage) {
! System.out.println(
! rb.getString("This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."));
! }
!
! if (badExtendedKeyUsage) {
! System.out.println(
! rb.getString("This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."));
! }
!
! if (badNetscapeCertType) {
! System.out.println(
! rb.getString("This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."));
! }
!
! if (hasUnsignedEntry) {
! System.out.println(rb.getString(
! "This jar contains unsigned entries which have not been integrity-checked. "));
! }
! if (hasExpiredCert) {
! System.out.println(rb.getString(
! "This jar contains entries whose signer certificate has expired. "));
! }
! if (hasExpiringCert) {
! System.out.println(rb.getString(
! "This jar contains entries whose signer certificate will expire within six months. "));
! }
! if (notYetValidCert) {
! System.out.println(rb.getString(
! "This jar contains entries whose signer certificate is not yet valid. "));
! }
!
! if (! (verbose && showcerts)) {
! System.out.println();
! System.out.println(rb.getString(
! "Re-run with the -verbose and -certs options for more details."));
! }
! }
! }
! System.exit(0);
! } catch (Exception e) {
! System.out.println(rb.getString("jarsigner: ") + e);
! if (debug) {
! e.printStackTrace();
! }
! } finally { // close the resource
! if (jf != null) {
! jf.close();
! }
! }
!
! System.exit(1);
! }
!
! /*
! * Display some details about a certificate:
! *
! * <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
! */
! String printCert(Certificate c) {
! return printCert("", c, false, 0);
! }
!
! private static MessageFormat validityTimeForm = null;
! private static MessageFormat notYetTimeForm = null;
! private static MessageFormat expiredTimeForm = null;
! private static MessageFormat expiringTimeForm = null;
!
! /*
! * Display some details about a certificate:
! *
! * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
! * [<validity-period> | <expiry-warning>]
! */
! String printCert(String tab, Certificate c, boolean checkValidityPeriod,
! long now) {
!
! StringBuilder certStr = new StringBuilder();
! String space = rb.getString(" ");
! X509Certificate x509Cert = null;
!
! if (c instanceof X509Certificate) {
! x509Cert = (X509Certificate) c;
! certStr.append(tab).append(x509Cert.getType())
! .append(rb.getString(", "))
! .append(x509Cert.getSubjectDN().getName());
! } else {
! certStr.append(tab).append(c.getType());
! }
! String alias = storeHash.get(c);
! if (alias != null) {
! certStr.append(space).append(alias);
! }
!
! if (checkValidityPeriod && x509Cert != null) {
!
! certStr.append("\n").append(tab).append("[");
! Date notAfter = x509Cert.getNotAfter();
! try {
! x509Cert.checkValidity();
! // test if cert will expire within six months
! if (now == 0) {
! now = System.currentTimeMillis();
! }
! if (notAfter.getTime() < now + SIX_MONTHS) {
! hasExpiringCert = true;
!
! if (expiringTimeForm == null) {
! expiringTimeForm = new MessageFormat(
! rb.getString("certificate will expire on"));
! }
! Object[] source = { notAfter };
! certStr.append(expiringTimeForm.format(source));
!
! } else {
! if (validityTimeForm == null) {
! validityTimeForm = new MessageFormat(
! rb.getString("certificate is valid from"));
! }
! Object[] source = { x509Cert.getNotBefore(), notAfter };
! certStr.append(validityTimeForm.format(source));
! }
! } catch (CertificateExpiredException cee) {
! hasExpiredCert = true;
!
! if (expiredTimeForm == null) {
! expiredTimeForm = new MessageFormat(
! rb.getString("certificate expired on"));
! }
! Object[] source = { notAfter };
! certStr.append(expiredTimeForm.format(source));
!
! } catch (CertificateNotYetValidException cnyve) {
! notYetValidCert = true;
!
! if (notYetTimeForm == null) {
! notYetTimeForm = new MessageFormat(
! rb.getString("certificate is not valid until"));
! }
! Object[] source = { x509Cert.getNotBefore() };
! certStr.append(notYetTimeForm.format(source));
! }
! certStr.append("]");
!
! boolean[] bad = new boolean[3];
! checkCertUsage(x509Cert, bad);
! if (bad[0] || bad[1] || bad[2]) {
! String x = "";
! if (bad[0]) {
! x ="KeyUsage";
! }
! if (bad[1]) {
! if (x.length() > 0) x = x + ", ";
! x = x + "ExtendedKeyUsage";
! }
! if (bad[2]) {
! if (x.length() > 0) x = x + ", ";
! x = x + "NetscapeCertType";
! }
! certStr.append("\n").append(tab)
! .append(MessageFormat.format(rb.getString(
! "[{0} extension does not support code signing]"), x));
! }
! }
! return certStr.toString();
! }
!
! private static MessageFormat signTimeForm = null;
!
! private String printTimestamp(String tab, Timestamp timestamp) {
!
! if (signTimeForm == null) {
! signTimeForm =
! new MessageFormat(rb.getString("entry was signed on"));
! }
! Object[] source = { timestamp.getTimestamp() };
!
! return new StringBuilder().append(tab).append("[")
! .append(signTimeForm.format(source)).append("]").toString();
! }
!
! Hashtable<Certificate, String> storeHash =
! new Hashtable<Certificate, String>();
!
! int inKeyStore(CodeSigner[] signers) {
! int result = 0;
!
! if (signers == null)
! return 0;
!
! boolean found = false;
!
! for (int i = 0; i < signers.length; i++) {
! found = false;
! List<? extends Certificate> certs =
! signers[i].getSignerCertPath().getCertificates();
!
! for (Certificate c : certs) {
! String alias = storeHash.get(c);
!
! if (alias != null) {
! if (alias.startsWith("("))
! result |= IN_KEYSTORE;
! else if (alias.startsWith("["))
! result |= IN_SCOPE;
! } else {
! if (store != null) {
! try {
! alias = store.getCertificateAlias(c);
! } catch (KeyStoreException kse) {
! // never happens, because keystore has been loaded
! }
! if (alias != null) {
! storeHash.put(c, "("+alias+")");
! found = true;
! result |= IN_KEYSTORE;
! }
! }
! if (!found && (scope != null)) {
! Identity id = scope.getIdentity(c.getPublicKey());
! if (id != null) {
! result |= IN_SCOPE;
! storeHash.put(c, "["+id.getName()+"]");
! }
! }
! }
! }
! }
! return result;
! }
!
! void signJar(String jarName, String alias, String[] args)
! throws Exception {
! boolean aliasUsed = false;
! X509Certificate tsaCert = null;
!
! if (sigfile == null) {
! sigfile = alias;
! aliasUsed = true;
! }
!
! if (sigfile.length() > 8) {
! sigfile = sigfile.substring(0, 8).toUpperCase();
! } else {
! sigfile = sigfile.toUpperCase();
! }
!
! StringBuilder tmpSigFile = new StringBuilder(sigfile.length());
! for (int j = 0; j < sigfile.length(); j++) {
! char c = sigfile.charAt(j);
! if (!
! ((c>= 'A' && c<= 'Z') ||
! (c>= '0' && c<= '9') ||
! (c == '-') ||
! (c == '_'))) {
! if (aliasUsed) {
! // convert illegal characters from the alias to be _'s
! c = '_';
! } else {
! throw new
! RuntimeException(rb.getString
! ("signature filename must consist of the following characters: A-Z, 0-9, _ or -"));
! }
! }
! tmpSigFile.append(c);
! }
!
! sigfile = tmpSigFile.toString();
!
! String tmpJarName;
! if (signedjar == null) tmpJarName = jarName+".sig";
! else tmpJarName = signedjar;
!
! File jarFile = new File(jarName);
! File signedJarFile = new File(tmpJarName);
!
! // Open the jar (zip) file
! try {
! zipFile = new ZipFile(jarName);
! } catch (IOException ioe) {
! error(rb.getString("unable to open jar file: ")+jarName, ioe);
! }
!
! FileOutputStream fos = null;
! try {
! fos = new FileOutputStream(signedJarFile);
! } catch (IOException ioe) {
! error(rb.getString("unable to create: ")+tmpJarName, ioe);
! }
!
! PrintStream ps = new PrintStream(fos);
! ZipOutputStream zos = new ZipOutputStream(ps);
!
! /* First guess at what they might be - we don't xclude RSA ones. */
! String sfFilename = (META_INF + sigfile + ".SF").toUpperCase();
! String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase();
!
! Manifest manifest = new Manifest();
! Map<String,Attributes> mfEntries = manifest.getEntries();
!
! // The Attributes of manifest before updating
! Attributes oldAttr = null;
!
! boolean mfModified = false;
! boolean mfCreated = false;
! byte[] mfRawBytes = null;
!
! try {
! MessageDigest digests[] = { MessageDigest.getInstance(digestalg) };
!
! // Check if manifest exists
! ZipEntry mfFile;
! if ((mfFile = getManifestFile(zipFile)) != null) {
! // Manifest exists. Read its raw bytes.
! mfRawBytes = getBytes(zipFile, mfFile);
! manifest.read(new ByteArrayInputStream(mfRawBytes));
! oldAttr = (Attributes)(manifest.getMainAttributes().clone());
! } else {
! // Create new manifest
! Attributes mattr = manifest.getMainAttributes();
! mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
! "1.0");
! String javaVendor = System.getProperty("java.vendor");
! String jdkVersion = System.getProperty("java.version");
! mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
! + ")");
! mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
! mfCreated = true;
! }
!
! /*
! * For each entry in jar
! * (except for signature-related META-INF entries),
! * do the following:
! *
! * - if entry is not contained in manifest, add it to manifest;
! * - if entry is contained in manifest, calculate its hash and
! * compare it with the one in the manifest; if they are
! * different, replace the hash in the manifest with the newly
! * generated one. (This may invalidate existing signatures!)
! */
! BASE64Encoder encoder = new JarBASE64Encoder();
! Vector<ZipEntry> mfFiles = new Vector<ZipEntry>();
!
! boolean wasSigned = false;
!
! for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
! enum_.hasMoreElements();) {
! ZipEntry ze = enum_.nextElement();
!
! if (ze.getName().startsWith(META_INF)) {
! // Store META-INF files in vector, so they can be written
! // out first
! mfFiles.addElement(ze);
!
! if (SignatureFileVerifier.isBlockOrSF(
! ze.getName().toUpperCase(Locale.ENGLISH))) {
! wasSigned = true;
! }
!
! if (signatureRelated(ze.getName())) {
! // ignore signature-related and manifest files
! continue;
! }
! }
!
! if (manifest.getAttributes(ze.getName()) != null) {
! // jar entry is contained in manifest, check and
! // possibly update its digest attributes
! if (updateDigests(ze, zipFile, digests, encoder,
! manifest) == true) {
! mfModified = true;
! }
! } else if (!ze.isDirectory()) {
! // Add entry to manifest
! Attributes attrs = getDigestAttributes(ze, zipFile,
! digests,
! encoder);
! mfEntries.put(ze.getName(), attrs);
! mfModified = true;
! }
! }
!
! // Recalculate the manifest raw bytes if necessary
! if (mfModified) {
! ByteArrayOutputStream baos = new ByteArrayOutputStream();
! manifest.write(baos);
! if (wasSigned) {
! byte[] newBytes = baos.toByteArray();
! if (mfRawBytes != null
! && oldAttr.equals(manifest.getMainAttributes())) {
!
! /*
! * Note:
! *
! * The Attributes object is based on HashMap and can handle
! * continuation columns. Therefore, even if the contents are
! * not changed (in a Map view), the bytes that it write()
! * may be different from the original bytes that it read()
! * from. Since the signature on the main attributes is based
! * on raw bytes, we must retain the exact bytes.
! */
!
! int newPos = findHeaderEnd(newBytes);
! int oldPos = findHeaderEnd(mfRawBytes);
!
! if (newPos == oldPos) {
! System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
! } else {
! // cat oldHead newTail > newBytes
! byte[] lastBytes = new byte[oldPos +
! newBytes.length - newPos];
! System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
! System.arraycopy(newBytes, newPos, lastBytes, oldPos,
! newBytes.length - newPos);
! newBytes = lastBytes;
! }
! }
! mfRawBytes = newBytes;
! } else {
! mfRawBytes = baos.toByteArray();
! }
! }
!
! // Write out the manifest
! if (mfModified) {
! // manifest file has new length
! mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
! }
! if (verbose) {
! if (mfCreated) {
! System.out.println(rb.getString(" adding: ") +
! mfFile.getName());
! } else if (mfModified) {
! System.out.println(rb.getString(" updating: ") +
! mfFile.getName());
! }
! }
! zos.putNextEntry(mfFile);
! zos.write(mfRawBytes);
!
! // Calculate SignatureFile (".SF") and SignatureBlockFile
! ManifestDigester manDig = new ManifestDigester(mfRawBytes);
! SignatureFile sf = new SignatureFile(digests, manifest, manDig,
! sigfile, signManifest);
!
! if (tsaAlias != null) {
! tsaCert = getTsaCert(tsaAlias);
! }
!
! SignatureFile.Block block = null;
!
! try {
! block =
! sf.generateBlock(privateKey, sigalg, certChain,
! externalSF, tsaUrl, tsaCert, signingMechanism, args,
! zipFile);
! } catch (SocketTimeoutException e) {
! // Provide a helpful message when TSA is beyond a firewall
! error(rb.getString("unable to sign jar: ") +
! rb.getString("no response from the Timestamping Authority. ") +
! rb.getString("When connecting from behind a firewall then an HTTP proxy may need to be specified. ") +
! rb.getString("Supply the following options to jarsigner: ") +
! "\n -J-Dhttp.proxyHost=<hostname> " +
! "\n -J-Dhttp.proxyPort=<portnumber> ", e);
! }
!
! sfFilename = sf.getMetaName();
! bkFilename = block.getMetaName();
!
! ZipEntry sfFile = new ZipEntry(sfFilename);
! ZipEntry bkFile = new ZipEntry(bkFilename);
!
! long time = System.currentTimeMillis();
! sfFile.setTime(time);
! bkFile.setTime(time);
!
! // signature file
! zos.putNextEntry(sfFile);
! sf.write(zos);
! if (verbose) {
! if (zipFile.getEntry(sfFilename) != null) {
! System.out.println(rb.getString(" updating: ") +
! sfFilename);
! } else {
! System.out.println(rb.getString(" adding: ") +
! sfFilename);
! }
! }
!
! if (verbose) {
! if (tsaUrl != null || tsaCert != null) {
! System.out.println(
! rb.getString("requesting a signature timestamp"));
! }
! if (tsaUrl != null) {
! System.out.println(rb.getString("TSA location: ") + tsaUrl);
! }
! if (tsaCert != null) {
! String certUrl =
! TimestampedSigner.getTimestampingUrl(tsaCert);
! if (certUrl != null) {
! System.out.println(rb.getString("TSA location: ") +
! certUrl);
! }
! System.out.println(
! rb.getString("TSA certificate: ") + printCert(tsaCert));
! }
! if (signingMechanism != null) {
! System.out.println(
! rb.getString("using an alternative signing mechanism"));
! }
! }
!
! // signature block file
! zos.putNextEntry(bkFile);
! block.write(zos);
! if (verbose) {
! if (zipFile.getEntry(bkFilename) != null) {
! System.out.println(rb.getString(" updating: ") +
! bkFilename);
! } else {
! System.out.println(rb.getString(" adding: ") +
! bkFilename);
! }
! }
!
! // Write out all other META-INF files that we stored in the
! // vector
! for (int i=0; i<mfFiles.size(); i++) {
! ZipEntry ze = mfFiles.elementAt(i);
! if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
! && !ze.getName().equalsIgnoreCase(sfFilename)
! && !ze.getName().equalsIgnoreCase(bkFilename)) {
! writeEntry(zipFile, zos, ze);
! }
! }
!
! // Write out all other files
! for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
! enum_.hasMoreElements();) {
! ZipEntry ze = enum_.nextElement();
!
! if (!ze.getName().startsWith(META_INF)) {
! if (verbose) {
! if (manifest.getAttributes(ze.getName()) != null)
! System.out.println(rb.getString(" signing: ") +
! ze.getName());
! else
! System.out.println(rb.getString(" adding: ") +
! ze.getName());
! }
! writeEntry(zipFile, zos, ze);
! }
! }
! } catch(IOException ioe) {
! error(rb.getString("unable to sign jar: ")+ioe, ioe);
! } finally {
! // close the resouces
! if (zipFile != null) {
! zipFile.close();
! zipFile = null;
! }
!
! if (zos != null) {
! zos.close();
! }
! }
!
! // no IOException thrown in the follow try clause, so disable
! // the try clause.
! // try {
! if (signedjar == null) {
! // attempt an atomic rename. If that fails,
! // rename the original jar file, then the signed
! // one, then delete the original.
! if (!signedJarFile.renameTo(jarFile)) {
! File origJar = new File(jarName+".orig");
!
! if (jarFile.renameTo(origJar)) {
! if (signedJarFile.renameTo(jarFile)) {
! origJar.delete();
! } else {
! MessageFormat form = new MessageFormat(rb.getString
! ("attempt to rename signedJarFile to jarFile failed"));
! Object[] source = {signedJarFile, jarFile};
! error(form.format(source));
! }
! } else {
! MessageFormat form = new MessageFormat(rb.getString
! ("attempt to rename jarFile to origJar failed"));
! Object[] source = {jarFile, origJar};
! error(form.format(source));
! }
! }
! }
!
! if (hasExpiredCert || hasExpiringCert || notYetValidCert
! || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
! System.out.println();
!
! System.out.println(rb.getString("Warning: "));
! if (badKeyUsage) {
! System.out.println(
! rb.getString("The signer certificate's KeyUsage extension doesn't allow code signing."));
! }
! if (badExtendedKeyUsage) {
! System.out.println(
! rb.getString("The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."));
! }
!
! if (badNetscapeCertType) {
! System.out.println(
! rb.getString("The signer certificate's NetscapeCertType extension doesn't allow code signing."));
! }
!
! if (hasExpiredCert) {
! System.out.println(
! rb.getString("The signer certificate has expired."));
! } else if (hasExpiringCert) {
! System.out.println(
! rb.getString("The signer certificate will expire within six months."));
! } else if (notYetValidCert) {
! System.out.println(
! rb.getString("The signer certificate is not yet valid."));
! }
! }
!
! // no IOException thrown in the above try clause, so disable
! // the catch clause.
! // } catch(IOException ioe) {
! // error(rb.getString("unable to sign jar: ")+ioe, ioe);
! // }
! }
!
! /**
! * Find the length of header inside bs. The header is a multiple (>=0)
! * lines of attributes plus an empty line. The empty line is included
! * in the header.
! */
! @SuppressWarnings("fallthrough")
! private int findHeaderEnd(byte[] bs) {
! // Initial state true to deal with empty header
! boolean newline = true; // just met a newline
! int len = bs.length;
! for (int i=0; i<len; i++) {
! switch (bs[i]) {
! case '\r':
! if (i < len - 1 && bs[i+1] == '\n') i++;
! // fallthrough
! case '\n':
! if (newline) return i+1; //+1 to get length
! newline = true;
! break;
! default:
! newline = false;
! }
! }
! // If header end is not found, it means the MANIFEST.MF has only
! // the main attributes section and it does not end with 2 newlines.
! // Returns the whole length so that it can be completely replaced.
! return len;
! }
!
! /**
! * signature-related files include:
! * . META-INF/MANIFEST.MF
! * . META-INF/SIG-*
! * . META-INF/*.SF
! * . META-INF/*.DSA
! * . META-INF/*.RSA
! */
! private boolean signatureRelated(String name) {
! return SignatureFileVerifier.isSigningRelated(name);
! }
!
! private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
! throws IOException
! {
! ZipEntry ze2 = new ZipEntry(ze.getName());
! ze2.setMethod(ze.getMethod());
! ze2.setTime(ze.getTime());
! ze2.setComment(ze.getComment());
! ze2.setExtra(ze.getExtra());
! if (ze.getMethod() == ZipEntry.STORED) {
! ze2.setSize(ze.getSize());
! ze2.setCrc(ze.getCrc());
! }
! os.putNextEntry(ze2);
! writeBytes(zf, ze, os);
! }
!
! /**
! * Writes all the bytes for a given entry to the specified output stream.
! */
! private synchronized void writeBytes
! (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
! int n;
!
! InputStream is = null;
! try {
! is = zf.getInputStream(ze);
! long left = ze.getSize();
!
! while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
! os.write(buffer, 0, n);
! left -= n;
! }
! } finally {
! if (is != null) {
! is.close();
! }
! }
! }
!
! void loadKeyStore(String keyStoreName, boolean prompt) {
!
! if (!nullStream && keyStoreName == null) {
! keyStoreName = System.getProperty("user.home") + File.separator
! + ".keystore";
! }
!
! try {
! if (providerName == null) {
! store = KeyStore.getInstance(storetype);
! } else {
! store = KeyStore.getInstance(storetype, providerName);
! }
!
! // Get pass phrase
! // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z
! // and on NT call ??
! if (token && storepass == null && !protectedPath
! && !KeyStoreUtil.isWindowsKeyStore(storetype)) {
! storepass = getPass
! (rb.getString("Enter Passphrase for keystore: "));
! } else if (!token && storepass == null && prompt) {
! storepass = getPass
! (rb.getString("Enter Passphrase for keystore: "));
! }
!
! if (nullStream) {
! store.load(null, storepass);
! } else {
! keyStoreName = keyStoreName.replace(File.separatorChar, '/');
! URL url = null;
! try {
! url = new URL(keyStoreName);
! } catch (java.net.MalformedURLException e) {
! // try as file
! url = new File(keyStoreName).toURI().toURL();
! }
! InputStream is = null;
! try {
! is = url.openStream();
! store.load(is, storepass);
! } finally {
! if (is != null) {
! is.close();
! }
! }
! }
! } catch (IOException ioe) {
! throw new RuntimeException(rb.getString("keystore load: ") +
! ioe.getMessage());
! } catch (java.security.cert.CertificateException ce) {
! throw new RuntimeException(rb.getString("certificate exception: ") +
! ce.getMessage());
! } catch (NoSuchProviderException pe) {
! throw new RuntimeException(rb.getString("keystore load: ") +
! pe.getMessage());
! } catch (NoSuchAlgorithmException nsae) {
! throw new RuntimeException(rb.getString("keystore load: ") +
! nsae.getMessage());
! } catch (KeyStoreException kse) {
! throw new RuntimeException
! (rb.getString("unable to instantiate keystore class: ") +
! kse.getMessage());
! }
! }
!
! X509Certificate getTsaCert(String alias) {
!
! java.security.cert.Certificate cs = null;
!
! try {
! cs = store.getCertificate(alias);
! } catch (KeyStoreException kse) {
! // this never happens, because keystore has been loaded
! }
! if (cs == null || (!(cs instanceof X509Certificate))) {
! MessageFormat form = new MessageFormat(rb.getString
! ("Certificate not found for: alias. alias must reference a valid KeyStore entry containing an X.509 public key certificate for the Timestamping Authority."));
! Object[] source = {alias, alias};
! error(form.format(source));
! }
! return (X509Certificate) cs;
! }
!
! /**
! * Check if userCert is designed to be a code signer
! * @param userCert the certificate to be examined
! * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage,
! * NetscapeCertType has codeSigning flag turned on.
! * If null, the class field badKeyUsage, badExtendedKeyUsage,
! * badNetscapeCertType will be set.
! */
! void checkCertUsage(X509Certificate userCert, boolean[] bad) {
!
! // Can act as a signer?
! // 1. if KeyUsage, then [0] should be true
! // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
! // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
! // 1,2,3 must be true
!
! if (bad != null) {
! bad[0] = bad[1] = bad[2] = false;
! }
!
! boolean[] keyUsage = userCert.getKeyUsage();
! if (keyUsage != null) {
! if (keyUsage.length < 1 || !keyUsage[0]) {
! if (bad != null) {
! bad[0] = true;
! } else {
! badKeyUsage = true;
! }
! }
! }
!
! try {
! List<String> xKeyUsage = userCert.getExtendedKeyUsage();
! if (xKeyUsage != null) {
! if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage
! && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
! if (bad != null) {
! bad[1] = true;
! } else {
! badExtendedKeyUsage = true;
! }
! }
! }
! } catch (java.security.cert.CertificateParsingException e) {
! // shouldn't happen
! }
!
! try {
! // OID_NETSCAPE_CERT_TYPE
! byte[] netscapeEx = userCert.getExtensionValue
! ("2.16.840.1.113730.1.1");
! if (netscapeEx != null) {
! DerInputStream in = new DerInputStream(netscapeEx);
! byte[] encoded = in.getOctetString();
! encoded = new DerValue(encoded).getUnalignedBitString()
! .toByteArray();
!
! NetscapeCertTypeExtension extn =
! new NetscapeCertTypeExtension(encoded);
!
! Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING);
! if (!val) {
! if (bad != null) {
! bad[2] = true;
! } else {
! badNetscapeCertType = true;
! }
! }
! }
! } catch (IOException e) {
! //
! }
! }
!
! void getAliasInfo(String alias) {
!
! Key key = null;
!
! try {
!
! java.security.cert.Certificate[] cs = null;
!
! try {
! cs = store.getCertificateChain(alias);
! } catch (KeyStoreException kse) {
! // this never happens, because keystore has been loaded
! }
! if (cs == null) {
! MessageFormat form = new MessageFormat(rb.getString
! ("Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."));
! Object[] source = {alias, alias};
! error(form.format(source));
! }
!
! certChain = new X509Certificate[cs.length];
! for (int i=0; i<cs.length; i++) {
! if (!(cs[i] instanceof X509Certificate)) {
! error(rb.getString
! ("found non-X.509 certificate in signer's chain"));
! }
! certChain[i] = (X509Certificate)cs[i];
! }
!
! // order the cert chain if necessary (put user cert first,
! // root-cert last in the chain)
! X509Certificate userCert
! = (X509Certificate)store.getCertificate(alias);
!
! // check validity of signer certificate
! try {
! userCert.checkValidity();
!
! if (userCert.getNotAfter().getTime() <
! System.currentTimeMillis() + SIX_MONTHS) {
!
! hasExpiringCert = true;
! }
! } catch (CertificateExpiredException cee) {
! hasExpiredCert = true;
!
! } catch (CertificateNotYetValidException cnyve) {
! notYetValidCert = true;
! }
!
! checkCertUsage(userCert, null);
!
! if (!userCert.equals(certChain[0])) {
! // need to order ...
! X509Certificate[] certChainTmp
! = new X509Certificate[certChain.length];
! certChainTmp[0] = userCert;
! Principal issuer = userCert.getIssuerDN();
! for (int i=1; i<certChain.length; i++) {
! int j;
! // look for the cert whose subject corresponds to the
! // given issuer
! for (j=0; j<certChainTmp.length; j++) {
! if (certChainTmp[j] == null)
! continue;
! Principal subject = certChainTmp[j].getSubjectDN();
! if (issuer.equals(subject)) {
! certChain[i] = certChainTmp[j];
! issuer = certChainTmp[j].getIssuerDN();
! certChainTmp[j] = null;
! break;
! }
! }
! if (j == certChainTmp.length) {
! error(rb.getString("incomplete certificate chain"));
! }
!
! }
! certChain = certChainTmp; // ordered
! }
!
! try {
! if (!token && keypass == null)
! key = store.getKey(alias, storepass);
! else
! key = store.getKey(alias, keypass);
! } catch (UnrecoverableKeyException e) {
! if (token) {
! throw e;
! } else if (keypass == null) {
! // Did not work out, so prompt user for key password
! MessageFormat form = new MessageFormat(rb.getString
! ("Enter key password for alias: "));
! Object[] source = {alias};
! keypass = getPass(form.format(source));
! key = store.getKey(alias, keypass);
! }
! }
! } catch (NoSuchAlgorithmException e) {
! error(e.getMessage());
! } catch (UnrecoverableKeyException e) {
! error(rb.getString("unable to recover key from keystore"));
! } catch (KeyStoreException kse) {
! // this never happens, because keystore has been loaded
! }
!
! if (!(key instanceof PrivateKey)) {
! MessageFormat form = new MessageFormat(rb.getString
! ("key associated with alias not a private key"));
! Object[] source = {alias};
! error(form.format(source));
! } else {
! privateKey = (PrivateKey)key;
! }
! }
!
! void error(String message)
! {
! System.out.println(rb.getString("jarsigner: ")+message);
! System.exit(1);
! }
!
!
! void error(String message, Exception e)
! {
! System.out.println(rb.getString("jarsigner: ")+message);
! if (debug) {
! e.printStackTrace();
! }
! System.exit(1);
! }
!
! char[] getPass(String prompt)
! {
! System.err.print(prompt);
! System.err.flush();
! try {
! char[] pass = Password.readPassword(System.in);
!
! if (pass == null) {
! error(rb.getString("you must enter key password"));
! } else {
! return pass;
! }
! } catch (IOException ioe) {
! error(rb.getString("unable to read password: ")+ioe.getMessage());
! }
! // this shouldn't happen
! return null;
! }
!
! /*
! * Reads all the bytes for a given zip entry.
! */
! private synchronized byte[] getBytes(ZipFile zf,
! ZipEntry ze) throws IOException {
! int n;
!
! InputStream is = null;
! try {
! is = zf.getInputStream(ze);
! baos.reset();
! long left = ze.getSize();
!
! while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
! baos.write(buffer, 0, n);
! left -= n;
! }
! } finally {
! if (is != null) {
! is.close();
! }
! }
!
! return baos.toByteArray();
! }
!
! /*
! * Returns manifest entry from given jar file, or null if given jar file
! * does not have a manifest entry.
! */
! private ZipEntry getManifestFile(ZipFile zf) {
! ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
! if (ze == null) {
! // Check all entries for matching name
! Enumeration<? extends ZipEntry> enum_ = zf.entries();
! while (enum_.hasMoreElements() && ze == null) {
! ze = enum_.nextElement();
! if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
! (ze.getName())) {
! ze = null;
! }
! }
! }
! return ze;
! }
!
! /*
! * Computes the digests of a zip entry, and returns them as an array
! * of base64-encoded strings.
! */
! private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
! MessageDigest[] digests,
! BASE64Encoder encoder)
! throws IOException {
!
! int n, i;
! InputStream is = null;
! try {
! is = zf.getInputStream(ze);
! long left = ze.getSize();
! while((left > 0)
! && (n = is.read(buffer, 0, buffer.length)) != -1) {
! for (i=0; i<digests.length; i++) {
! digests[i].update(buffer, 0, n);
! }
! left -= n;
! }
! } finally {
! if (is != null) {
! is.close();
! }
! }
!
! // complete the digests
! String[] base64Digests = new String[digests.length];
! for (i=0; i<digests.length; i++) {
! base64Digests[i] = encoder.encode(digests[i].digest());
! }
! return base64Digests;
! }
!
! /*
! * Computes the digests of a zip entry, and returns them as a list of
! * attributes
! */
! private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
! MessageDigest[] digests,
! BASE64Encoder encoder)
! throws IOException {
!
! String[] base64Digests = getDigests(ze, zf, digests, encoder);
! Attributes attrs = new Attributes();
!
! for (int i=0; i<digests.length; i++) {
! attrs.putValue(digests[i].getAlgorithm()+"-Digest",
! base64Digests[i]);
! }
! return attrs;
! }
!
! /*
! * Updates the digest attributes of a manifest entry, by adding or
! * replacing digest values.
! * A digest value is added if the manifest entry does not contain a digest
! * for that particular algorithm.
! * A digest value is replaced if it is obsolete.
! *
! * Returns true if the manifest entry has been changed, and false
! * otherwise.
! */
! private boolean updateDigests(ZipEntry ze, ZipFile zf,
! MessageDigest[] digests,
! BASE64Encoder encoder,
! Manifest mf) throws IOException {
! boolean update = false;
!
! Attributes attrs = mf.getAttributes(ze.getName());
! String[] base64Digests = getDigests(ze, zf, digests, encoder);
!
! for (int i=0; i<digests.length; i++) {
! String name = digests[i].getAlgorithm()+"-Digest";
! String mfDigest = attrs.getValue(name);
! if (mfDigest == null
! && digests[i].getAlgorithm().equalsIgnoreCase("SHA")) {
! // treat "SHA" and "SHA1" the same
! mfDigest = attrs.getValue("SHA-Digest");
! }
! if (mfDigest == null) {
! // compute digest and add it to list of attributes
! attrs.putValue(name, base64Digests[i]);
! update=true;
! } else {
! // compare digests, and replace the one in the manifest
! // if they are different
! if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
! attrs.putValue(name, base64Digests[i]);
! update=true;
! }
! }
! }
! return update;
! }
!
! /*
! * Try to load the specified signing mechanism.
! * The URL class loader is used.
! */
! private ContentSigner loadSigningMechanism(String signerClassName,
! String signerClassPath) throws Exception {
!
! // construct class loader
! String cpString = null; // make sure env.class.path defaults to dot
!
! // do prepends to get correct ordering
! cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);
! cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
! cpString = PathList.appendPath(signerClassPath, cpString);
! URL[] urls = PathList.pathToURLs(cpString);
! ClassLoader appClassLoader = new URLClassLoader(urls);
!
! // attempt to find signer
! Class<?> signerClass = appClassLoader.loadClass(signerClassName);
!
! // Check that it implements ContentSigner
! Object signer = signerClass.newInstance();
! if (!(signer instanceof ContentSigner)) {
! MessageFormat form = new MessageFormat(
! rb.getString("signerClass is not a signing mechanism"));
! Object[] source = {signerClass.getName()};
! throw new IllegalArgumentException(form.format(source));
! }
! return (ContentSigner)signer;
! }
! }
!
! /**
! * This is a BASE64Encoder that does not insert a default newline at the end of
! * every output line. This is necessary because java.util.jar does its own
! * line management (see Manifest.make72Safe()). Inserting additional new lines
! * can cause line-wrapping problems (see CR 6219522).
! */
! class JarBASE64Encoder extends BASE64Encoder {
! /**
! * Encode the suffix that ends every output line.
! */
! protected void encodeLineSuffix(OutputStream aStream) throws IOException { }
! }
!
! class SignatureFile {
!
! /** SignatureFile */
! Manifest sf;
!
! /** .SF base name */
! String baseName;
!
! public SignatureFile(MessageDigest digests[],
! Manifest mf,
! ManifestDigester md,
! String baseName,
! boolean signManifest)
!
! {
! this.baseName = baseName;
!
! String version = System.getProperty("java.version");
! String javaVendor = System.getProperty("java.vendor");
!
! sf = new Manifest();
! Attributes mattr = sf.getMainAttributes();
! BASE64Encoder encoder = new JarBASE64Encoder();
!
! mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
! mattr.putValue("Created-By", version + " (" + javaVendor + ")");
!
! if (signManifest) {
! // sign the whole manifest
! for (int i=0; i < digests.length; i++) {
! mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
! encoder.encode(md.manifestDigest(digests[i])));
! }
! }
!
! // create digest of the manifest main attributes
! ManifestDigester.Entry mde =
! md.get(ManifestDigester.MF_MAIN_ATTRS, false);
! if (mde != null) {
! for (int i=0; i < digests.length; i++) {
! mattr.putValue(digests[i].getAlgorithm() +
! "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
! encoder.encode(mde.digest(digests[i])));
! }
! } else {
! throw new IllegalStateException
! ("ManifestDigester failed to create " +
! "Manifest-Main-Attribute entry");
! }
!
! /* go through the manifest entries and create the digests */
!
! Map<String,Attributes> entries = sf.getEntries();
! Iterator<Map.Entry<String,Attributes>> mit =
! mf.getEntries().entrySet().iterator();
! while(mit.hasNext()) {
! Map.Entry<String,Attributes> e = mit.next();
! String name = e.getKey();
! mde = md.get(name, false);
! if (mde != null) {
! Attributes attr = new Attributes();
! for (int i=0; i < digests.length; i++) {
! attr.putValue(digests[i].getAlgorithm()+"-Digest",
! encoder.encode(mde.digest(digests[i])));
! }
! entries.put(name, attr);
! }
! }
! }
!
! /**
! * Writes the SignatureFile to the specified OutputStream.
! *
! * @param out the output stream
! * @exception IOException if an I/O error has occurred
! */
!
! public void write(OutputStream out) throws IOException
! {
! sf.write(out);
! }
!
! /**
! * get .SF file name
! */
! public String getMetaName()
! {
! return "META-INF/"+ baseName + ".SF";
! }
!
! /**
! * get base file name
! */
! public String getBaseName()
! {
! return baseName;
! }
!
! /*
! * Generate a signed data block.
! * If a URL or a certificate (containing a URL) for a Timestamping
! * Authority is supplied then a signature timestamp is generated and
! * inserted into the signed data block.
! *
! * @param sigalg signature algorithm to use, or null to use default
! * @param tsaUrl The location of the Timestamping Authority. If null
! * then no timestamp is requested.
! * @param tsaCert The certificate for the Timestamping Authority. If null
! * then no timestamp is requested.
! * @param signingMechanism The signing mechanism to use.
! * @param args The command-line arguments to jarsigner.
! * @param zipFile The original source Zip file.
! */
! public Block generateBlock(PrivateKey privateKey,
! String sigalg,
! X509Certificate[] certChain,
! boolean externalSF, String tsaUrl,
! X509Certificate tsaCert,
! ContentSigner signingMechanism,
! String[] args, ZipFile zipFile)
! throws NoSuchAlgorithmException, InvalidKeyException, IOException,
! SignatureException, CertificateException
! {
! return new Block(this, privateKey, sigalg, certChain, externalSF,
! tsaUrl, tsaCert, signingMechanism, args, zipFile);
! }
!
!
! public static class Block {
!
! private byte[] block;
! private String blockFileName;
!
! /*
! * Construct a new signature block.
! */
! Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
! X509Certificate[] certChain, boolean externalSF, String tsaUrl,
! X509Certificate tsaCert, ContentSigner signingMechanism,
! String[] args, ZipFile zipFile)
! throws NoSuchAlgorithmException, InvalidKeyException, IOException,
! SignatureException, CertificateException {
!
! Principal issuerName = certChain[0].getIssuerDN();
! if (!(issuerName instanceof X500Name)) {
! // must extract the original encoded form of DN for subsequent
! // name comparison checks (converting to a String and back to
! // an encoded DN could cause the types of String attribute
! // values to be changed)
! X509CertInfo tbsCert = new
! X509CertInfo(certChain[0].getTBSCertificate());
! issuerName = (Principal)
! tbsCert.get(CertificateIssuerName.NAME + "." +
! CertificateIssuerName.DN_NAME);
! }
! BigInteger serial = certChain[0].getSerialNumber();
!
! String digestAlgorithm;
! String signatureAlgorithm;
! String keyAlgorithm = privateKey.getAlgorithm();
! /*
! * If no signature algorithm was specified, we choose a
! * default that is compatible with the private key algorithm.
! */
! if (sigalg == null) {
!
! if (keyAlgorithm.equalsIgnoreCase("DSA"))
! digestAlgorithm = "SHA1";
! else if (keyAlgorithm.equalsIgnoreCase("RSA"))
! digestAlgorithm = "SHA1";
! else {
! throw new RuntimeException("private key is not a DSA or "
! + "RSA key");
! }
! signatureAlgorithm = digestAlgorithm + "with" + keyAlgorithm;
! } else {
! signatureAlgorithm = sigalg;
! }
!
! // check common invalid key/signature algorithm combinations
! String sigAlgUpperCase = signatureAlgorithm.toUpperCase();
! if ((sigAlgUpperCase.endsWith("WITHRSA") &&
! !keyAlgorithm.equalsIgnoreCase("RSA")) ||
! (sigAlgUpperCase.endsWith("WITHDSA") &&
! !keyAlgorithm.equalsIgnoreCase("DSA"))) {
! throw new SignatureException
! ("private key algorithm is not compatible with signature algorithm");
! }
!
! blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;
!
! AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);
! AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);
!
! Signature sig = Signature.getInstance(signatureAlgorithm);
! sig.initSign(privateKey);
!
! ByteArrayOutputStream baos = new ByteArrayOutputStream();
! sfg.write(baos);
!
! byte[] content = baos.toByteArray();
!
! sig.update(content);
! byte[] signature = sig.sign();
!
! // Timestamp the signature and generate the signature block file
! if (signingMechanism == null) {
! signingMechanism = new TimestampedSigner();
! }
! URI tsaUri = null;
! try {
! if (tsaUrl != null) {
! tsaUri = new URI(tsaUrl);
! }
! } catch (URISyntaxException e) {
! throw new IOException(e);
! }
!
! // Assemble parameters for the signing mechanism
! ContentSignerParameters params =
! new JarSignerParameters(args, tsaUri, tsaCert, signature,
! signatureAlgorithm, certChain, content, zipFile);
!
! // Generate the signature block
! block = signingMechanism.generateSignedData(
! params, externalSF, (tsaUrl != null || tsaCert != null));
! }
!
! /*
! * get block file name.
! */
! public String getMetaName()
! {
! return blockFileName;
! }
!
! /**
! * Writes the block file to the specified OutputStream.
! *
! * @param out the output stream
! * @exception IOException if an I/O error has occurred
! */
!
! public void write(OutputStream out) throws IOException
! {
! out.write(block);
! }
! }
! }
!
!
! /*
! * This object encapsulates the parameters used to perform content signing.
! */
! class JarSignerParameters implements ContentSignerParameters {
!
! private String[] args;
! private URI tsa;
! private X509Certificate tsaCertificate;
! private byte[] signature;
! private String signatureAlgorithm;
! private X509Certificate[] signerCertificateChain;
! private byte[] content;
! private ZipFile source;
!
! /**
! * Create a new object.
! */
! JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
! byte[] signature, String signatureAlgorithm,
! X509Certificate[] signerCertificateChain, byte[] content,
! ZipFile source) {
!
! if (signature == null || signatureAlgorithm == null ||
! signerCertificateChain == null) {
! throw new NullPointerException();
! }
! this.args = args;
! this.tsa = tsa;
! this.tsaCertificate = tsaCertificate;
! this.signature = signature;
! this.signatureAlgorithm = signatureAlgorithm;
! this.signerCertificateChain = signerCertificateChain;
! this.content = content;
! this.source = source;
! }
!
! /**
! * Retrieves the command-line arguments.
! *
! * @return The command-line arguments. May be null.
! */
! public String[] getCommandLine() {
! return args;
! }
!
! /**
! * Retrieves the identifier for a Timestamping Authority (TSA).
! *
! * @return The TSA identifier. May be null.
! */
! public URI getTimestampingAuthority() {
! return tsa;
! }
!
! /**
! * Retrieves the certificate for a Timestamping Authority (TSA).
! *
! * @return The TSA certificate. May be null.
! */
! public X509Certificate getTimestampingAuthorityCertificate() {
! return tsaCertificate;
! }
!
! /**
! * Retrieves the signature.
! *
! * @return The non-null signature bytes.
! */
! public byte[] getSignature() {
! return signature;
! }
!
! /**
! * Retrieves the name of the signature algorithm.
! *
! * @return The non-null string name of the signature algorithm.
! */
! public String getSignatureAlgorithm() {
! return signatureAlgorithm;
! }
!
! /**
! * Retrieves the signer's X.509 certificate chain.
! *
! * @return The non-null array of X.509 public-key certificates.
! */
! public X509Certificate[] getSignerCertificateChain() {
! return signerCertificateChain;
! }
!
! /**
! * Retrieves the content that was signed.
! *
! * @return The content bytes. May be null.
! */
! public byte[] getContent() {
! return content;
! }
!
! /**
! * Retrieves the original source ZIP file before it was signed.
! *
! * @return The original ZIP file. May be null.
! */
! public ZipFile getSource() {
! return source;
}
}
--- 23,41 ----
* questions.
*/
package sun.security.tools;
! import sun.security.tools.jarsigner.Main;
/**
! * This is a stub for compatibility reasons.
! * Please use sun.security.tools.jarsigner.Main in new code.
*/
! public final class JarSigner {
! private JarSigner() { }
! public static void main(String[] args) throws Exception {
! Main.main(args);
}
}
< prev index next >