--- a/jdk/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Wed Feb 08 12:27:45 2017 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java Wed Feb 08 12:08:28 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -28,25 +28,23 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.CodeSigner;
-import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
+import java.security.Timestamp;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.Base64;
-import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarException;
import java.util.jar.JarFile;
@@ -61,9 +59,6 @@
/* Are we debugging ? */
private static final Debug debug = Debug.getInstance("jar");
- private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET =
- Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
-
private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
new DisabledAlgorithmConstraints(
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
@@ -97,6 +92,14 @@
/* for generating certpath objects */
private CertificateFactory certificateFactory = null;
+ /** Algorithms that have been checked if they are weak. */
+ private Map<String, Boolean> permittedAlgs= new HashMap<>();
+
+ /** TSA timestamp of signed jar. The newest timestamp is used. If there
+ * was no TSA timestamp used when signed, current time is used ("null").
+ */
+ private Timestamp timestamp = null;
+
/**
* Create the named SignatureFileVerifier.
*
@@ -222,15 +225,8 @@
/** get digest from cache */
- private MessageDigest getDigest(String algorithm) throws SignatureException {
- // check that algorithm is not restricted
- if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, algorithm, null)) {
- SignatureException e =
- new SignatureException("SignatureFile check failed. " +
- "Disabled algorithm used: " + algorithm);
- throw e;
- }
-
+ private MessageDigest getDigest(String algorithm)
+ throws SignatureException {
if (createdDigests == null)
createdDigests = new HashMap<>();
@@ -302,6 +298,27 @@
if (newSigners == null)
return;
+ /*
+ * Look for the latest timestamp in the signature block. If an entry
+ * has no timestamp, use current time (aka null).
+ */
+ for (CodeSigner s: newSigners) {
+ if (debug != null) {
+ debug.println("Gathering timestamp for: " + s.toString());
+ }
+ if (s.getTimestamp() == null) {
+ timestamp = null;
+ break;
+ } else if (timestamp == null) {
+ timestamp = s.getTimestamp();
+ } else {
+ if (timestamp.getTimestamp().before(
+ s.getTimestamp().getTimestamp())) {
+ timestamp = s.getTimestamp();
+ }
+ }
+ }
+
Iterator<Map.Entry<String,Attributes>> entries =
sf.getEntries().entrySet().iterator();
@@ -345,6 +362,68 @@
}
/**
+ * Check if algorithm is permitted using the permittedAlgs Map.
+ * If the algorithm is not in the map, check against disabled algorithms and
+ * store the result. If the algorithm is in the map use that result.
+ * False is returned for weak algorithm, true for good algorithms.
+ */
+ boolean permittedCheck(String key, String algorithm) {
+ Boolean permitted = permittedAlgs.get(algorithm);
+ if (permitted == null) {
+ try {
+ JAR_DISABLED_CHECK.permits(algorithm,
+ new ConstraintsParameters(timestamp));
+ } catch(GeneralSecurityException e) {
+ permittedAlgs.put(algorithm, Boolean.FALSE);
+ permittedAlgs.put(key.toUpperCase(), Boolean.FALSE);
+ if (debug != null) {
+ if (e.getMessage() != null) {
+ debug.println(key + ": " + e.getMessage());
+ } else {
+ debug.println(key + ": " + algorithm +
+ " was disabled, no exception msg given.");
+ e.printStackTrace();
+ }
+ }
+ return false;
+ }
+
+ permittedAlgs.put(algorithm, Boolean.TRUE);
+ return true;
+ }
+
+ // Algorithm has already been checked, return the value from map.
+ return permitted.booleanValue();
+ }
+
+ /**
+ * With a given header (*-DIGEST*), return a string that lists all the
+ * algorithms associated with the header.
+ * If there are none, return "Unknown Algorithm".
+ */
+ String getWeakAlgorithms(String header) {
+ String w = "";
+ try {
+ for (String key : permittedAlgs.keySet()) {
+ if (key.endsWith(header)) {
+ w += key.substring(0, key.length() - header.length()) + " ";
+ }
+ }
+ } catch (RuntimeException e) {
+ w = "Unknown Algorithm(s). Error processing " + header + ". " +
+ e.getMessage();
+ }
+
+ // This means we have an error in finding weak algorithms, run in
+ // debug mode to see permittedAlgs map's values.
+ if (w.length() == 0) {
+ return "Unknown Algorithm(s)";
+ }
+
+ return w;
+ }
+
+ /**
* See if the whole manifest was signed.
*/
private boolean verifyManifestHash(Manifest sf,
@@ -354,6 +433,7 @@
{
Attributes mattr = sf.getMainAttributes();
boolean manifestSigned = false;
+ boolean weakAlgs = true;
// go through all the attributes and process *-Digest-Manifest entries
for (Map.Entry<Object,Object> se : mattr.entrySet()) {
@@ -364,6 +444,15 @@
// 16 is length of "-Digest-Manifest"
String algorithm = key.substring(0, key.length()-16);
+ // Check if this algorithm is permitted, skip if false.
+ if (!permittedCheck(key, algorithm)) {
+ continue;
+ }
+
+ // A non-weak algorithm was used, any weak algorithms found do
+ // not need to be reported.
+ weakAlgs = false;
+
manifestDigests.add(key);
manifestDigests.add(se.getValue());
MessageDigest digest = getDigest(algorithm);
@@ -373,15 +462,14 @@
Base64.getMimeDecoder().decode((String)se.getValue());
if (debug != null) {
- debug.println("Signature File: Manifest digest " +
- digest.getAlgorithm());
- debug.println( " sigfile " + toHex(expectedHash));
- debug.println( " computed " + toHex(computedHash));
- debug.println();
+ debug.println("Signature File: Manifest digest " +
+ algorithm);
+ debug.println( " sigfile " + toHex(expectedHash));
+ debug.println( " computed " + toHex(computedHash));
+ debug.println();
}
- if (MessageDigest.isEqual(computedHash,
- expectedHash)) {
+ if (MessageDigest.isEqual(computedHash, expectedHash)) {
manifestSigned = true;
} else {
//XXX: we will continue and verify each section
@@ -389,15 +477,31 @@
}
}
}
+
+ if (debug != null) {
+ debug.println("PermittedAlgs mapping: ");
+ for (String key : permittedAlgs.keySet()) {
+ debug.println(key + " : " +
+ permittedAlgs.get(key).toString());
+ }
+ }
+
+ // If there were only weak algorithms used, throw an exception.
+ if (weakAlgs) {
+ String weakAlgorithms = getWeakAlgorithms("-DIGEST-MANIFEST");
+ throw new SignatureException("Manifest hash check failed " +
+ "(DIGEST-MANIFEST). Disabled algorithm(s) used: " +
+ weakAlgorithms);
+ }
return manifestSigned;
}
- private boolean verifyManifestMainAttrs(Manifest sf,
- ManifestDigester md)
+ private boolean verifyManifestMainAttrs(Manifest sf, ManifestDigester md)
throws IOException, SignatureException
{
Attributes mattr = sf.getMainAttributes();
boolean attrsVerified = true;
+ boolean weakAlgs = true;
// go through all the attributes and process
// digest entries for the manifest main attributes
@@ -408,6 +512,15 @@
String algorithm =
key.substring(0, key.length() - ATTR_DIGEST.length());
+ // Check if this algorithm is permitted, skip if false.
+ if (!permittedCheck(key, algorithm)) {
+ continue;
+ }
+
+ // A non-weak algorithm was used, any weak algorithms found do
+ // not need to be reported.
+ weakAlgs = false;
+
MessageDigest digest = getDigest(algorithm);
if (digest != null) {
ManifestDigester.Entry mde =
@@ -425,8 +538,7 @@
debug.println();
}
- if (MessageDigest.isEqual(computedHash,
- expectedHash)) {
+ if (MessageDigest.isEqual(computedHash, expectedHash)) {
// good
} else {
// we will *not* continue and verify each section
@@ -442,6 +554,23 @@
}
}
+ if (debug != null) {
+ debug.println("PermittedAlgs mapping: ");
+ for (String key : permittedAlgs.keySet()) {
+ debug.println(key + " : " +
+ permittedAlgs.get(key).toString());
+ }
+ }
+
+ // If there were only weak algorithms used, throw an exception.
+ if (weakAlgs) {
+ String weakAlgorithms = getWeakAlgorithms("-DIGEST-" +
+ ManifestDigester.MF_MAIN_ATTRS);
+ throw new SignatureException("Manifest Main Attribute check " +
+ "failed (DIGEST-" + ManifestDigester.MF_MAIN_ATTRS +
+ "). " + "Disabled algorithm(s) used: " + weakAlgorithms);
+ }
+
// this method returns 'true' if either:
// . manifest main attributes were not signed, or
// . manifest main attributes were signed and verified
@@ -464,6 +593,7 @@
{
boolean oneDigestVerified = false;
ManifestDigester.Entry mde = md.get(name,block.isOldStyle());
+ boolean weakAlgs = true;
if (mde == null) {
throw new SecurityException(
@@ -471,7 +601,6 @@
}
if (sfAttr != null) {
-
//sun.security.util.HexDumpEncoder hex = new sun.security.util.HexDumpEncoder();
//hex.encodeBuffer(data, System.out);
@@ -483,6 +612,15 @@
// 7 is length of "-Digest"
String algorithm = key.substring(0, key.length()-7);
+ // Check if this algorithm is permitted, skip if false.
+ if (!permittedCheck(key, algorithm)) {
+ continue;
+ }
+
+ // A non-weak algorithm was used, any weak algorithms found do
+ // not need to be reported.
+ weakAlgs = false;
+
MessageDigest digest = getDigest(algorithm);
if (digest != null) {
@@ -532,6 +670,23 @@
}
}
}
+
+ if (debug != null) {
+ debug.println("PermittedAlgs mapping: ");
+ for (String key : permittedAlgs.keySet()) {
+ debug.println(key + " : " +
+ permittedAlgs.get(key).toString());
+ }
+ }
+
+ // If there were only weak algorithms used, throw an exception.
+ if (weakAlgs) {
+ String weakAlgorithms = getWeakAlgorithms("DIGEST");
+ throw new SignatureException("Manifest Main Attribute check " +
+ "failed (DIGEST). " + "Disabled algorithm(s) used: " +
+ weakAlgorithms);
+ }
+
return oneDigestVerified;
}