8249906: Enhance opening JARs

Reviewed-by: weijun, rhalade, mschoene
This commit is contained in:
Sean Mullan 2020-10-26 18:55:15 +00:00 committed by Henry Jen
parent 17a741d6bc
commit 7232e3c704
24 changed files with 766 additions and 502 deletions

View file

@ -36,6 +36,7 @@ import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import sun.security.x509.X509CertImpl;
/**
@ -47,9 +48,10 @@ public class AnchorCertificates {
private static final Debug debug = Debug.getInstance("certpath");
private static final String HASH = "SHA-256";
private static Set<String> certs = Collections.emptySet();
private static Set<X500Principal> certIssuers = Collections.emptySet();
static {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
File f = new File(FilePaths.cacerts());
@ -59,15 +61,16 @@ public class AnchorCertificates {
try (FileInputStream fis = new FileInputStream(f)) {
cacerts.load(fis, null);
certs = new HashSet<>();
certIssuers = new HashSet<>();
Enumeration<String> list = cacerts.aliases();
String alias;
while (list.hasMoreElements()) {
alias = list.nextElement();
String alias = list.nextElement();
// Check if this cert is labeled a trust anchor.
if (alias.contains(" [jdk")) {
X509Certificate cert = (X509Certificate) cacerts
.getCertificate(alias);
certs.add(X509CertImpl.getFingerprint(HASH, cert));
certIssuers.add(cert.getSubjectX500Principal());
}
}
}
@ -83,10 +86,10 @@ public class AnchorCertificates {
}
/**
* Checks if a certificate is a trust anchor.
* Checks if a certificate is a JDK trust anchor.
*
* @param cert the certificate to check
* @return true if the certificate is trusted.
* @return true if the certificate is a JDK trust anchor
*/
public static boolean contains(X509Certificate cert) {
String key = X509CertImpl.getFingerprint(HASH, cert);
@ -98,5 +101,15 @@ public class AnchorCertificates {
return result;
}
/**
* Checks if a JDK trust anchor is the issuer of a certificate.
*
* @param cert the certificate to check
* @return true if the certificate is issued by a trust anchor
*/
public static boolean issuerOf(X509Certificate cert) {
return certIssuers.contains(cert.getIssuerX500Principal());
}
private AnchorCertificates() {}
}

View file

