< prev index next >
src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
Print this page
rev 15874 : 8163304: jarsigner -verbose -verify should print the algorithms used to sign the jar
@@ -48,10 +48,13 @@
import java.security.cert.TrustAnchor;
import java.util.Map.Entry;
import jdk.security.jarsigner.JarSigner;
import jdk.security.jarsigner.JarSignerException;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.timestamp.TimestampToken;
import sun.security.tools.KeyStoreUtil;
import sun.security.x509.*;
import sun.security.util.*;
@@ -85,10 +88,19 @@
private static final String NONE = "NONE";
private static final String P11KEYSTORE = "PKCS11";
private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
+ private static final DisabledAlgorithmConstraints DISABLED_CHECK =
+ new DisabledAlgorithmConstraints(
+ DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
+
+ private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections
+ .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
+ private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
+ .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
// Attention:
// This is the entry that get launched by the security tool jarsigner.
public static void main(String args[]) throws Exception {
Main js = new Main();
js.run(args);
@@ -161,10 +173,12 @@
private boolean badNetscapeCertType = false;
private boolean signerSelfSigned = false;
private Throwable chainNotValidatedReason = null;
+ private boolean seeWeak = false;
+
CertificateFactory certificateFactory;
CertPathValidator validator;
PKIXParameters pkixParameters;
public void run(String args[]) {
@@ -626,30 +640,62 @@
void verifyJar(String jarName)
throws Exception
{
boolean anySigned = false; // if there exists entry inside jar signed
JarFile jf = null;
+ Map<String,String> digestMap = new HashMap<>();
+ Map<String,PKCS7> sigMap = new HashMap<>();
+ Map<String,String> sigNameMap = new HashMap<>();
+ Map<String,String> unparsableSignatures = new HashMap<>();
try {
jf = new JarFile(jarName, true);
Vector<JarEntry> entriesVec = new Vector<>();
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);
+ try (InputStream is = jf.getInputStream(je)) {
+ String name = je.getName();
+ if (signatureRelated(name)
+ && SignatureFileVerifier.isBlockOrSF(name)) {
+ String alias = name.substring(name.lastIndexOf('/') + 1,
+ name.lastIndexOf('.'));
+ try {
+ if (name.endsWith(".SF")) {
+ Manifest sf = new Manifest(is);
+ boolean found = false;
+ for (Object obj : sf.getMainAttributes().keySet()) {
+ String key = obj.toString();
+ if (key.endsWith("-Digest-Manifest")) {
+ digestMap.put(alias,
+ key.substring(0, key.length() - 16));
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ unparsableSignatures.putIfAbsent(alias,
+ String.format(
+ rb.getString("history.unparsable"),
+ name));
+ }
+ } else {
+ sigNameMap.put(alias, name);
+ sigMap.put(alias, new PKCS7(is));
+ }
+ } catch (IOException ioe) {
+ unparsableSignatures.putIfAbsent(alias, String.format(
+ rb.getString("history.unparsable"), name));
+ }
+ } else {
while (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();
@@ -803,23 +849,115 @@
".k.at.least.one.certificate.was.found.in.keystore"));
if (ckaliases.size() > 0) {
System.out.println(rb.getString(
".X.not.signed.by.specified.alias.es."));
}
- System.out.println();
}
- if (man == null)
+ if (man == null) {
+ System.out.println();
System.out.println(rb.getString("no.manifest."));
+ }
// If signer is a trusted cert or private entry in user's own
// keystore, it can be self-signed.
if (!aliasNotInStore) {
signerSelfSigned = false;
}
+ // Even if the verbose option is not specified, all out strings
+ // must be generated so seeWeak can be updated.
+ if (!digestMap.isEmpty()
+ || !sigMap.isEmpty()
+ || !unparsableSignatures.isEmpty()) {
+ if (verbose != null) {
+ System.out.println();
+ }
+ for (String s : sigMap.keySet()) {
+ if (!digestMap.containsKey(s)) {
+ unparsableSignatures.putIfAbsent(s, String.format(
+ rb.getString("history.nosf"), s));
+ }
+ }
+ for (String s : digestMap.keySet()) {
+ PKCS7 p7 = sigMap.get(s);
+ if (p7 != null) {
+ String history;
+ try {
+ SignerInfo si = p7.getSignerInfos()[0];
+ X509Certificate signer = si.getCertificate(p7);
+ String digestAlg = digestMap.get(s);
+ String sigAlg = AlgorithmId.makeSigAlg(
+ si.getDigestAlgorithmId().getName(),
+ si.getDigestEncryptionAlgorithmId().getName());
+ PublicKey key = signer.getPublicKey();
+ PKCS7 tsToken = si.getTsToken();
+ if (tsToken != null) {
+ SignerInfo tsSi = tsToken.getSignerInfos()[0];
+ X509Certificate tsSigner = tsSi.getCertificate(tsToken);
+ byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
+ TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
+ PublicKey tsKey = tsSigner.getPublicKey();
+ String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
+ String tsSigAlg = AlgorithmId.makeSigAlg(
+ tsSi.getDigestAlgorithmId().getName(),
+ tsSi.getDigestEncryptionAlgorithmId().getName());
+ Calendar c = Calendar.getInstance(
+ TimeZone.getTimeZone("UTC"),
+ Locale.getDefault(Locale.Category.FORMAT));
+ c.setTime(tsTokenInfo.getDate());
+ history = String.format(
+ rb.getString("history.with.ts"),
+ signer.getSubjectX500Principal(),
+ withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
+ withWeak(sigAlg, SIG_PRIMITIVE_SET),
+ withWeak(key),
+ c,
+ tsSigner.getSubjectX500Principal(),
+ withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET),
+ withWeak(tsSigAlg, SIG_PRIMITIVE_SET),
+ withWeak(tsKey));
+ } else {
+ history = String.format(
+ rb.getString("history.without.ts"),
+ signer.getSubjectX500Principal(),
+ withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
+ withWeak(sigAlg, SIG_PRIMITIVE_SET),
+ withWeak(key));
+ }
+ } catch (Exception e) {
+ // The only usage of sigNameMap, remember the name
+ // of the block file if it's invalid.
+ history = String.format(
+ rb.getString("history.unparsable"),
+ sigNameMap.get(s));
+ }
+ if (verbose != null) {
+ System.out.println(history);
+ }
+ } else {
+ unparsableSignatures.putIfAbsent(s, String.format(
+ rb.getString("history.nobk"), s));
+ }
+ }
+ if (verbose != null) {
+ for (String s : unparsableSignatures.keySet()) {
+ System.out.println(unparsableSignatures.get(s));
+ }
+ }
+ }
+ System.out.println();
if (!anySigned) {
- if (hasSignature) {
+ if (seeWeak) {
+ if (verbose != null) {
+ System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
+ System.out.println("\n " +
+ DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +
+ "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));
+ } else {
+ System.out.println(rb.getString("jar.treated.unsigned.see.weak"));
+ }
+ } else if (hasSignature) {
System.out.println(rb.getString("jar.treated.unsigned"));
} else {
System.out.println(rb.getString("jar.is.unsigned"));
}
} else {
@@ -843,11 +981,13 @@
}
if (weakAlg != 0) {
// In fact, jarsigner verification did not catch this
// since it has not read the JarFile content itself.
- // Everything is done with JarFile API.
+ // Everything is done with JarFile API. The signing
+ // history (digestMap etc) will show these info and
+ // print out proper warnings.
}
if (badKeyUsage) {
System.out.println(
rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
@@ -934,10 +1074,30 @@
}
System.exit(1);
}
+ private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) {
+ if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
+ return alg;
+ } else {
+ seeWeak = true;
+ return String.format(rb.getString("with.weak"), alg);
+ }
+ }
+
+ private String withWeak(PublicKey key) {
+ if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
+ return String.format(
+ rb.getString("key.bit"), KeyUtil.getKeySize(key));
+ } else {
+ seeWeak = true;
+ return String.format(
+ rb.getString("key.bit.weak"), KeyUtil.getKeySize(key));
+ }
+ }
+
private static MessageFormat validityTimeForm = null;
private static MessageFormat notYetTimeForm = null;
private static MessageFormat expiredTimeForm = null;
private static MessageFormat expiringTimeForm = null;
@@ -1123,26 +1283,26 @@
}
void signJar(String jarName, String alias)
throws Exception {
- DisabledAlgorithmConstraints dac =
- new DisabledAlgorithmConstraints(
- DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
-
- if (digestalg != null && !dac.permits(
- Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), digestalg, null)) {
+ if (digestalg != null && !DISABLED_CHECK.permits(
+ DIGEST_PRIMITIVE_SET, digestalg, null)) {
weakAlg |= 1;
}
- if (tSADigestAlg != null && !dac.permits(
- Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), tSADigestAlg, null)) {
+ if (tSADigestAlg != null && !DISABLED_CHECK.permits(
+ DIGEST_PRIMITIVE_SET, tSADigestAlg, null)) {
weakAlg |= 4;
}
- if (sigalg != null && !dac.permits(
- Collections.singleton(CryptoPrimitive.SIGNATURE), sigalg, null)) {
+ if (sigalg != null && !DISABLED_CHECK.permits(
+ SIG_PRIMITIVE_SET , sigalg, null)) {
weakAlg |= 2;
}
+ if (!DISABLED_CHECK.permits(
+ SIG_PRIMITIVE_SET, privateKey)) {
+ weakAlg |= 8;
+ }
boolean aliasUsed = false;
X509Certificate tsaCert = null;
if (sigfile == null) {
@@ -1383,10 +1543,15 @@
if ((weakAlg & 4) == 4) {
System.out.println(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
tSADigestAlg, "-tsadigestalg"));
}
+ if ((weakAlg & 8) == 8) {
+ System.out.println(String.format(
+ rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."),
+ privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
+ }
} else {
System.out.println(rb.getString("jar.signed."));
}
if (hasExpiringCert || noTimestamp) {
if (!warningAppeared) {
< prev index next >