提交 01b84298 编写于 作者: W weijun

6802846: jarsigner needs enhanced cert validation(options)

Reviewed-by: xuelei
上级 ae3501c3
/* /*
* Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -32,28 +32,44 @@ import java.util.jar.*; ...@@ -32,28 +32,44 @@ import java.util.jar.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.SocketTimeoutException;
import java.text.Collator; import java.text.Collator;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.*; import java.security.*;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import com.sun.jarsigner.ContentSigner; import com.sun.jarsigner.ContentSigner;
import com.sun.jarsigner.ContentSignerParameters; import com.sun.jarsigner.ContentSignerParameters;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.util.Map.Entry;
import sun.security.x509.*; import sun.security.x509.*;
import sun.security.util.*; import sun.security.util.*;
import sun.misc.BASE64Encoder; import sun.misc.BASE64Encoder;
/** /**
* <p>The jarsigner utility. * <p>The jarsigner utility.
* *
* The exit codes for the main method are:
*
* 0: success
* 1: any error that the jar cannot be signed or verified, including:
* keystore loading error
* TSP communciation error
* jarsigner command line error...
* otherwise: error codes from -strict
*
* @author Roland Schemers * @author Roland Schemers
* @author Jan Luehe * @author Jan Luehe
*/ */
...@@ -84,8 +100,6 @@ public class JarSigner { ...@@ -84,8 +100,6 @@ public class JarSigner {
// Attention: // Attention:
// This is the entry that get launched by the security tool jarsigner. // 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 { public static void main(String args[]) throws Exception {
JarSigner js = new JarSigner(); JarSigner js = new JarSigner();
js.run(args); js.run(args);
...@@ -93,31 +107,32 @@ public class JarSigner { ...@@ -93,31 +107,32 @@ public class JarSigner {
static final String VERSION = "1.0"; static final String VERSION = "1.0";
static final int IN_KEYSTORE = 0x01; static final int IN_KEYSTORE = 0x01; // signer is in keystore
static final int IN_SCOPE = 0x02; static final int IN_SCOPE = 0x02;
static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
// signer is not in alias list
static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
// signer's certificate chain (when composing) X509Certificate[] certChain; // signer's cert chain (when composing)
X509Certificate[] certChain; PrivateKey privateKey; // private key
KeyStore store; // the keystore specified by -keystore
/* // or the default keystore, never null
* private key
*/
PrivateKey privateKey;
KeyStore store;
IdentityScope scope; IdentityScope scope;
String keystore; // key store file String keystore; // key store file
boolean nullStream = false; // null keystore input stream (NONE) boolean nullStream = false; // null keystore input stream (NONE)
boolean token = false; // token-based keystore boolean token = false; // token-based keystore
String jarfile; // jar file to sign String jarfile; // jar file to sign or verify
String alias; // alias to sign jar with String alias; // alias to sign jar with
List<String> ckaliases = new ArrayList<String>(); // aliases in -verify
char[] storepass; // keystore password char[] storepass; // keystore password
boolean protectedPath; // protected authentication path boolean protectedPath; // protected authentication path
String storetype; // keystore type String storetype; // keystore type
String providerName; // provider name String providerName; // provider name
Vector<String> providers = null; // list of providers Vector<String> providers = null; // list of providers
HashMap<String,String> providerArgs = new HashMap<String, String>(); // arguments for provider constructors // arguments for provider constructors
HashMap<String,String> providerArgs = new HashMap<String, String>();
char[] keypass; // private key password char[] keypass; // private key password
String sigfile; // name of .SF file String sigfile; // name of .SF file
String sigalg; // name of signature algorithm String sigalg; // name of signature algorithm
...@@ -125,12 +140,14 @@ public class JarSigner { ...@@ -125,12 +140,14 @@ public class JarSigner {
String signedjar; // output filename String signedjar; // output filename
String tsaUrl; // location of the Timestamping Authority String tsaUrl; // location of the Timestamping Authority
String tsaAlias; // alias for the Timestamping Authority's certificate String tsaAlias; // alias for the Timestamping Authority's certificate
String altCertChain; // file to read alternative cert chain from
boolean verify = false; // verify the jar boolean verify = false; // verify the jar
boolean verbose = false; // verbose output when signing/verifying String verbose = null; // verbose output when signing/verifying
boolean showcerts = false; // show certs when verifying boolean showcerts = false; // show certs when verifying
boolean debug = false; // debug boolean debug = false; // debug
boolean signManifest = true; // "sign" the whole manifest boolean signManifest = true; // "sign" the whole manifest
boolean externalSF = true; // leave the .SF out of the PKCS7 block boolean externalSF = true; // leave the .SF out of the PKCS7 block
boolean strict = false; // treat warnings as error
// read zip entry raw bytes // read zip entry raw bytes
private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
...@@ -139,14 +156,22 @@ public class JarSigner { ...@@ -139,14 +156,22 @@ public class JarSigner {
private String altSignerClass = null; private String altSignerClass = null;
private String altSignerClasspath = null; private String altSignerClasspath = null;
private ZipFile zipFile = null; private ZipFile zipFile = null;
private boolean hasExpiredCert = false; private boolean hasExpiredCert = false;
private boolean hasExpiringCert = false; private boolean hasExpiringCert = false;
private boolean notYetValidCert = false; private boolean notYetValidCert = false;
private boolean chainNotValidated = false;
private boolean notSignedByAlias = false;
private boolean aliasNotInStore = false;
private boolean hasUnsignedEntry = false;
private boolean badKeyUsage = false; private boolean badKeyUsage = false;
private boolean badExtendedKeyUsage = false; private boolean badExtendedKeyUsage = false;
private boolean badNetscapeCertType = false; private boolean badNetscapeCertType = false;
CertificateFactory certificateFactory;
CertPathValidator validator;
PKIXParameters pkixParameters;
public void run(String args[]) { public void run(String args[]) {
try { try {
parseArgs(args); parseArgs(args);
...@@ -184,14 +209,6 @@ public class JarSigner { ...@@ -184,14 +209,6 @@ public class JarSigner {
} }
} }
hasExpiredCert = false;
hasExpiringCert = false;
notYetValidCert = false;
badKeyUsage = false;
badExtendedKeyUsage = false;
badNetscapeCertType = false;
if (verify) { if (verify) {
try { try {
loadKeyStore(keystore, false); loadKeyStore(keystore, false);
...@@ -238,6 +255,29 @@ public class JarSigner { ...@@ -238,6 +255,29 @@ public class JarSigner {
storepass = null; storepass = null;
} }
} }
if (strict) {
int exitCode = 0;
if (hasExpiringCert) {
exitCode |= 2;
}
if (chainNotValidated) {
// hasExpiredCert and notYetValidCert included in this case
exitCode |= 4;
}
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
exitCode |= 8;
}
if (hasUnsignedEntry) {
exitCode |= 16;
}
if (notSignedByAlias || aliasNotInStore) {
exitCode |= 32;
}
if (exitCode != 0) {
System.exit(exitCode);
}
}
} }
/* /*
...@@ -247,25 +287,26 @@ public class JarSigner { ...@@ -247,25 +287,26 @@ public class JarSigner {
/* parse flags */ /* parse flags */
int n = 0; int n = 0;
for (n=0; (n < args.length) && args[n].startsWith("-"); n++) { if (args.length == 0) fullusage();
for (n=0; n < args.length; n++) {
String flags = args[n]; String flags = args[n];
if (collator.compare(flags, "-keystore") == 0) { if (collator.compare(flags, "-keystore") == 0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
keystore = args[n]; keystore = args[n];
} else if (collator.compare(flags, "-storepass") ==0) { } else if (collator.compare(flags, "-storepass") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
storepass = args[n].toCharArray(); storepass = args[n].toCharArray();
} else if (collator.compare(flags, "-storetype") ==0) { } else if (collator.compare(flags, "-storetype") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
storetype = args[n]; storetype = args[n];
} else if (collator.compare(flags, "-providerName") ==0) { } else if (collator.compare(flags, "-providerName") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
providerName = args[n]; providerName = args[n];
} else if ((collator.compare(flags, "-provider") == 0) || } else if ((collator.compare(flags, "-provider") == 0) ||
(collator.compare(flags, "-providerClass") == 0)) { (collator.compare(flags, "-providerClass") == 0)) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
if (providers == null) { if (providers == null) {
providers = new Vector<String>(3); providers = new Vector<String>(3);
} }
...@@ -274,35 +315,38 @@ public class JarSigner { ...@@ -274,35 +315,38 @@ public class JarSigner {
if (args.length > (n+1)) { if (args.length > (n+1)) {
flags = args[n+1]; flags = args[n+1];
if (collator.compare(flags, "-providerArg") == 0) { if (collator.compare(flags, "-providerArg") == 0) {
if (args.length == (n+2)) usage(); if (args.length == (n+2)) usageNoArg();
providerArgs.put(args[n], args[n+2]); providerArgs.put(args[n], args[n+2]);
n += 2; n += 2;
} }
} }
} else if (collator.compare(flags, "-protected") ==0) { } else if (collator.compare(flags, "-protected") ==0) {
protectedPath = true; protectedPath = true;
} else if (collator.compare(flags, "-certchain") ==0) {
if (++n == args.length) usageNoArg();
altCertChain = args[n];
} else if (collator.compare(flags, "-debug") ==0) { } else if (collator.compare(flags, "-debug") ==0) {
debug = true; debug = true;
} else if (collator.compare(flags, "-keypass") ==0) { } else if (collator.compare(flags, "-keypass") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
keypass = args[n].toCharArray(); keypass = args[n].toCharArray();
} else if (collator.compare(flags, "-sigfile") ==0) { } else if (collator.compare(flags, "-sigfile") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
sigfile = args[n]; sigfile = args[n];
} else if (collator.compare(flags, "-signedjar") ==0) { } else if (collator.compare(flags, "-signedjar") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
signedjar = args[n]; signedjar = args[n];
} else if (collator.compare(flags, "-tsa") ==0) { } else if (collator.compare(flags, "-tsa") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
tsaUrl = args[n]; tsaUrl = args[n];
} else if (collator.compare(flags, "-tsacert") ==0) { } else if (collator.compare(flags, "-tsacert") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
tsaAlias = args[n]; tsaAlias = args[n];
} else if (collator.compare(flags, "-altsigner") ==0) { } else if (collator.compare(flags, "-altsigner") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
altSignerClass = args[n]; altSignerClass = args[n];
} else if (collator.compare(flags, "-altsignerpath") ==0) { } else if (collator.compare(flags, "-altsignerpath") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
altSignerClasspath = args[n]; altSignerClasspath = args[n];
} else if (collator.compare(flags, "-sectionsonly") ==0) { } else if (collator.compare(flags, "-sectionsonly") ==0) {
signManifest = false; signManifest = false;
...@@ -311,30 +355,56 @@ public class JarSigner { ...@@ -311,30 +355,56 @@ public class JarSigner {
} else if (collator.compare(flags, "-verify") ==0) { } else if (collator.compare(flags, "-verify") ==0) {
verify = true; verify = true;
} else if (collator.compare(flags, "-verbose") ==0) { } else if (collator.compare(flags, "-verbose") ==0) {
verbose = true; verbose = "all";
} else if (collator.compare(flags, "-verbose:all") ==0) {
verbose = "all";
} else if (collator.compare(flags, "-verbose:summary") ==0) {
verbose = "summary";
} else if (collator.compare(flags, "-verbose:grouped") ==0) {
verbose = "grouped";
} else if (collator.compare(flags, "-sigalg") ==0) { } else if (collator.compare(flags, "-sigalg") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
sigalg = args[n]; sigalg = args[n];
} else if (collator.compare(flags, "-digestalg") ==0) { } else if (collator.compare(flags, "-digestalg") ==0) {
if (++n == args.length) usage(); if (++n == args.length) usageNoArg();
digestalg = args[n]; digestalg = args[n];
} else if (collator.compare(flags, "-certs") ==0) { } else if (collator.compare(flags, "-certs") ==0) {
showcerts = true; showcerts = true;
} else if (collator.compare(flags, "-strict") ==0) {
strict = true;
} else if (collator.compare(flags, "-h") == 0 || } else if (collator.compare(flags, "-h") == 0 ||
collator.compare(flags, "-help") == 0) { collator.compare(flags, "-help") == 0) {
usage(); fullusage();
} else { } else {
System.err.println(rb.getString("Illegal option: ") + flags); if (!flags.startsWith("-")) {
usage(); if (jarfile == null) {
jarfile = flags;
} else {
alias = flags;
ckaliases.add(alias);
}
} else {
System.err.println(
rb.getString("Illegal option: ") + flags);
usage();
}
} }
} }
if (n == args.length) usage(); // -certs must always be specified with -verbose
jarfile = args[n++]; if (verbose == null) showcerts = false;
if (!verify) { if (jarfile == null) {
if (n == args.length) usage(); System.err.println(rb.getString("Please specify jarfile name"));
alias = args[n++]; usage();
}
if (!verify && alias == null) {
System.err.println(rb.getString("Please specify alias name"));
usage();
}
if (!verify && ckaliases.size() > 1) {
System.err.println(rb.getString("Only one alias can be specified"));
usage();
} }
if (storetype == null) { if (storetype == null) {
...@@ -357,7 +427,6 @@ public class JarSigner { ...@@ -357,7 +427,6 @@ public class JarSigner {
if (token && !nullStream) { if (token && !nullStream) {
System.err.println(MessageFormat.format(rb.getString System.err.println(MessageFormat.format(rb.getString
("-keystore must be NONE if -storetype is {0}"), storetype)); ("-keystore must be NONE if -storetype is {0}"), storetype));
System.err.println();
usage(); usage();
} }
...@@ -365,7 +434,6 @@ public class JarSigner { ...@@ -365,7 +434,6 @@ public class JarSigner {
System.err.println(MessageFormat.format(rb.getString System.err.println(MessageFormat.format(rb.getString
("-keypass can not be specified " + ("-keypass can not be specified " +
"if -storetype is {0}"), storetype)); "if -storetype is {0}"), storetype));
System.err.println();
usage(); usage();
} }
...@@ -374,7 +442,6 @@ public class JarSigner { ...@@ -374,7 +442,6 @@ public class JarSigner {
System.err.println(rb.getString System.err.println(rb.getString
("If -protected is specified, " + ("If -protected is specified, " +
"then -storepass and -keypass must not be specified")); "then -storepass and -keypass must not be specified"));
System.err.println();
usage(); usage();
} }
} }
...@@ -383,17 +450,27 @@ public class JarSigner { ...@@ -383,17 +450,27 @@ public class JarSigner {
System.err.println(rb.getString System.err.println(rb.getString
("If keystore is not password protected, " + ("If keystore is not password protected, " +
"then -storepass and -keypass must not be specified")); "then -storepass and -keypass must not be specified"));
System.err.println();
usage(); usage();
} }
} }
} }
void usageNoArg() {
System.out.println(rb.getString("Option lacks argument"));
usage();
}
void usage() { void usage() {
System.out.println();
System.out.println(rb.getString("Please type jarsigner -help for usage"));
System.exit(1);
}
void fullusage() {
System.out.println(rb.getString System.out.println(rb.getString
("Usage: jarsigner [options] jar-file alias")); ("Usage: jarsigner [options] jar-file alias"));
System.out.println(rb.getString System.out.println(rb.getString
(" jarsigner -verify [options] jar-file")); (" jarsigner -verify [options] jar-file [alias...]"));
System.out.println(); System.out.println();
System.out.println(rb.getString System.out.println(rb.getString
("[-keystore <url>] keystore location")); ("[-keystore <url>] keystore location"));
...@@ -407,6 +484,9 @@ public class JarSigner { ...@@ -407,6 +484,9 @@ public class JarSigner {
System.out.println(rb.getString System.out.println(rb.getString
("[-keypass <password>] password for private key (if different)")); ("[-keypass <password>] password for private key (if different)"));
System.out.println(); System.out.println();
System.out.println(rb.getString
("[-certchain <file>] name of alternative certchain file"));
System.out.println();
System.out.println(rb.getString System.out.println(rb.getString
("[-sigfile <file>] name of .SF/.DSA file")); ("[-sigfile <file>] name of .SF/.DSA file"));
System.out.println(); System.out.println();
...@@ -423,7 +503,9 @@ public class JarSigner { ...@@ -423,7 +503,9 @@ public class JarSigner {
("[-verify] verify a signed JAR file")); ("[-verify] verify a signed JAR file"));
System.out.println(); System.out.println();
System.out.println(rb.getString System.out.println(rb.getString
("[-verbose] verbose output when signing/verifying")); ("[-verbose[:suboptions]] verbose output when signing/verifying."));
System.out.println(rb.getString
(" suboptions can be all, grouped or summary"));
System.out.println(); System.out.println();
System.out.println(rb.getString System.out.println(rb.getString
("[-certs] display certificates when verbose and verifying")); ("[-certs] display certificates when verbose and verifying"));
...@@ -457,15 +539,17 @@ public class JarSigner { ...@@ -457,15 +539,17 @@ public class JarSigner {
System.out.println(rb.getString System.out.println(rb.getString
(" [-providerArg <arg>]] ... master class file and constructor argument")); (" [-providerArg <arg>]] ... master class file and constructor argument"));
System.out.println(); System.out.println();
System.out.println(rb.getString
("[-strict] treat warnings as errors"));
System.out.println();
System.exit(1); System.exit(0);
} }
void verifyJar(String jarName) void verifyJar(String jarName)
throws Exception throws Exception
{ {
boolean anySigned = false; boolean anySigned = false; // if there exists entry inside jar signed
boolean hasUnsignedEntry = false;
JarFile jf = null; JarFile jf = null;
try { try {
...@@ -494,11 +578,18 @@ public class JarSigner { ...@@ -494,11 +578,18 @@ public class JarSigner {
Manifest man = jf.getManifest(); Manifest man = jf.getManifest();
// The map to record display info, only used when -verbose provided
// key: signer info string
// value: the list of files with common key
Map<String,List<String>> output =
new LinkedHashMap<String,List<String>>();
if (man != null) { if (man != null) {
if (verbose) System.out.println(); if (verbose != null) System.out.println();
Enumeration<JarEntry> e = entriesVec.elements(); Enumeration<JarEntry> e = entriesVec.elements();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
String tab = rb.getString(" ");
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
JarEntry je = e.nextElement(); JarEntry je = e.nextElement();
...@@ -509,77 +600,118 @@ public class JarSigner { ...@@ -509,77 +600,118 @@ public class JarSigner {
hasUnsignedEntry |= !je.isDirectory() && !isSigned hasUnsignedEntry |= !je.isDirectory() && !isSigned
&& !signatureRelated(name); && !signatureRelated(name);
if (verbose) { int inStoreOrScope = inKeyStore(signers);
int inStoreOrScope = inKeyStore(signers);
boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0; boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
boolean inScope = (inStoreOrScope & IN_SCOPE) != 0; boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;
aliasNotInStore |= isSigned && (!inStore && !inScope);
// Only used when -verbose provided
StringBuffer sb = null;
if (verbose != null) {
sb = new StringBuffer();
boolean inManifest = boolean inManifest =
((man.getAttributes(name) != null) || ((man.getAttributes(name) != null) ||
(man.getAttributes("./"+name) != null) || (man.getAttributes("./"+name) != null) ||
(man.getAttributes("/"+name) != null)); (man.getAttributes("/"+name) != null));
System.out.print( sb.append(
(isSigned ? rb.getString("s") : rb.getString(" ")) + (isSigned ? rb.getString("s") : rb.getString(" ")) +
(inManifest ? rb.getString("m") : rb.getString(" ")) + (inManifest ? rb.getString("m") : rb.getString(" ")) +
(inStore ? rb.getString("k") : rb.getString(" ")) + (inStore ? rb.getString("k") : rb.getString(" ")) +
(inScope ? rb.getString("i") : rb.getString(" ")) + (inScope ? rb.getString("i") : rb.getString(" ")) +
rb.getString(" ")); ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +
StringBuffer sb = new StringBuffer(); rb.getString(" "));
sb.append("|");
}
// When -certs provided, display info has extra empty
// lines at the beginning and end.
if (isSigned) {
if (showcerts) sb.append('\n');
for (CodeSigner signer: signers) {
// signerInfo() must be called even if -verbose
// not provided. The method updates various
// warning flags.
String si = signerInfo(signer, tab, now);
if (showcerts) {
sb.append(si);
sb.append('\n');
}
}
} else if (showcerts && !verbose.equals("all")) {
// Print no info for unsigned entries when -verbose:all,
// to be consistent with old behavior.
if (signatureRelated(name)) {
sb.append("\n" + tab + rb.getString(
"(Signature related entries)") + "\n\n");
} else {
sb.append("\n" + tab + rb.getString(
"(Unsigned entries)") + "\n\n");
}
}
if (verbose != null) {
String label = sb.toString();
if (signatureRelated(name)) {
// Entries inside META-INF and other unsigned
// entries are grouped separately.
label = "-" + label.substring(1);
}
// The label finally contains 2 parts separated by '|':
// The legend displayed before the entry names, and
// the cert info (if -certs specfied).
if (!output.containsKey(label)) {
output.put(label, new ArrayList<String>());
}
StringBuffer fb = new StringBuffer();
String s = Long.toString(je.getSize()); String s = Long.toString(je.getSize());
for (int i = 6 - s.length(); i > 0; --i) { for (int i = 6 - s.length(); i > 0; --i) {
sb.append(' '); fb.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();
} }
fb.append(s).append(' ').
append(new Date(je.getTime()).toString());
fb.append(' ').append(name);
output.get(label).add(fb.toString());
} }
if (isSigned) { }
for (int i = 0; i < signers.length; i++) { }
Certificate cert = if (verbose != null) {
signers[i].getSignerCertPath() for (Entry<String,List<String>> s: output.entrySet()) {
.getCertificates().get(0); List<String> files = s.getValue();
if (cert instanceof X509Certificate) { String key = s.getKey();
checkCertUsage((X509Certificate)cert, null); if (key.charAt(0) == '-') { // the signature-related group
if (!showcerts) { key = ' ' + key.substring(1);
long notAfter = ((X509Certificate)cert) }
.getNotAfter().getTime(); int pipe = key.indexOf('|');
if (verbose.equals("all")) {
if (notAfter < now) { for (String f: files) {
hasExpiredCert = true; System.out.println(key.substring(0, pipe) + f);
} else if (notAfter < now + SIX_MONTHS) { System.out.printf(key.substring(pipe+1));
hasExpiringCert = true; }
} } else {
} if (verbose.equals("grouped")) {
for (String f: files) {
System.out.println(key.substring(0, pipe) + f);
}
} else if (verbose.equals("summary")) {
System.out.print(key.substring(0, pipe));
if (files.size() > 1) {
System.out.println(files.get(0) + " " +
String.format(rb.getString(
"(and %d more)"), files.size()-1));
} else {
System.out.println(files.get(0));
} }
} }
System.out.printf(key.substring(pipe+1));
} }
} }
}
if (verbose) {
System.out.println(); System.out.println();
System.out.println(rb.getString( System.out.println(rb.getString(
" s = signature was verified ")); " s = signature was verified "));
...@@ -589,9 +721,12 @@ public class JarSigner { ...@@ -589,9 +721,12 @@ public class JarSigner {
" k = at least one certificate was found in keystore")); " k = at least one certificate was found in keystore"));
System.out.println(rb.getString( System.out.println(rb.getString(
" i = at least one certificate was found in identity scope")); " i = at least one certificate was found in identity scope"));
if (ckaliases.size() > 0) {
System.out.println((
" X = not signed by specified alias(es)"));
}
System.out.println(); System.out.println();
} }
if (man == null) if (man == null)
System.out.println(rb.getString("no manifest.")); System.out.println(rb.getString("no manifest."));
...@@ -602,7 +737,8 @@ public class JarSigner { ...@@ -602,7 +737,8 @@ public class JarSigner {
System.out.println(rb.getString("jar verified.")); System.out.println(rb.getString("jar verified."));
if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert || if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
notYetValidCert) { notYetValidCert || chainNotValidated ||
aliasNotInStore || notSignedByAlias) {
System.out.println(); System.out.println();
System.out.println(rb.getString("Warning: ")); System.out.println(rb.getString("Warning: "));
...@@ -638,14 +774,27 @@ public class JarSigner { ...@@ -638,14 +774,27 @@ public class JarSigner {
"This jar contains entries whose signer certificate is not yet valid. ")); "This jar contains entries whose signer certificate is not yet valid. "));
} }
if (! (verbose && showcerts)) { if (chainNotValidated) {
System.out.println(
rb.getString("This jar contains entries whose certificate chain is not validated."));
}
if (notSignedByAlias) {
System.out.println(
rb.getString("This jar contains signed entries which is not signed by the specified alias(es)."));
}
if (aliasNotInStore) {
System.out.println(rb.getString("This jar contains signed entries that's not signed by alias in this keystore."));
}
if (! (verbose != null && showcerts)) {
System.out.println(); System.out.println();
System.out.println(rb.getString( System.out.println(rb.getString(
"Re-run with the -verbose and -certs options for more details.")); "Re-run with the -verbose and -certs options for more details."));
} }
} }
} }
System.exit(0); return;
} catch (Exception e) { } catch (Exception e) {
System.out.println(rb.getString("jarsigner: ") + e); System.out.println(rb.getString("jarsigner: ") + e);
if (debug) { if (debug) {
...@@ -660,15 +809,6 @@ public class JarSigner { ...@@ -660,15 +809,6 @@ public class JarSigner {
System.exit(1); 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 validityTimeForm = null;
private static MessageFormat notYetTimeForm = null; private static MessageFormat notYetTimeForm = null;
private static MessageFormat expiredTimeForm = null; private static MessageFormat expiredTimeForm = null;
...@@ -679,6 +819,8 @@ public class JarSigner { ...@@ -679,6 +819,8 @@ public class JarSigner {
* *
* [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"] * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
* [<validity-period> | <expiry-warning>] * [<validity-period> | <expiry-warning>]
*
* Note: no newline character at the end
*/ */
String printCert(String tab, Certificate c, boolean checkValidityPeriod, String printCert(String tab, Certificate c, boolean checkValidityPeriod,
long now) { long now) {
...@@ -788,54 +930,75 @@ public class JarSigner { ...@@ -788,54 +930,75 @@ public class JarSigner {
.append(signTimeForm.format(source)).append("]").toString(); .append(signTimeForm.format(source)).append("]").toString();
} }
private Map<CodeSigner,Integer> cacheForInKS =
new IdentityHashMap<CodeSigner,Integer>();
private int inKeyStoreForOneSigner(CodeSigner signer) {
if (cacheForInKS.containsKey(signer)) {
return cacheForInKS.get(signer);
}
boolean found = false;
int result = 0;
List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
for (Certificate c : certs) {
String alias = storeHash.get(c);
if (alias != null) {
if (alias.startsWith("(")) {
result |= IN_KEYSTORE;
} else if (alias.startsWith("[")) {
result |= IN_SCOPE;
}
if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
result |= SIGNED_BY_ALIAS;
}
} else {
if (store != null) {
try {
alias = store.getCertificateAlias(c);
} catch (KeyStoreException kse) {
// never happens, because keystore has been loaded
}
if (alias != null) {
storeHash.put(c, "(" + alias + ")");
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() + "]");
}
}
if (ckaliases.contains(alias)) {
result |= SIGNED_BY_ALIAS;
}
}
}
cacheForInKS.put(signer, result);
return result;
}
Hashtable<Certificate, String> storeHash = Hashtable<Certificate, String> storeHash =
new Hashtable<Certificate, String>(); new Hashtable<Certificate, String>();
int inKeyStore(CodeSigner[] signers) { int inKeyStore(CodeSigner[] signers) {
int result = 0;
if (signers == null) if (signers == null)
return 0; return 0;
boolean found = false; int output = 0;
for (int i = 0; i < signers.length; i++) {
found = false;
List<? extends Certificate> certs =
signers[i].getSignerCertPath().getCertificates();
for (Certificate c : certs) { for (CodeSigner signer: signers) {
String alias = storeHash.get(c); int result = inKeyStoreForOneSigner(signer);
output |= result;
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; if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
output |= NOT_ALIAS;
}
return output;
} }
void signJar(String jarName, String alias, String[] args) void signJar(String jarName, String alias, String[] args)
...@@ -1025,7 +1188,7 @@ public class JarSigner { ...@@ -1025,7 +1188,7 @@ public class JarSigner {
// manifest file has new length // manifest file has new length
mfFile = new ZipEntry(JarFile.MANIFEST_NAME); mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
} }
if (verbose) { if (verbose != null) {
if (mfCreated) { if (mfCreated) {
System.out.println(rb.getString(" adding: ") + System.out.println(rb.getString(" adding: ") +
mfFile.getName()); mfFile.getName());
...@@ -1076,7 +1239,7 @@ public class JarSigner { ...@@ -1076,7 +1239,7 @@ public class JarSigner {
// signature file // signature file
zos.putNextEntry(sfFile); zos.putNextEntry(sfFile);
sf.write(zos); sf.write(zos);
if (verbose) { if (verbose != null) {
if (zipFile.getEntry(sfFilename) != null) { if (zipFile.getEntry(sfFilename) != null) {
System.out.println(rb.getString(" updating: ") + System.out.println(rb.getString(" updating: ") +
sfFilename); sfFilename);
...@@ -1086,7 +1249,7 @@ public class JarSigner { ...@@ -1086,7 +1249,7 @@ public class JarSigner {
} }
} }
if (verbose) { if (verbose != null) {
if (tsaUrl != null || tsaCert != null) { if (tsaUrl != null || tsaCert != null) {
System.out.println( System.out.println(
rb.getString("requesting a signature timestamp")); rb.getString("requesting a signature timestamp"));
...@@ -1101,8 +1264,8 @@ public class JarSigner { ...@@ -1101,8 +1264,8 @@ public class JarSigner {
System.out.println(rb.getString("TSA location: ") + System.out.println(rb.getString("TSA location: ") +
certUrl); certUrl);
} }
System.out.println( System.out.println(rb.getString("TSA certificate: ") +
rb.getString("TSA certificate: ") + printCert(tsaCert)); printCert("", tsaCert, false, 0));
} }
if (signingMechanism != null) { if (signingMechanism != null) {
System.out.println( System.out.println(
...@@ -1113,7 +1276,7 @@ public class JarSigner { ...@@ -1113,7 +1276,7 @@ public class JarSigner {
// signature block file // signature block file
zos.putNextEntry(bkFile); zos.putNextEntry(bkFile);
block.write(zos); block.write(zos);
if (verbose) { if (verbose != null) {
if (zipFile.getEntry(bkFilename) != null) { if (zipFile.getEntry(bkFilename) != null) {
System.out.println(rb.getString(" updating: ") + System.out.println(rb.getString(" updating: ") +
bkFilename); bkFilename);
...@@ -1140,7 +1303,7 @@ public class JarSigner { ...@@ -1140,7 +1303,7 @@ public class JarSigner {
ZipEntry ze = enum_.nextElement(); ZipEntry ze = enum_.nextElement();
if (!ze.getName().startsWith(META_INF)) { if (!ze.getName().startsWith(META_INF)) {
if (verbose) { if (verbose != null) {
if (manifest.getAttributes(ze.getName()) != null) if (manifest.getAttributes(ze.getName()) != null)
System.out.println(rb.getString(" signing: ") + System.out.println(rb.getString(" signing: ") +
ze.getName()); ze.getName());
...@@ -1194,7 +1357,8 @@ public class JarSigner { ...@@ -1194,7 +1357,8 @@ public class JarSigner {
} }
if (hasExpiredCert || hasExpiringCert || notYetValidCert if (hasExpiredCert || hasExpiringCert || notYetValidCert
|| badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) { || badKeyUsage || badExtendedKeyUsage
|| badNetscapeCertType || chainNotValidated) {
System.out.println(); System.out.println();
System.out.println(rb.getString("Warning: ")); System.out.println(rb.getString("Warning: "));
...@@ -1223,6 +1387,11 @@ public class JarSigner { ...@@ -1223,6 +1387,11 @@ public class JarSigner {
System.out.println( System.out.println(
rb.getString("The signer certificate is not yet valid.")); rb.getString("The signer certificate is not yet valid."));
} }
if (chainNotValidated) {
System.out.println(
rb.getString("The signer's certificate chain is not validated."));
}
} }
// no IOException thrown in the above try clause, so disable // no IOException thrown in the above try clause, so disable
...@@ -1274,6 +1443,40 @@ public class JarSigner { ...@@ -1274,6 +1443,40 @@ public class JarSigner {
return false; return false;
} }
Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<CodeSigner,String>();
/**
* Returns a string of singer info, with a newline at the end
*/
private String signerInfo(CodeSigner signer, String tab, long now) {
if (cacheForSignerInfo.containsKey(signer)) {
return cacheForSignerInfo.get(signer);
}
StringBuffer s = new StringBuffer();
List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
// display the signature timestamp, if present
Timestamp timestamp = signer.getTimestamp();
if (timestamp != null) {
s.append(printTimestamp(tab, timestamp));
}
// display the certificate(s)
for (Certificate c : certs) {
s.append(printCert(tab, c, true, now));
s.append('\n');
}
try {
CertPath cp = certificateFactory.generateCertPath(certs);
validator.validate(cp, pkixParameters);
} catch (Exception e) {
chainNotValidated = true;
s.append(tab + rb.getString("[CertPath not validated: ") +
e.getLocalizedMessage() + "]\n"); // TODO
}
String result = s.toString();
cacheForSignerInfo.put(signer, result);
return result;
}
private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze) private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
throws IOException throws IOException
{ {
...@@ -1360,6 +1563,48 @@ public class JarSigner { ...@@ -1360,6 +1563,48 @@ public class JarSigner {
} }
} }
} }
Set<TrustAnchor> tas = new HashSet<TrustAnchor>();
try {
KeyStore caks = KeyTool.getCacertsKeyStore();
if (caks != null) {
Enumeration<String> aliases = caks.aliases();
while (aliases.hasMoreElements()) {
String a = aliases.nextElement();
try {
tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
} catch (Exception e2) {
// ignore, when a SecretkeyEntry does not include a cert
}
}
}
} catch (Exception e) {
// Ignore, if cacerts cannot be loaded
}
if (store != null) {
Enumeration<String> aliases = store.aliases();
while (aliases.hasMoreElements()) {
String a = aliases.nextElement();
try {
X509Certificate c = (X509Certificate)store.getCertificate(a);
// Only add TrustedCertificateEntry and self-signed
// PrivateKeyEntry
if (store.isCertificateEntry(a) ||
c.getSubjectDN().equals(c.getIssuerDN())) {
tas.add(new TrustAnchor(c, null));
}
} catch (Exception e2) {
// ignore, when a SecretkeyEntry does not include a cert
}
}
}
certificateFactory = CertificateFactory.getInstance("X.509");
validator = CertPathValidator.getInstance("PKIX");
try {
pkixParameters = new PKIXParameters(tas);
pkixParameters.setRevocationEnabled(false);
} catch (InvalidAlgorithmParameterException ex) {
// Only if tas is empty
}
} catch (IOException ioe) { } catch (IOException ioe) {
throw new RuntimeException(rb.getString("keystore load: ") + throw new RuntimeException(rb.getString("keystore load: ") +
ioe.getMessage()); ioe.getMessage());
...@@ -1408,7 +1653,8 @@ public class JarSigner { ...@@ -1408,7 +1653,8 @@ public class JarSigner {
void checkCertUsage(X509Certificate userCert, boolean[] bad) { void checkCertUsage(X509Certificate userCert, boolean[] bad) {
// Can act as a signer? // Can act as a signer?
// 1. if KeyUsage, then [0] should be true // 1. if KeyUsage, then [0:digitalSignature] or
// [1:nonRepudiation] should be true
// 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
// 3. if NetscapeCertType, then should contains OBJECT_SIGNING // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
// 1,2,3 must be true // 1,2,3 must be true
...@@ -1419,10 +1665,10 @@ public class JarSigner { ...@@ -1419,10 +1665,10 @@ public class JarSigner {
boolean[] keyUsage = userCert.getKeyUsage(); boolean[] keyUsage = userCert.getKeyUsage();
if (keyUsage != null) { if (keyUsage != null) {
if (keyUsage.length < 1 || !keyUsage[0]) { keyUsage = Arrays.copyOf(keyUsage, 9);
if (!keyUsage[0] && !keyUsage[1]) {
if (bad != null) { if (bad != null) {
bad[0] = true; bad[0] = true;
} else {
badKeyUsage = true; badKeyUsage = true;
} }
} }
...@@ -1435,7 +1681,6 @@ public class JarSigner { ...@@ -1435,7 +1681,6 @@ public class JarSigner {
&& !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning
if (bad != null) { if (bad != null) {
bad[1] = true; bad[1] = true;
} else {
badExtendedKeyUsage = true; badExtendedKeyUsage = true;
} }
} }
...@@ -1462,7 +1707,6 @@ public class JarSigner { ...@@ -1462,7 +1707,6 @@ public class JarSigner {
if (!val) { if (!val) {
if (bad != null) { if (bad != null) {
bad[2] = true; bad[2] = true;
} else {
badNetscapeCertType = true; badNetscapeCertType = true;
} }
} }
...@@ -1477,19 +1721,36 @@ public class JarSigner { ...@@ -1477,19 +1721,36 @@ public class JarSigner {
Key key = null; Key key = null;
try { try {
java.security.cert.Certificate[] cs = null; java.security.cert.Certificate[] cs = null;
if (altCertChain != null) {
try { try {
cs = store.getCertificateChain(alias); cs = CertificateFactory.getInstance("X.509").
} catch (KeyStoreException kse) { generateCertificates(new FileInputStream(altCertChain)).
// this never happens, because keystore has been loaded toArray(new Certificate[0]);
} catch (CertificateException ex) {
error(rb.getString("Cannot restore certchain from file specified"));
} catch (FileNotFoundException ex) {
error(rb.getString("File specified by -certchain does not exist"));
}
} else {
try {
cs = store.getCertificateChain(alias);
} catch (KeyStoreException kse) {
// this never happens, because keystore has been loaded
}
} }
if (cs == null) { if (cs == null || cs.length == 0) {
MessageFormat form = new MessageFormat(rb.getString if (altCertChain != null) {
("Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.")); error(rb.getString
Object[] source = {alias, alias}; ("Certificate chain not found in the file specified."));
error(form.format(source)); } else {
MessageFormat form = new MessageFormat(rb.getString
("Certificate chain not found for: alias. alias must" +
" reference a valid KeyStore key entry containing a" +
" private key and corresponding public key certificate chain."));
Object[] source = {alias, alias};
error(form.format(source));
}
} }
certChain = new X509Certificate[cs.length]; certChain = new X509Certificate[cs.length];
...@@ -1501,56 +1762,15 @@ public class JarSigner { ...@@ -1501,56 +1762,15 @@ public class JarSigner {
certChain[i] = (X509Certificate)cs[i]; certChain[i] = (X509Certificate)cs[i];
} }
// order the cert chain if necessary (put user cert first, // We don't meant to print anything, the next call
// root-cert last in the chain) // checks validity and keyUsage etc
X509Certificate userCert printCert("", certChain[0], true, 0);
= (X509Certificate)store.getCertificate(alias);
// check validity of signer certificate
try { try {
userCert.checkValidity(); CertPath cp = certificateFactory.generateCertPath(Arrays.asList(certChain));
validator.validate(cp, pkixParameters);
if (userCert.getNotAfter().getTime() < } catch (Exception e) {
System.currentTimeMillis() + SIX_MONTHS) { chainNotValidated = true;
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 { try {
......
/* /*
* Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -54,8 +54,8 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -54,8 +54,8 @@ public class JarSignerResources extends java.util.ListResourceBundle {
"If keystore is not password protected, then -storepass and -keypass must not be specified"}, "If keystore is not password protected, then -storepass and -keypass must not be specified"},
{"Usage: jarsigner [options] jar-file alias", {"Usage: jarsigner [options] jar-file alias",
"Usage: jarsigner [options] jar-file alias"}, "Usage: jarsigner [options] jar-file alias"},
{" jarsigner -verify [options] jar-file", {" jarsigner -verify [options] jar-file [alias...]",
" jarsigner -verify [options] jar-file"}, " jarsigner -verify [options] jar-file [alias...]"},
{"[-keystore <url>] keystore location", {"[-keystore <url>] keystore location",
"[-keystore <url>] keystore location"}, "[-keystore <url>] keystore location"},
{"[-storepass <password>] password for keystore integrity", {"[-storepass <password>] password for keystore integrity",
...@@ -64,6 +64,8 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -64,6 +64,8 @@ public class JarSignerResources extends java.util.ListResourceBundle {
"[-storetype <type>] keystore type"}, "[-storetype <type>] keystore type"},
{"[-keypass <password>] password for private key (if different)", {"[-keypass <password>] password for private key (if different)",
"[-keypass <password>] password for private key (if different)"}, "[-keypass <password>] password for private key (if different)"},
{"[-certchain <file>] name of alternative certchain file",
"[-certchain <file>] name of alternative certchain file"},
{"[-sigfile <file>] name of .SF/.DSA file", {"[-sigfile <file>] name of .SF/.DSA file",
"[-sigfile <file>] name of .SF/.DSA file"}, "[-sigfile <file>] name of .SF/.DSA file"},
{"[-signedjar <file>] name of signed JAR file", {"[-signedjar <file>] name of signed JAR file",
...@@ -74,8 +76,10 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -74,8 +76,10 @@ public class JarSignerResources extends java.util.ListResourceBundle {
"[-sigalg <algorithm>] name of signature algorithm"}, "[-sigalg <algorithm>] name of signature algorithm"},
{"[-verify] verify a signed JAR file", {"[-verify] verify a signed JAR file",
"[-verify] verify a signed JAR file"}, "[-verify] verify a signed JAR file"},
{"[-verbose] verbose output when signing/verifying", {"[-verbose[:suboptions]] verbose output when signing/verifying.",
"[-verbose] verbose output when signing/verifying"}, "[-verbose[:suboptions]] verbose output when signing/verifying."},
{" suboptions can be all, grouped or summary",
" suboptions can be all, grouped or summary"},
{"[-certs] display certificates when verbose and verifying", {"[-certs] display certificates when verbose and verifying",
"[-certs] display certificates when verbose and verifying"}, "[-certs] display certificates when verbose and verifying"},
{"[-tsa <url>] location of the Timestamping Authority", {"[-tsa <url>] location of the Timestamping Authority",
...@@ -98,10 +102,22 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -98,10 +102,22 @@ public class JarSignerResources extends java.util.ListResourceBundle {
"[-providerClass <class> name of cryptographic service provider's"}, "[-providerClass <class> name of cryptographic service provider's"},
{" [-providerArg <arg>]] ... master class file and constructor argument", {" [-providerArg <arg>]] ... master class file and constructor argument",
" [-providerArg <arg>]] ... master class file and constructor argument"}, " [-providerArg <arg>]] ... master class file and constructor argument"},
{"[-strict] treat warnings as errors",
"[-strict] treat warnings as errors"},
{"Option lacks argument", "Option lacks argument"},
{"Please type jarsigner -help for usage", "Please type jarsigner -help for usage"},
{"Please specify jarfile name", "Please specify jarfile name"},
{"Please specify alias name", "Please specify alias name"},
{"Only one alias can be specified", "Only one alias can be specified"},
{"This jar contains signed entries which is not signed by the specified alias(es).",
"This jar contains signed entries which is not signed by the specified alias(es)."},
{"This jar contains signed entries that's not signed by alias in this keystore.",
"This jar contains signed entries that's not signed by alias in this keystore."},
{"s", "s"}, {"s", "s"},
{"m", "m"}, {"m", "m"},
{"k", "k"}, {"k", "k"},
{"i", "i"}, {"i", "i"},
{"(and %d more)", "(and %d more)"},
{" s = signature was verified ", {" s = signature was verified ",
" s = signature was verified "}, " s = signature was verified "},
{" m = entry is listed in manifest", {" m = entry is listed in manifest",
...@@ -110,7 +126,11 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -110,7 +126,11 @@ public class JarSignerResources extends java.util.ListResourceBundle {
" k = at least one certificate was found in keystore"}, " k = at least one certificate was found in keystore"},
{" i = at least one certificate was found in identity scope", {" i = at least one certificate was found in identity scope",
" i = at least one certificate was found in identity scope"}, " i = at least one certificate was found in identity scope"},
{" X = not signed by specified alias(es)",
" X = not signed by specified alias(es)"},
{"no manifest.", "no manifest."}, {"no manifest.", "no manifest."},
{"(Signature related entries)","(Signature related entries)"},
{"(Unsigned entries)", "(Unsigned entries)"},
{"jar is unsigned. (signatures missing or not parsable)", {"jar is unsigned. (signatures missing or not parsable)",
"jar is unsigned. (signatures missing or not parsable)"}, "jar is unsigned. (signatures missing or not parsable)"},
{"jar verified.", "jar verified."}, {"jar verified.", "jar verified."},
...@@ -134,6 +154,12 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -134,6 +154,12 @@ public class JarSignerResources extends java.util.ListResourceBundle {
"unable to instantiate keystore class: "}, "unable to instantiate keystore class: "},
{"Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.", {"Certificate chain not found for: alias. alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.",
"Certificate chain not found for: {0}. {1} must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."}, "Certificate chain not found for: {0}. {1} must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."},
{"File specified by -certchain does not exist",
"File specified by -certchain does not exist"},
{"Cannot restore certchain from file specified",
"Cannot restore certchain from file specified"},
{"Certificate chain not found in the file specified.",
"Certificate chain not found in the file specified."},
{"found non-X.509 certificate in signer's chain", {"found non-X.509 certificate in signer's chain",
"found non-X.509 certificate in signer's chain"}, "found non-X.509 certificate in signer's chain"},
{"incomplete certificate chain", "incomplete certificate chain"}, {"incomplete certificate chain", "incomplete certificate chain"},
...@@ -149,6 +175,7 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -149,6 +175,7 @@ public class JarSignerResources extends java.util.ListResourceBundle {
{"certificate is not valid until", {"certificate is not valid until",
"certificate is not valid until {0}"}, "certificate is not valid until {0}"},
{"certificate will expire on", "certificate will expire on {0}"}, {"certificate will expire on", "certificate will expire on {0}"},
{"[CertPath not validated: ", "[CertPath not validated: "},
{"requesting a signature timestamp", {"requesting a signature timestamp",
"requesting a signature timestamp"}, "requesting a signature timestamp"},
{"TSA location: ", "TSA location: "}, {"TSA location: ", "TSA location: "},
...@@ -189,14 +216,18 @@ public class JarSignerResources extends java.util.ListResourceBundle { ...@@ -189,14 +216,18 @@ public class JarSignerResources extends java.util.ListResourceBundle {
"The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."}, "The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
{"The signer certificate's NetscapeCertType extension doesn't allow code signing.", {"The signer certificate's NetscapeCertType extension doesn't allow code signing.",
"The signer certificate's NetscapeCertType extension doesn't allow code signing."}, "The signer certificate's NetscapeCertType extension doesn't allow code signing."},
{"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.", {"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.",
"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."}, "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."},
{"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.", {"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."}, "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
{"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.", {"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.",
"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."}, "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
{"[{0} extension does not support code signing]", {"[{0} extension does not support code signing]",
"[{0} extension does not support code signing]"}, "[{0} extension does not support code signing]"},
{"The signer's certificate chain is not validated.",
"The signer's certificate chain is not validated."},
{"This jar contains entries whose certificate chain is not validated.",
"This jar contains entries whose certificate chain is not validated."},
}; };
/** /**
......
...@@ -3108,7 +3108,7 @@ public final class KeyTool { ...@@ -3108,7 +3108,7 @@ public final class KeyTool {
/** /**
* Returns the keystore with the configured CA certificates. * Returns the keystore with the configured CA certificates.
*/ */
private KeyStore getCacertsKeyStore() public static KeyStore getCacertsKeyStore()
throws Exception throws Exception
{ {
String sep = File.separator; String sep = File.separator;
......
#
# Copyright 2009 Sun Microsystems, Inc. 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.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
# @test
# @bug 6802846
# @summary jarsigner needs enhanced cert validation(options)
#
# @run shell concise_jarsigner.sh
#
if [ "${TESTJAVA}" = "" ] ; then
JAVAC_CMD=`which javac`
TESTJAVA=`dirname $JAVAC_CMD`/..
fi
# set platform-dependent variables
OS=`uname -s`
case "$OS" in
Windows_* )
FS="\\"
;;
* )
FS="/"
;;
esac
KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore js.jks"
JAR=$TESTJAVA${FS}bin${FS}jar
JARSIGNER=$TESTJAVA${FS}bin${FS}jarsigner
JAVAC=$TESTJAVA${FS}bin${FS}javac
rm js.jks
echo class A1 {} > A1.java
echo class A2 {} > A2.java
echo class A3 {} > A3.java
echo class A4 {} > A4.java
echo class A5 {} > A5.java
echo class A6 {} > A6.java
$JAVAC A1.java A2.java A3.java A4.java A5.java A6.java
YEAR=`date +%Y`
# ==========================================================
# First part: output format
# ==========================================================
$KT -genkeypair -alias a1 -dname CN=a1 -validity 365
$KT -genkeypair -alias a2 -dname CN=a2 -validity 365
# a.jar includes 8 unsigned, 2 signed by a1 and a2, 2 signed by a3
$JAR cvf a.jar A1.class A2.class
$JARSIGNER -keystore js.jks -storepass changeit a.jar a1
$JAR uvf a.jar A3.class A4.class
$JARSIGNER -keystore js.jks -storepass changeit a.jar a2
$JAR uvf a.jar A5.class A6.class
# Verify OK
$JARSIGNER -verify a.jar
[ $? = 0 ] || exit $LINENO
# 4(chainNotValidated)+16(hasUnsignedEntry)+32(aliasNotInStore)
$JARSIGNER -verify a.jar -strict
[ $? = 52 ] || exit $LINENO
# 16(hasUnsignedEntry)
$JARSIGNER -verify a.jar -strict -keystore js.jks
[ $? = 16 ] || exit $LINENO
# 16(hasUnsignedEntry)+32(notSignedByAlias)
$JARSIGNER -verify a.jar a1 -strict -keystore js.jks
[ $? = 48 ] || exit $LINENO
# 16(hasUnsignedEntry)
$JARSIGNER -verify a.jar a1 a2 -strict -keystore js.jks
[ $? = 16 ] || exit $LINENO
# 12 entries all together
LINES=`$JARSIGNER -verify a.jar -verbose | grep $YEAR | wc -l`
[ $LINES = 12 ] || exit $LINENO
# 12 entries all listed
LINES=`$JARSIGNER -verify a.jar -verbose:grouped | grep $YEAR | wc -l`
[ $LINES = 12 ] || exit $LINENO
# 3 groups: unrelated, signed, unsigned
LINES=`$JARSIGNER -verify a.jar -verbose:summary | grep $YEAR | wc -l`
[ $LINES = 3 ] || exit $LINENO
# 4 groups: unrelated, signed by a1/a2, signed by a2, unsigned
LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep $YEAR | wc -l`
[ $LINES = 4 ] || exit $LINENO
# 2*2 for A1/A2, 2 for A3/A4
LINES=`$JARSIGNER -verify a.jar -verbose -certs | grep "\[certificate" | wc -l`
[ $LINES = 6 ] || exit $LINENO
# a1,a2 for A1/A2, a2 for A3/A4
LINES=`$JARSIGNER -verify a.jar -verbose:grouped -certs | grep "\[certificate" | wc -l`
[ $LINES = 3 ] || exit $LINENO
# a1,a2 for A1/A2, a2 for A3/A4
LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep "\[certificate" | wc -l`
[ $LINES = 3 ] || exit $LINENO
# 4 groups
LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep "more)" | wc -l`
[ $LINES = 4 ] || exit $LINENO
# ==========================================================
# Second part: exit code 2, 4, 8
# 16 and 32 already covered in the first part
# ==========================================================
$KT -genkeypair -alias expiring -dname CN=expiring -startdate -1m
$KT -genkeypair -alias expired -dname CN=expired -startdate -10m
$KT -genkeypair -alias notyetvalid -dname CN=notyetvalid -startdate +1m
$KT -genkeypair -alias badku -dname CN=badku -ext KU=cRLSign -validity 365
$KT -genkeypair -alias badeku -dname CN=badeku -ext EKU=sa -validity 365
$KT -genkeypair -alias goodku -dname CN=goodku -ext KU=dig -validity 365
$KT -genkeypair -alias goodeku -dname CN=goodeku -ext EKU=codesign -validity 365
# badchain signed by ca, but ca is removed later
$KT -genkeypair -alias badchain -dname CN=badchain -validity 365
$KT -genkeypair -alias ca -dname CN=ca -ext bc -validity 365
$KT -certreq -alias badchain | $KT -gencert -alias ca -validity 365 | \
$KT -importcert -alias badchain
$KT -delete -alias ca
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expiring
[ $? = 2 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expired
[ $? = 4 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar notyetvalid
[ $? = 4 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badku
[ $? = 8 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badeku
[ $? = 8 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar goodku
[ $? = 0 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar goodeku
[ $? = 0 ] || exit $LINENO
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badchain
[ $? = 4 ] || exit $LINENO
$JARSIGNER -verify a.jar
[ $? = 0 ] || exit $LINENO
# ==========================================================
# Third part: -certchain test
# ==========================================================
# altchain signed by ca2, but ca2 is removed later
$KT -genkeypair -alias altchain -dname CN=altchain -validity 365
$KT -genkeypair -alias ca2 -dname CN=ca2 -ext bc -validity 365
$KT -certreq -alias altchain | $KT -gencert -alias ca2 -validity 365 -rfc > certchain
$KT -exportcert -alias ca2 -rfc >> certchain
$KT -delete -alias ca2
# Now altchain is still self-signed
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar altchain
[ $? = 0 ] || exit $LINENO
# If -certchain is used, then it's bad
$JARSIGNER -strict -keystore js.jks -storepass changeit -certchain certchain a.jar altchain
[ $? = 4 ] || exit $LINENO
$JARSIGNER -verify a.jar
[ $? = 0 ] || exit $LINENO
echo OK
exit 0
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册