@ -25,167 +25,42 @@
package sun.security.util;
import sun.security.validator.Validator;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Timestamp;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECKey;
import java.security.interfaces.XECKey;
import java.security.spec.NamedParameterSpec;
import java.util.Date;
import java.util.Set;
/**
* This class contains parameters for checking against constraints that extend
* past the publicly available parameters in java.security.AlgorithmConstraints.
*
* This is currently passed between PKIX, AlgorithmChecker,
* and DisabledAlgorithmConstraints.
* This interface contains parameters for checking against constraints that
* extend past the publicly available parameters in
* java.security.AlgorithmConstraints.
*/
public class ConstraintsParameters {
/*
* The below 3 values are used the same as the permit() methods
* published in java.security.AlgorithmConstraints.
public interface ConstraintsParameters {
/**
* Returns true if a certificate chains back to a trusted JDK root CA.
*/
// Algorithm string to be checked against constraints
private final String algorithm;
// AlgorithmParameters to the algorithm being checked
private final AlgorithmParameters algParams;
// Key being checked against constraints
private final Key key;
boolean anchorIsJdkCA();
/*
* New values that are checked against constraints that the current public
* API does not support.
/**
* Returns the set of keys that should be checked against the
* constraints, or an empty set if there are no keys to be checked.
*/
// A certificate being passed to check against constraints.
private final X509Certificate cert;
// This is true if the trust anchor in the certificate chain matches a cert
// in AnchorCertificates
private final boolean trustedMatch;
// PKIXParameter date
private final Date pkixDate;
// Timestamp of the signed JAR file
private final Timestamp jarTimestamp;
private final String variant;
// Named Curve
private final String[] curveStr;
private static final String[] EMPTYLIST = new String[0];
Set<Key> getKeys();
public ConstraintsParameters(X509Certificate c, boolean match,
Date pkixdate, Timestamp jarTime, String variant) {
cert = c;
trustedMatch = match;
pkixDate = pkixdate;
jarTimestamp = jarTime;
this.variant = (variant == null ? Validator.VAR_GENERIC : variant);
algorithm = null;
algParams = null;
key = null;
if (c != null) {
curveStr = getNamedCurveFromKey(c.getPublicKey());
} else {
curveStr = EMPTYLIST;
}
}
/**
* Returns the date that should be checked against the constraints, or
* null if not set.
*/
Date getDate();
public ConstraintsParameters(String algorithm, AlgorithmParameters params,
Key key, String variant) {
this.algorithm = algorithm;
algParams = params;
this.key = key;
curveStr = getNamedCurveFromKey(key);
cert = null;
trustedMatch = false;
pkixDate = null;
jarTimestamp = null;
this.variant = (variant == null ? Validator.VAR_GENERIC : variant);
}
public ConstraintsParameters(X509Certificate c) {
this(c, false, null, null,
Validator.VAR_GENERIC);
}
public ConstraintsParameters(Timestamp jarTime) {
this(null, false, null, jarTime, Validator.VAR_GENERIC);
}
public String getAlgorithm() {
return algorithm;
}
public AlgorithmParameters getAlgParams() {
return algParams;
}
public Key getKey() {
return key;
}
// Returns if the trust anchor has a match if anchor checking is enabled.
public boolean isTrustedMatch() {
return trustedMatch;
}
public X509Certificate getCertificate() {
return cert;
}
public Date getPKIXParamDate() {
return pkixDate;
}
public Timestamp getJARTimestamp() {
return jarTimestamp;
}
public String getVariant() {
return variant;
}
public String[] getNamedCurve() {
return curveStr;
}
public static String[] getNamedCurveFromKey(Key key) {
if (key instanceof ECKey) {
NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams());
return (nc == null ? EMPTYLIST : nc.getNameAndAliases());
} else if (key instanceof XECKey) {
String[] s = {
((NamedParameterSpec)((XECKey)key).getParams()).getName()
};
return s;
} else {
return EMPTYLIST;
}
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("Cert: ");
if (cert != null) {
s.append(cert.toString());
s.append("\nSigAlgo: ");
s.append(cert.getSigAlgName());
} else {
s.append("None");
}
s.append("\nAlgParams: ");
if (getAlgParams() != null) {
getAlgParams().toString();
} else {
s.append("None");
}
s.append("\nNamedCurves: ");
for (String c : getNamedCurve()) {
s.append(c + " ");
}
s.append("\nVariant: " + getVariant());
return s.toString();
}
/**
* Returns the Validator variant.
*/
String getVariant();
/**
* Returns an extended message used in exceptions. See
* DisabledAlgorithmConstraints for usage.
*/
String extendedExceptionMsg();
}

View file

@ -27,12 +27,18 @@ package sun.security.util;
import sun.security.validator.Validator;
import java.security.CryptoPrimitive;
import java.security.AlgorithmParameters;
import java.security.CryptoPrimitive;
import java.security.Key;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECKey;
import java.security.interfaces.XECKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.NamedParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -79,9 +85,27 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
private static final String PROPERTY_DISABLED_EC_CURVES =
"jdk.disabled.namedCurves";
private static class CertPathHolder {
static final DisabledAlgorithmConstraints CONSTRAINTS =
new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS);
}
private static class JarHolder {
static final DisabledAlgorithmConstraints CONSTRAINTS =
new DisabledAlgorithmConstraints(PROPERTY_JAR_DISABLED_ALGS);
}
private final List<String> disabledAlgorithms;
private final Constraints algorithmConstraints;
public static DisabledAlgorithmConstraints certPathConstraints() {
return CertPathHolder.CONSTRAINTS;
}
public static DisabledAlgorithmConstraints jarConstraints() {
return JarHolder.CONSTRAINTS;
}
/**
* Initialize algorithm constraints with the specified security property.
*
@ -122,7 +146,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
disabledAlgorithms.addAll(ecindex,
getAlgorithms(PROPERTY_DISABLED_EC_CURVES));
}
algorithmConstraints = new Constraints(disabledAlgorithms);
algorithmConstraints = new Constraints(propertyName, disabledAlgorithms);
}
/*
@ -172,32 +196,54 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
return checkConstraints(primitives, algorithm, key, parameters);
}
public final void permits(ConstraintsParameters cp)
throws CertPathValidatorException {
permits(cp.getAlgorithm(), cp);
public final void permits(String algorithm, AlgorithmParameters ap,
ConstraintsParameters cp) throws CertPathValidatorException {
permits(algorithm, cp);
if (ap != null) {
permits(ap, cp);
}
}
public final void permits(String algorithm, Key key,
AlgorithmParameters params, String variant)
throws CertPathValidatorException {
permits(algorithm, new ConstraintsParameters(algorithm, params, key,
(variant == null) ? Validator.VAR_GENERIC : variant));
private void permits(AlgorithmParameters ap, ConstraintsParameters cp)
throws CertPathValidatorException {
switch (ap.getAlgorithm().toUpperCase(Locale.ENGLISH)) {
case "RSASSA-PSS":
permitsPSSParams(ap, cp);
break;
default:
// unknown algorithm, just ignore
}
}
/*
* Check if a x509Certificate object is permitted. Check if all
* algorithms are allowed, certificate constraints, and the
* public key against key constraints.
*
* Uses new style permit() which throws exceptions.
*/
private void permitsPSSParams(AlgorithmParameters ap,
ConstraintsParameters cp) throws CertPathValidatorException {
try {
PSSParameterSpec pssParams =
ap.getParameterSpec(PSSParameterSpec.class);
String digestAlg = pssParams.getDigestAlgorithm();
permits(digestAlg, cp);
AlgorithmParameterSpec mgfParams = pssParams.getMGFParameters();
if (mgfParams instanceof MGF1ParameterSpec) {
String mgfDigestAlg =
((MGF1ParameterSpec)mgfParams).getDigestAlgorithm();
if (!mgfDigestAlg.equalsIgnoreCase(digestAlg)) {
permits(mgfDigestAlg, cp);
}
}
} catch (InvalidParameterSpecException ipse) {
// ignore
}
}
public final void permits(String algorithm, ConstraintsParameters cp)
throws CertPathValidatorException {
// Check if named curves in the ConstraintParameters are disabled.
if (cp.getNamedCurve() != null) {
for (String curve : cp.getNamedCurve()) {
// Check if named curves in the key are disabled.
for (Key key : cp.getKeys()) {
for (String curve : getNamedCurveFromKey(key)) {
if (!checkAlgorithm(disabledAlgorithms, curve, decomposer)) {
throw new CertPathValidatorException(
"Algorithm constraints check failed on disabled " +
@ -210,15 +256,17 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
algorithmConstraints.permits(algorithm, cp);
}
// Check if a string is contained inside the property
public boolean checkProperty(String param) {
param = param.toLowerCase(Locale.ENGLISH);
for (String block : disabledAlgorithms) {
if (block.toLowerCase(Locale.ENGLISH).indexOf(param) >= 0) {
return true;
}
private static List<String> getNamedCurveFromKey(Key key) {
if (key instanceof ECKey) {
NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams());
return (nc == null ? List.of()
: Arrays.asList(nc.getNameAndAliases()));
} else if (key instanceof XECKey) {
return List.of(
((NamedParameterSpec)((XECKey)key).getParams()).getName());
} else {
return List.of();
}
return false;
}
// Check algorithm constraints with key and algorithm
@ -246,8 +294,8 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
return false;
}
// If this is an elliptic curve, check disabled the named curve.
for (String curve : ConstraintsParameters.getNamedCurveFromKey(key)) {
// If this is an elliptic curve, check if it is disabled
for (String curve : getNamedCurveFromKey(key)) {
if (!permits(primitives, curve, null)) {
return false;
}
@ -284,7 +332,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
"denyAfter\\s+(\\d{4})-(\\d{2})-(\\d{2})");
}
public Constraints(List<String> constraintArray) {
public Constraints(String propertyName, List<String> constraintArray) {
for (String constraintEntry : constraintArray) {
if (constraintEntry == null || constraintEntry.isEmpty()) {
continue;
@ -399,7 +447,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
}
}
// Get applicable constraints based off the signature algorithm
// Get applicable constraints based off the algorithm
private List<Constraint> getConstraints(String algorithm) {
return constraintsMap.get(algorithm.toUpperCase(Locale.ENGLISH));
}
@ -443,13 +491,12 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
return true;
}
// Check if constraints permit this cert.
public void permits(String algorithm, ConstraintsParameters cp)
throws CertPathValidatorException {
X509Certificate cert = cp.getCertificate();
if (debug != null) {
debug.println("Constraints.permits(): " + cp.toString());
debug.println("Constraints.permits(): " + algorithm + ", "
+ cp.toString());
}
// Get all signature algorithms to check for constraints
@ -459,13 +506,10 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
algorithms.add(algorithm);
}
// Attempt to add the public key algorithm if cert provided
if (cert != null) {
algorithms.add(cert.getPublicKey().getAlgorithm());
}
if (cp.getKey() != null) {
algorithms.add(cp.getKey().getAlgorithm());
for (Key key : cp.getKeys()) {
algorithms.add(key.getAlgorithm());
}
// Check all applicable constraints
for (String alg : algorithms) {
List<Constraint> list = getConstraints(alg);
@ -556,7 +600,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
* {@code next()} with the same {@code ConstraintsParameters}
* parameter passed if multiple constraints need to be checked.
*
* @param cp CertConstraintParameter containing certificate info
* @param cp ConstraintsParameter containing certificate info
* @throws CertPathValidatorException if constraint disallows.
*
*/
@ -605,14 +649,6 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
boolean next(Key key) {
return nextConstraint != null && nextConstraint.permits(key);
}
String extendedMsg(ConstraintsParameters cp) {
return (cp.getCertificate() == null ? "." :
" used with certificate: " +
cp.getCertificate().getSubjectX500Principal() +
(cp.getVariant() != Validator.VAR_GENERIC ?
". Usage was " + cp.getVariant() : "."));
}
}
/*
@ -636,14 +672,15 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
debug.println("jdkCAConstraints.permits(): " + algorithm);
}
// Check chain has a trust anchor in cacerts
if (cp.isTrustedMatch()) {
// Check if any certs chain back to at least one trust anchor in
// cacerts
if (cp.anchorIsJdkCA()) {
if (next(cp)) {
return;
}
throw new CertPathValidatorException(
"Algorithm constraints check failed on certificate " +
"anchor limits. " + algorithm + extendedMsg(cp),
"anchor limits. " + algorithm + cp.extendedExceptionMsg(),
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
}
@ -708,15 +745,10 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
Date currentDate;
String errmsg;
if (cp.getJARTimestamp() != null) {
currentDate = cp.getJARTimestamp().getTimestamp();
errmsg = "JAR Timestamp date: ";
} else if (cp.getPKIXParamDate() != null) {
currentDate = cp.getPKIXParamDate();
errmsg = "PKIXParameter date: ";
if (cp.getDate() != null) {
currentDate = cp.getDate();
} else {
currentDate = new Date();
errmsg = "Current date: ";
}
if (!denyAfterDate.after(currentDate)) {
@ -726,8 +758,8 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
throw new CertPathValidatorException(
"denyAfter constraint check failed: " + algorithm +
" used with Constraint date: " +
dateFormat.format(denyAfterDate) + "; " + errmsg +
dateFormat.format(currentDate) + extendedMsg(cp),
dateFormat.format(denyAfterDate) + "; params date: " +
dateFormat.format(currentDate) + cp.extendedExceptionMsg(),
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
}
@ -764,19 +796,27 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
@Override
public void permits(ConstraintsParameters cp)
throws CertPathValidatorException {
String variant = cp.getVariant();
for (String usage : usages) {
String v = null;
if (usage.compareToIgnoreCase("TLSServer") == 0) {
v = Validator.VAR_TLS_SERVER;
} else if (usage.compareToIgnoreCase("TLSClient") == 0) {
v = Validator.VAR_TLS_CLIENT;
} else if (usage.compareToIgnoreCase("SignedJAR") == 0) {
v = Validator.VAR_PLUGIN_CODE_SIGNING;
boolean match = false;
switch (usage.toLowerCase()) {
case "tlsserver":
match = variant.equals(Validator.VAR_TLS_SERVER);
break;
case "tlsclient":
match = variant.equals(Validator.VAR_TLS_CLIENT);
break;
case "signedjar":
match =
variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING) ||
variant.equals(Validator.VAR_CODE_SIGNING) ||
variant.equals(Validator.VAR_TSA_SERVER);
break;
}
if (debug != null) {
debug.println("Checking if usage constraint \"" + v +
debug.println("Checking if usage constraint \"" + usage +
"\" matches \"" + cp.getVariant() + "\"");
if (Debug.isVerbose()) {
// Because usage checking can come from many places
@ -784,13 +824,13 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
(new Exception()).printStackTrace(debug.getPrintStream());
}
}
if (cp.getVariant().compareTo(v) == 0) {
if (match) {
if (next(cp)) {
return;
}
throw new CertPathValidatorException("Usage constraint " +
usage + " check failed: " + algorithm +
extendedMsg(cp),
cp.extendedExceptionMsg(),
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
}
@ -843,31 +883,25 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
}
/*
* If we are passed a certificate, extract the public key and use it.
*
* Check if each constraint fails and check if there is a linked
* constraint Any permitted constraint will exit the linked list
* to allow the operation.
* For each key, check if each constraint fails and check if there is
* a linked constraint. Any permitted constraint will exit the linked
* list to allow the operation.
*/
@Override
public void permits(ConstraintsParameters cp)
throws CertPathValidatorException {
Key key = null;
if (cp.getKey() != null) {
key = cp.getKey();
} else if (cp.getCertificate() != null) {
key = cp.getCertificate().getPublicKey();
}
if (key != null && !permitsImpl(key)) {
if (nextConstraint != null) {
nextConstraint.permits(cp);
return;
}
throw new CertPathValidatorException(
"Algorithm constraints check failed on keysize limits. " +
algorithm + " " + KeyUtil.getKeySize(key) + "bit key" +
extendedMsg(cp),
for (Key key : cp.getKeys()) {
if (!permitsImpl(key)) {
if (nextConstraint != null) {
nextConstraint.permits(cp);
continue;
}
throw new CertPathValidatorException(
"Algorithm constraints check failed on keysize limits: " +
algorithm + " " + KeyUtil.getKeySize(key) + " bit key" +
cp.extendedExceptionMsg(),
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
}
}
@ -944,7 +978,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
throws CertPathValidatorException {
throw new CertPathValidatorException(
"Algorithm constraints check failed on disabled " +
"algorithm: " + algorithm + extendedMsg(cp),
"algorithm: " + algorithm + cp.extendedExceptionMsg(),
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2020, 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
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.util;
import java.security.CodeSigner;
import java.security.Key;
import java.security.Timestamp;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import sun.security.util.AnchorCertificates;
import sun.security.util.ConstraintsParameters;
import sun.security.validator.Validator;
/**
* This class contains parameters for checking signed JARs against
* constraints specified in the jdk.jar.disabledAlgorithms security
* property.
*/
public class JarConstraintsParameters implements ConstraintsParameters {
// true if chain is anchored by a JDK root CA
private boolean anchorIsJdkCA;
private boolean anchorIsJdkCASet;
// The timestamp of the signed JAR file, if timestamped
private Date timestamp;
// The keys of the signers
private final Set<Key> keys;
// The certs in the signers' chains that are issued by the trust anchor
private final Set<X509Certificate> certsIssuedByAnchor;
// The extended exception message
private String message;
/**
* Create a JarConstraintsParameters.
*
* @param signers the CodeSigners that signed the JAR
*/
public JarConstraintsParameters(CodeSigner[] signers) {
this.keys = new HashSet<>();
this.certsIssuedByAnchor = new HashSet<>();
Date latestTimestamp = null;
boolean skipTimestamp = false;
// Iterate over the signers and extract the keys, the latest
// timestamp, and the last certificate of each chain which can be
// used for checking if the signer's certificate chains back to a
// JDK root CA
for (CodeSigner signer : signers) {
init(signer.getSignerCertPath());
Timestamp timestamp = signer.getTimestamp();
if (timestamp == null) {
// this means one of the signers doesn't have a timestamp
// and the JAR should be treated as if it isn't timestamped
latestTimestamp = null;
skipTimestamp = true;
} else {
// add the key and last cert of TSA too
init(timestamp.getSignerCertPath());
if (!skipTimestamp) {
Date timestampDate = timestamp.getTimestamp();
if (latestTimestamp == null) {
latestTimestamp = timestampDate;
} else {
if (latestTimestamp.before(timestampDate)) {
latestTimestamp = timestampDate;
}
}
}
}
}
this.timestamp = latestTimestamp;
}
// extract last certificate and key from chain
private void init(CertPath cp) {
@SuppressWarnings("unchecked")
List<X509Certificate> chain =
(List<X509Certificate>)cp.getCertificates();
if (!chain.isEmpty()) {
this.certsIssuedByAnchor.add(chain.get(chain.size() - 1));
this.keys.add(chain.get(0).getPublicKey());
}
}
@Override
public String getVariant() {
return Validator.VAR_GENERIC;
}
/**
* Since loading the cacerts keystore can be an expensive operation,
* this is only performed if this method is called during a "jdkCA"
* constraints check of a disabled algorithm, and the result is cached.
*
* @return true if at least one of the certificates are issued by a
* JDK root CA
*/
@Override
public boolean anchorIsJdkCA() {
if (anchorIsJdkCASet) {
return anchorIsJdkCA;
}
for (X509Certificate cert : certsIssuedByAnchor) {
if (AnchorCertificates.issuerOf(cert)) {
anchorIsJdkCA = true;
break;
}
}
anchorIsJdkCASet = true;
return anchorIsJdkCA;
}
@Override
public Date getDate() {
return timestamp;
}
@Override
public Set<Key> getKeys() {
return keys;
}
/**
* Sets the extended error message. Note: this should be used
* carefully as it is specific to the attribute/entry/file being checked.
*
* @param file the name of the signature related file being verified
* @param target the attribute containing the algorithm that is being
* checked
*/
public void setExtendedExceptionMsg(String file, String target) {
message = " used" + (target != null ? " with " + target : "") +
" in " + file + " file.";
}
@Override
public String extendedExceptionMsg() {
return message;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[\n");
sb.append("\n Variant: ").append(getVariant());
sb.append("\n Certs Issued by Anchor:");
for (X509Certificate cert : certsIssuedByAnchor) {
sb.append("\n Cert Issuer: ")
.append(cert.getIssuerX500Principal());
sb.append("\n Cert Subject: ")
.append(cert.getSubjectX500Principal());
}
for (Key key : keys) {
sb.append("\n Key: ").append(key.getAlgorithm());
}
if (timestamp != null) {
sb.append("\n Timestamp: ").append(timestamp);
}
sb.append("\n]");
return sb.toString();
}
}

View file

@ -27,13 +27,12 @@ package sun.security.util;
import java.security.*;
import java.io.*;
import java.security.CodeSigner;
import java.util.*;
import java.util.jar.*;
import java.util.Base64;
import sun.security.jca.Providers;
import sun.security.util.DisabledAlgorithmConstraints;
import sun.security.util.JarConstraintsParameters;
/**
* This class is used to verify each entry in a jar file with its
@ -202,12 +201,29 @@ public class ManifestEntryVerifier {
throw new SecurityException("digest missing for " + name);
}
if (signers != null)
if (signers != null) {
return signers;
}
JarConstraintsParameters params =
getParams(verifiedSigners, sigFileSigners);
for (int i=0; i < digests.size(); i++) {
MessageDigest digest = digests.get(i);
MessageDigest digest = digests.get(i);
if (params != null) {
try {
params.setExtendedExceptionMsg(JarFile.MANIFEST_NAME,
name + " entry");
DisabledAlgorithmConstraints.jarConstraints()
.permits(digest.getAlgorithm(), params);
} catch (GeneralSecurityException e) {
if (debug != null) {
debug.println("Digest algorithm is restricted: " + e);
}
return null;
}
}
byte [] manHash = manifestHashes.get(i);
byte [] theHash = digest.digest();
@ -231,4 +247,37 @@ public class ManifestEntryVerifier {
}
return signers;
}
/**
* Get constraints parameters for JAR. The constraints should be
* checked against all code signers. Returns the parameters,
* or null if the signers for this entry have already been checked.
*/
private JarConstraintsParameters getParams(
Map<String, CodeSigner[]> verifiedSigners,
Map<String, CodeSigner[]> sigFileSigners) {
// verifiedSigners is usually preloaded with the Manifest's signers.
// If verifiedSigners contains the Manifest, then it will have all of
// the signers of the JAR. But if it doesn't then we need to fallback
// and check verifiedSigners to see if the signers of this entry have
// been checked already.
if (verifiedSigners.containsKey(JarFile.MANIFEST_NAME)) {
if (verifiedSigners.size() > 1) {
// this means we already checked it previously
return null;
} else {
return new JarConstraintsParameters(
verifiedSigners.get(JarFile.MANIFEST_NAME));
}
} else {
CodeSigner[] signers = sigFileSigners.get(name);
if (verifiedSigners.containsValue(signers)) {
return null;
} else {
return new JarConstraintsParameters(signers);
}
}
}
}

View file

@ -33,7 +33,6 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.Timestamp;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
@ -47,6 +46,7 @@ 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,16 +61,6 @@ public class SignatureFileVerifier {
/* Are we debugging ? */
private static final Debug debug = Debug.getInstance("jar");
/**
* Holder class to delay initialization of DisabledAlgorithmConstraints
* until needed.
*/
private static class ConfigurationHolder {
static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
new DisabledAlgorithmConstraints(
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
}
private ArrayList<CodeSigner[]> signerCache;
private static final String ATTR_DIGEST =
@ -99,13 +89,13 @@ public class SignatureFileVerifier {
/* 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").
/** Algorithms that have been previously checked against disabled
* constraints.
*/
private Timestamp timestamp = null;
private Map<String, Boolean> permittedAlgs = new HashMap<>();
/** ConstraintsParameters for checking disabled algorithms */
private JarConstraintsParameters params;
/**
* Create the named SignatureFileVerifier.
@ -320,32 +310,23 @@ public class SignatureFileVerifier {
name);
}
CodeSigner[] newSigners = getSigners(infos, block);
// make sure we have something to do all this work for...
if (newSigners == null)
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();
}
}
// check if any of the algorithms used to verify the SignerInfos
// are disabled
params = new JarConstraintsParameters(newSigners);
Set<String> notDisabledAlgorithms =
SignerInfo.verifyAlgorithms(infos, params, name + " PKCS7");
// add the SignerInfo algorithms that are ok to the permittedAlgs map
// so they are not checked again
for (String algorithm : notDisabledAlgorithms) {
permittedAlgs.put(algorithm, Boolean.TRUE);
}
Iterator<Map.Entry<String,Attributes>> entries =
@ -396,13 +377,14 @@ public class SignatureFileVerifier {
* 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) {
private boolean permittedCheck(String key, String algorithm) {
Boolean permitted = permittedAlgs.get(algorithm);
if (permitted == null) {
try {
ConfigurationHolder.JAR_DISABLED_CHECK.permits(algorithm,
new ConstraintsParameters(timestamp));
} catch(GeneralSecurityException e) {
params.setExtendedExceptionMsg(name + ".SF", key + " attribute");
DisabledAlgorithmConstraints
.jarConstraints().permits(algorithm, params);
} catch (GeneralSecurityException e) {
permittedAlgs.put(algorithm, Boolean.FALSE);
permittedAlgs.put(key.toUpperCase(), Boolean.FALSE);
if (debug != null) {