# HG changeset patch # User avoitylov # Date 1617771476 -3600 # Node ID 412d2b1381a44b0a247bd67df9f35530b275d9a5 # Parent f396f4a7ee5dc6621abd4c89b2c9cdb1b1096ca9 8249906: Enhance opening JARs 8258247: Couple of issues in fix for JDK-8249906 8259428: AlgorithmId.getEncodedParams() should return copy Reviewed-by: mbalao, andrew diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/java/security/cert/CertPathHelperImpl.java --- a/src/share/classes/java/security/cert/CertPathHelperImpl.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/java/security/cert/CertPathHelperImpl.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -63,4 +63,8 @@ protected void implSetDateAndTime(X509CRLSelector sel, Date date, long skew) { sel.setDateAndTime(date, skew); } + + protected boolean implIsJdkCA(TrustAnchor anchor) { + return anchor.isJdkCA(); + } } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/java/security/cert/TrustAnchor.java --- a/src/share/classes/java/security/cert/TrustAnchor.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/java/security/cert/TrustAnchor.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -30,6 +30,7 @@ import javax.security.auth.x500.X500Principal; +import sun.security.util.AnchorCertificates; import sun.security.x509.NameConstraintsExtension; import sun.security.x509.X500Name; @@ -68,6 +69,12 @@ private final X509Certificate trustedCert; private byte[] ncBytes; private NameConstraintsExtension nc; + private boolean jdkCA; + private boolean hasJdkCABeenChecked; + + static { + CertPathHelperImpl.initialize(); + } /** * Creates an instance of {@code TrustAnchor} with the specified @@ -330,4 +337,18 @@ sb.append(" Name Constraints: " + nc.toString() + "\n"); return sb.toString(); } + + /** + * Returns true if anchor is a JDK CA (a root CA that is included by + * default in the cacerts keystore). + */ + synchronized boolean isJdkCA() { + if (!hasJdkCABeenChecked) { + if (trustedCert != null) { + jdkCA = AnchorCertificates.contains(trustedCert); + } + hasJdkCABeenChecked = true; + } + return jdkCA; + } } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/pkcs/SignerInfo.java --- a/src/share/classes/sun/security/pkcs/SignerInfo.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/pkcs/SignerInfo.java Wed Apr 07 05:57:56 2021 +0100 @@ -35,26 +35,28 @@ import java.security.cert.X509Certificate; import java.security.*; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.EnumSet; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; import sun.misc.HexDumpEncoder; import sun.security.timestamp.TimestampToken; -import sun.security.util.ConstraintsParameters; import sun.security.util.Debug; import sun.security.util.DerEncoder; import sun.security.util.DerInputStream; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.DisabledAlgorithmConstraints; +import sun.security.util.JarConstraintsParameters; import sun.security.util.KeyUtil; import sun.security.util.ObjectIdentifier; +import sun.security.util.SignatureUtil; import sun.security.x509.AlgorithmId; import sun.security.x509.X500Name; import sun.security.x509.KeyUsageExtension; -import sun.security.util.SignatureUtil; /** * A SignerInfo, as defined in PKCS#7's signedData type. @@ -63,16 +65,8 @@ */ public class SignerInfo implements DerEncoder { - // Digest and Signature restrictions - private static final Set DIGEST_PRIMITIVE_SET = - Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); - - private static final Set SIG_PRIMITIVE_SET = - Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); - private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK = - new DisabledAlgorithmConstraints( - DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); + DisabledAlgorithmConstraints.jarConstraints(); BigInteger version; X500Name issuerName; @@ -87,6 +81,14 @@ PKCS9Attributes authenticatedAttributes; PKCS9Attributes unauthenticatedAttributes; + /** + * A map containing the algorithms in this SignerInfo. This is used to + * avoid checking algorithms to see if they are disabled more than once. + * The key is the AlgorithmId of the algorithm, and the value is the name of + * the field or attribute. + */ + private Map algorithms = new HashMap<>(); + public SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, @@ -313,21 +315,15 @@ throws NoSuchAlgorithmException, SignatureException { try { + Timestamp timestamp = getTimestamp(); ContentInfo content = block.getContentInfo(); if (data == null) { data = content.getContentBytes(); } - Timestamp timestamp = null; - try { - timestamp = getTimestamp(); - } catch (Exception ignore) { - } - - ConstraintsParameters cparams = - new ConstraintsParameters(timestamp); - String digestAlgname = getDigestAlgorithmId().getName(); + String digestAlgName = digestAlgorithmId.getName(); + algorithms.put(digestAlgorithmId, "SignerInfo digestAlgorithm field"); byte[] dataSigned; @@ -353,21 +349,11 @@ if (messageDigest == null) // fail if there is no message digest return null; - // check that digest algorithm is not restricted - try { - JAR_DISABLED_CHECK.permits(digestAlgname, cparams); - } catch (CertPathValidatorException e) { - throw new SignatureException(e.getMessage(), e); - } - - MessageDigest md = MessageDigest.getInstance(digestAlgname); + MessageDigest md = MessageDigest.getInstance(digestAlgName); byte[] computedMessageDigest = md.digest(data); - if (messageDigest.length != computedMessageDigest.length) + if (!MessageDigest.isEqual(messageDigest, computedMessageDigest)) { return null; - for (int i = 0; i < messageDigest.length; i++) { - if (messageDigest[i] != computedMessageDigest[i]) - return null; } // message digest attribute matched @@ -381,21 +367,23 @@ // put together digest algorithm and encryption algorithm // to form signing algorithm - String encryptionAlgname = + String encryptionAlgName = getDigestEncryptionAlgorithmId().getName(); // Workaround: sometimes the encryptionAlgname is actually // a signature name - String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); - if (tmp != null) encryptionAlgname = tmp; - String algname = AlgorithmId.makeSigAlg( - digestAlgname, encryptionAlgname); - - // check that jar signature algorithm is not restricted + String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgName); + if (tmp != null) encryptionAlgName = tmp; + String sigAlgName = AlgorithmId.makeSigAlg( + digestAlgName, encryptionAlgName); try { - JAR_DISABLED_CHECK.permits(algname, cparams); - } catch (CertPathValidatorException e) { - throw new SignatureException(e.getMessage(), e); + ObjectIdentifier oid = AlgorithmId.get(sigAlgName).getOID(); + AlgorithmId sigAlgId = + new AlgorithmId(oid, + digestEncryptionAlgorithmId.getParameters()); + algorithms.put(sigAlgId, + "SignerInfo digestEncryptionAlgorithm field"); + } catch (NoSuchAlgorithmException ignore) { } X509Certificate cert = getCertificate(block); @@ -404,14 +392,6 @@ } PublicKey key = cert.getPublicKey(); - // check if the public key is restricted - if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { - throw new SignatureException("Public key check failed. " + - "Disabled key used: " + - KeyUtil.getKeySize(key) + " bit " + - key.getAlgorithm()); - } - if (cert.hasUnsupportedCriticalExtension()) { throw new SignatureException("Certificate has unsupported " + "critical extension(s)"); @@ -448,13 +428,13 @@ } } - Signature sig = Signature.getInstance(algname); + Signature sig = Signature.getInstance(sigAlgName); AlgorithmParameters ap = digestEncryptionAlgorithmId.getParameters(); try { SignatureUtil.initVerifyWithParam(sig, key, - SignatureUtil.getParamSpec(algname, ap)); + SignatureUtil.getParamSpec(sigAlgName, ap)); } catch (ProviderException | InvalidAlgorithmParameterException | InvalidKeyException e) { throw new SignatureException(e.getMessage(), e); @@ -464,9 +444,8 @@ if (sig.verify(encryptedDigest)) { return this; } - } catch (IOException e) { - throw new SignatureException("IO error verifying signature:\n" + - e.getMessage()); + } catch (IOException | CertificateException e) { + throw new SignatureException("Error verifying signature", e); } return null; } @@ -564,6 +543,9 @@ // Extract the signer (the Timestamping Authority) // while verifying the content SignerInfo[] tsa = tsToken.verify(encTsTokenInfo); + if (tsa == null || tsa.length == 0) { + throw new SignatureException("Unable to verify timestamp"); + } // Expect only one signer ArrayList chain = tsa[0].getCertificateChain(tsToken); CertificateFactory cf = CertificateFactory.getInstance("X.509"); @@ -572,6 +554,7 @@ TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); // Check that the signature timestamp applies to this signature verifyTimestamp(tsTokenInfo); + algorithms.putAll(tsa[0].algorithms); // Create a timestamp object timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain); return timestamp; @@ -584,18 +567,13 @@ */ private void verifyTimestamp(TimestampToken token) throws NoSuchAlgorithmException, SignatureException { - String digestAlgname = token.getHashAlgorithm().getName(); - // check that algorithm is not restricted - if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, digestAlgname, - null)) { - throw new SignatureException("Timestamp token digest check failed. " + - "Disabled algorithm used: " + digestAlgname); - } + + AlgorithmId digestAlgId = token.getHashAlgorithm(); + algorithms.put(digestAlgId, "TimestampToken digestAlgorithm field"); - MessageDigest md = - MessageDigest.getInstance(digestAlgname); + MessageDigest md = MessageDigest.getInstance(digestAlgId.getName()); - if (!Arrays.equals(token.getHashedMessage(), + if (!MessageDigest.isEqual(token.getHashedMessage(), md.digest(encryptedDigest))) { throw new SignatureException("Signature timestamp (#" + @@ -636,4 +614,35 @@ } return out; } + + /** + * Verify all of the algorithms in the array of SignerInfos against the + * constraints in the jdk.jar.disabledAlgorithms security property. + * + * @param infos array of SignerInfos + * @param params constraint parameters + * @param name the name of the signer's PKCS7 file + * @return a set of algorithms that passed the checks and are not disabled + */ + public static Set verifyAlgorithms(SignerInfo[] infos, + JarConstraintsParameters params, String name) throws SignatureException { + Map algorithms = new HashMap<>(); + for (SignerInfo info : infos) { + algorithms.putAll(info.algorithms); + } + + Set enabledAlgorithms = new HashSet<>(); + try { + for (Map.Entry algorithm : algorithms.entrySet()) { + params.setExtendedExceptionMsg(name, algorithm.getValue()); + AlgorithmId algId = algorithm.getKey(); + JAR_DISABLED_CHECK.permits(algId.getName(), + algId.getParameters(), params); + enabledAlgorithms.add(algId.getName()); + } + } catch (CertPathValidatorException e) { + throw new SignatureException(e); + } + return enabledAlgorithms; + } } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/AlgorithmChecker.java --- a/src/share/classes/sun/security/provider/certpath/AlgorithmChecker.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/AlgorithmChecker.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -27,8 +27,6 @@ import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; -import java.security.Timestamp; -import java.security.cert.CertPathValidator; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -53,14 +51,13 @@ import java.security.interfaces.DSAPublicKey; import java.security.spec.DSAPublicKeySpec; -import sun.security.util.AnchorCertificates; import sun.security.util.ConstraintsParameters; import sun.security.util.Debug; import sun.security.util.DisabledAlgorithmConstraints; import sun.security.validator.Validator; +import sun.security.x509.AlgorithmId; import sun.security.x509.X509CertImpl; import sun.security.x509.X509CRLImpl; -import sun.security.x509.AlgorithmId; /** * A {@code PKIXCertPathChecker} implementation to check whether a @@ -78,10 +75,10 @@ private final AlgorithmConstraints constraints; private final PublicKey trustedPubKey; - private final Date pkixdate; + private final Date date; private PublicKey prevPubKey; - private final Timestamp jarTimestamp; private final String variant; + private TrustAnchor anchor; private static final Set SIGNATURE_PRIMITIVE_SET = Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); @@ -94,95 +91,70 @@ CryptoPrimitive.KEY_AGREEMENT)); private static final DisabledAlgorithmConstraints - certPathDefaultConstraints = new DisabledAlgorithmConstraints( - DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); - - // If there is no "cacerts" keyword, then disable anchor checking - private static final boolean publicCALimits = - certPathDefaultConstraints.checkProperty("jdkCA"); - - // If anchor checking enabled, this will be true if the trust anchor - // has a match in the cacerts file - private boolean trustedMatch = false; + certPathDefaultConstraints = + DisabledAlgorithmConstraints.certPathConstraints(); /** - * Create a new {@code AlgorithmChecker} with the given algorithm - * given {@code TrustAnchor} and {@code String} variant. + * Create a new {@code AlgorithmChecker} with the given + * {@code TrustAnchor} and {@code String} variant. * * @param anchor the trust anchor selected to validate the target * certificate - * @param variant is the Validator variants of the operation. A null value + * @param variant the Validator variant of the operation. A null value * passed will set it to Validator.GENERIC. */ public AlgorithmChecker(TrustAnchor anchor, String variant) { - this(anchor, certPathDefaultConstraints, null, null, variant); + this(anchor, certPathDefaultConstraints, null, variant); } /** * Create a new {@code AlgorithmChecker} with the given - * {@code AlgorithmConstraints}, {@code Timestamp}, and {@code String} - * variant. + * {@code AlgorithmConstraints} and {@code String} variant. * * Note that this constructor can initialize a variation of situations where - * the AlgorithmConstraints, Timestamp, or Variant maybe known. + * the AlgorithmConstraints or Variant maybe known. * * @param constraints the algorithm constraints (or null) - * @param jarTimestamp Timestamp passed for JAR timestamp constraint - * checking. Set to null if not applicable. - * @param variant is the Validator variants of the operation. A null value + * @param variant the Validator variant of the operation. A null value * passed will set it to Validator.GENERIC. */ - public AlgorithmChecker(AlgorithmConstraints constraints, - Timestamp jarTimestamp, String variant) { - this(null, constraints, null, jarTimestamp, variant); + public AlgorithmChecker(AlgorithmConstraints constraints, String variant) { + this(null, constraints, null, variant); } /** * Create a new {@code AlgorithmChecker} with the - * given {@code TrustAnchor}, {@code AlgorithmConstraints}, - * {@code Timestamp}, and {@code String} variant. + * given {@code TrustAnchor}, {@code AlgorithmConstraints}, {@code Date}, + * and {@code String} variant. * * @param anchor the trust anchor selected to validate the target * certificate * @param constraints the algorithm constraints (or null) - * @param pkixdate The date specified by the PKIXParameters date. If the - * PKIXParameters is null, the current date is used. This - * should be null when jar files are being checked. - * @param jarTimestamp Timestamp passed for JAR timestamp constraint - * checking. Set to null if not applicable. - * @param variant is the Validator variants of the operation. A null value + * @param date the date specified by the PKIXParameters date, or the + * JAR timestamp if jar files are being validated and the + * JAR is timestamped. May be null if no timestamp or + * PKIXParameter date is set. + * @param variant the Validator variant of the operation. A null value * passed will set it to Validator.GENERIC. */ public AlgorithmChecker(TrustAnchor anchor, - AlgorithmConstraints constraints, Date pkixdate, - Timestamp jarTimestamp, String variant) { + AlgorithmConstraints constraints, Date date, String variant) { if (anchor != null) { if (anchor.getTrustedCert() != null) { this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); - // Check for anchor certificate restrictions - trustedMatch = checkFingerprint(anchor.getTrustedCert()); - if (trustedMatch && debug != null) { - debug.println("trustedMatch = true"); - } } else { this.trustedPubKey = anchor.getCAPublicKey(); } + this.anchor = anchor; } else { this.trustedPubKey = null; - if (debug != null) { - debug.println("TrustAnchor is null, trustedMatch is false."); - } } this.prevPubKey = this.trustedPubKey; this.constraints = (constraints == null ? certPathDefaultConstraints : constraints); - // If we are checking jar files, set pkixdate the same as the timestamp - // for certificate checking - this.pkixdate = (jarTimestamp != null ? jarTimestamp.getTimestamp() : - pkixdate); - this.jarTimestamp = jarTimestamp; + this.date = date; this.variant = (variant == null ? Validator.VAR_GENERIC : variant); } @@ -194,24 +166,11 @@ * certificate * @param pkixdate Date the constraints are checked against. The value is * either the PKIXParameters date or null for the current date. - * @param variant is the Validator variants of the operation. A null value + * @param variant the Validator variant of the operation. A null value * passed will set it to Validator.GENERIC. */ public AlgorithmChecker(TrustAnchor anchor, Date pkixdate, String variant) { - this(anchor, certPathDefaultConstraints, pkixdate, null, variant); - } - - // Check this 'cert' for restrictions in the AnchorCertificates - // trusted certificates list - private static boolean checkFingerprint(X509Certificate cert) { - if (!publicCALimits) { - return false; - } - - if (debug != null) { - debug.println("AlgorithmChecker.contains: " + cert.getSigAlgName()); - } - return AnchorCertificates.contains(cert); + this(anchor, certPathDefaultConstraints, pkixdate, variant); } @Override @@ -318,18 +277,19 @@ } ConstraintsParameters cp = - new ConstraintsParameters((X509Certificate)cert, - trustedMatch, pkixdate, jarTimestamp, variant); + new CertPathConstraintsParameters(x509Cert, variant, + anchor, date); // Check against local constraints if it is DisabledAlgorithmConstraints if (constraints instanceof DisabledAlgorithmConstraints) { - ((DisabledAlgorithmConstraints)constraints).permits(currSigAlg, cp); + ((DisabledAlgorithmConstraints)constraints).permits(currSigAlg, + currSigAlgParams, cp); // DisabledAlgorithmsConstraints does not check primitives, so key // additional key check. } else { // Perform the default constraints checking anyway. - certPathDefaultConstraints.permits(currSigAlg, cp); + certPathDefaultConstraints.permits(currSigAlg, currSigAlgParams, cp); // Call locally set constraints to check key with primitives. if (!constraints.permits(primitives, currPubKey)) { throw new CertPathValidatorException( @@ -408,14 +368,10 @@ // Don't bother to change the trustedPubKey. if (anchor.getTrustedCert() != null) { prevPubKey = anchor.getTrustedCert().getPublicKey(); - // Check for anchor certificate restrictions - trustedMatch = checkFingerprint(anchor.getTrustedCert()); - if (trustedMatch && debug != null) { - debug.println("trustedMatch = true"); - } } else { prevPubKey = anchor.getCAPublicKey(); } + this.anchor = anchor; } } @@ -424,11 +380,12 @@ * * @param key the public key to verify the CRL signature * @param crl the target CRL - * @param variant is the Validator variants of the operation. A null value + * @param variant the Validator variant of the operation. A null value * passed will set it to Validator.GENERIC. + * @param anchor the trust anchor selected to validate the CRL issuer */ - static void check(PublicKey key, X509CRL crl, String variant) - throws CertPathValidatorException { + static void check(PublicKey key, X509CRL crl, String variant, + TrustAnchor anchor) throws CertPathValidatorException { X509CRLImpl x509CRLImpl = null; try { @@ -438,7 +395,7 @@ } AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); - check(key, algorithmId, variant); + check(key, algorithmId, variant, anchor); } /** @@ -446,16 +403,16 @@ * * @param key the public key to verify the CRL signature * @param algorithmId signature algorithm Algorithm ID - * @param variant is the Validator variants of the operation. A null value - * passed will set it to Validator.GENERIC. + * @param variant the Validator variant of the operation. A null + * value passed will set it to Validator.GENERIC. + * @param anchor the trust anchor selected to validate the public key */ - static void check(PublicKey key, AlgorithmId algorithmId, String variant) - throws CertPathValidatorException { - String sigAlgName = algorithmId.getName(); - AlgorithmParameters sigAlgParams = algorithmId.getParameters(); + static void check(PublicKey key, AlgorithmId algorithmId, String variant, + TrustAnchor anchor) throws CertPathValidatorException { - certPathDefaultConstraints.permits(new ConstraintsParameters( - sigAlgName, sigAlgParams, key, variant)); + certPathDefaultConstraints.permits(algorithmId.getName(), + algorithmId.getParameters(), + new CertPathConstraintsParameters(key, variant, anchor)); } } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java Wed Apr 07 05:57:56 2021 +0100 @@ -0,0 +1,127 @@ +/* + * 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.provider.certpath; + +import java.security.Key; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Set; +import java.util.Collections; + +import sun.security.util.ConstraintsParameters; +import sun.security.validator.Validator; + +/** + * This class contains parameters for checking certificates against + * constraints specified in the jdk.certpath.disabledAlgorithms security + * property. + */ +class CertPathConstraintsParameters implements ConstraintsParameters { + // The public key of the certificate + private final Key key; + // The certificate's trust anchor which will be checked against the + // jdkCA constraint, if specified. + private final TrustAnchor anchor; + // The PKIXParameter validity date or the timestamp of the signed JAR + // file, if this chain is associated with a timestamped signed JAR. + private final Date date; + // The variant or usage of this certificate + private final String variant; + // The certificate being checked (may be null if a CRL or OCSPResponse is + // being checked) + private final X509Certificate cert; + + public CertPathConstraintsParameters(X509Certificate cert, + String variant, TrustAnchor anchor, Date date) { + this(cert.getPublicKey(), variant, anchor, date, cert); + } + + public CertPathConstraintsParameters(Key key, String variant, + TrustAnchor anchor) { + this(key, variant, anchor, null, null); + } + + private CertPathConstraintsParameters(Key key, String variant, + TrustAnchor anchor, Date date, X509Certificate cert) { + this.key = key; + this.variant = (variant == null ? Validator.VAR_GENERIC : variant); + this.anchor = anchor; + this.date = date; + this.cert = cert; + } + + @Override + public boolean anchorIsJdkCA() { + return CertPathHelper.isJdkCA(anchor); + } + + @Override + public Set getKeys() { + return (key == null) ? Collections.emptySet() + : Collections.singleton(key); + } + + @Override + public Date getDate() { + return date; + } + + @Override + public String getVariant() { + return variant; + } + + @Override + public String extendedExceptionMsg() { + return (cert == null ? "." + : " used with certificate: " + + cert.getSubjectX500Principal()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[\n"); + sb.append("\n Variant: ").append(variant); + if (anchor != null) { + sb.append("\n Anchor: ").append(anchor); + } + if (cert != null) { + sb.append("\n Cert Issuer: ") + .append(cert.getIssuerX500Principal()); + sb.append("\n Cert Subject: ") + .append(cert.getSubjectX500Principal()); + } + if (key != null) { + sb.append("\n Key: ").append(key.getAlgorithm()); + } + if (date != null) { + sb.append("\n Date: ").append(date); + } + sb.append("\n]"); + return sb.toString(); + } +} diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/CertPathHelper.java --- a/src/share/classes/sun/security/provider/certpath/CertPathHelper.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/CertPathHelper.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -28,13 +28,14 @@ import java.util.Date; import java.util.Set; +import java.security.cert.TrustAnchor; import java.security.cert.X509CertSelector; import java.security.cert.X509CRLSelector; import sun.security.x509.GeneralNameInterface; /** - * Helper class that allows access to Sun specific known-public methods in the + * Helper class that allows access to JDK specific known-public methods in the * java.security.cert package. It relies on a subclass in the * java.security.cert packages that is initialized before any of these methods * are called (achieved via static initializers). @@ -59,6 +60,8 @@ protected abstract void implSetDateAndTime(X509CRLSelector sel, Date date, long skew); + protected abstract boolean implIsJdkCA(TrustAnchor anchor); + static void setPathToNames(X509CertSelector sel, Set names) { instance.implSetPathToNames(sel, names); @@ -67,4 +70,8 @@ public static void setDateAndTime(X509CRLSelector sel, Date date, long skew) { instance.implSetDateAndTime(sel, date, skew); } + + public static boolean isJdkCA(TrustAnchor anchor) { + return (anchor == null) ? false : instance.implIsJdkCA(anchor); + } } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java --- a/src/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java Wed Apr 07 05:57:56 2021 +0100 @@ -73,7 +73,7 @@ throws CertStoreException { return getCRLs(selector, signFlag, prevKey, null, provider, certStores, - reasonsMask, trustAnchors, validity, variant); + reasonsMask, trustAnchors, validity, variant, null); } /** * Return the X509CRLs matching this selector. The selector must be @@ -90,8 +90,14 @@ Date validity) throws CertStoreException { + if (trustAnchors.isEmpty()) { + throw new CertStoreException( + "at least one TrustAnchor must be specified"); + } + TrustAnchor anchor = trustAnchors.iterator().next(); return getCRLs(selector, signFlag, prevKey, null, provider, certStores, - reasonsMask, trustAnchors, validity, Validator.VAR_GENERIC); + reasonsMask, trustAnchors, validity, + Validator.VAR_PLUGIN_CODE_SIGNING, anchor); } /** @@ -107,7 +113,8 @@ boolean[] reasonsMask, Set trustAnchors, Date validity, - String variant) + String variant, + TrustAnchor anchor) throws CertStoreException { X509Certificate cert = selector.getCertificateChecking(); @@ -136,7 +143,7 @@ DistributionPoint point = t.next(); Collection crls = getCRLs(selector, certImpl, point, reasonsMask, signFlag, prevKey, prevCert, provider, - certStores, trustAnchors, validity, variant); + certStores, trustAnchors, validity, variant, anchor); results.addAll(crls); } if (debug != null) { @@ -161,7 +168,8 @@ X509CertImpl certImpl, DistributionPoint point, boolean[] reasonsMask, boolean signFlag, PublicKey prevKey, X509Certificate prevCert, String provider, List certStores, - Set trustAnchors, Date validity, String variant) + Set trustAnchors, Date validity, String variant, + TrustAnchor anchor) throws CertStoreException { // check for full name @@ -224,7 +232,7 @@ selector.setIssuerNames(null); if (selector.match(crl) && verifyCRL(certImpl, point, crl, reasonsMask, signFlag, prevKey, prevCert, provider, - trustAnchors, certStores, validity, variant)) { + trustAnchors, certStores, validity, variant, anchor)) { crls.add(crl); } } catch (IOException | CRLException e) { @@ -333,7 +341,8 @@ X509CRL crl, boolean[] reasonsMask, boolean signFlag, PublicKey prevKey, X509Certificate prevCert, String provider, Set trustAnchors, List certStores, - Date validity, String variant) throws CRLException, IOException { + Date validity, String variant, TrustAnchor anchor) + throws CRLException, IOException { if (debug != null) { debug.println("DistributionPointFetcher.verifyCRL: " + @@ -680,7 +689,7 @@ // check the crl signature algorithm try { - AlgorithmChecker.check(prevKey, crl, variant); + AlgorithmChecker.check(prevKey, crl, variant, anchor); } catch (CertPathValidatorException cpve) { if (debug != null) { debug.println("CRL signature algorithm check failed: " + cpve); diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/OCSP.java --- a/src/share/classes/sun/security/provider/certpath/OCSP.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/OCSP.java Wed Apr 07 05:57:56 2021 +0100 @@ -121,7 +121,8 @@ throws IOException, CertPathValidatorException { return check(cert, issuerCert, responderURI, responderCert, date, - Collections.emptyList(), Validator.VAR_GENERIC); + Collections.emptyList(), + Validator.VAR_PLUGIN_CODE_SIGNING); } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/OCSPResponse.java --- a/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2021, 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 @@ -465,6 +465,7 @@ } // Check whether the signer cert returned by the responder is trusted + boolean signedByTrustedResponder = false; if (signerCert != null) { // Check if the response is signed by the issuing CA if (signerCert.getSubjectX500Principal().equals( @@ -479,6 +480,7 @@ // Check if the response is signed by a trusted responder } else if (signerCert.equals(responderCert)) { + signedByTrustedResponder = true; if (debug != null) { debug.println("OCSP response is signed by a Trusted " + "Responder"); @@ -569,7 +571,10 @@ if (signerCert != null) { // Check algorithm constraints specified in security property // "jdk.certpath.disabledAlgorithms". - AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant); + AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant, + signedByTrustedResponder + ? new TrustAnchor(responderCert, null) + : issuerInfo.getAnchor()); if (!verifySignature(signerCert)) { throw new CertPathValidatorException( diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/PKIX.java --- a/src/share/classes/sun/security/provider/certpath/PKIX.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/PKIX.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -33,6 +33,7 @@ import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; +import sun.security.validator.Validator; /** * Common utility methods and classes used by the PKIX CertPathValidator and @@ -87,7 +88,7 @@ private Set anchors; private List certs; private Timestamp timestamp; - private String variant; + private String variant = Validator.VAR_GENERIC; ValidatorParams(CertPath cp, PKIXParameters params) throws InvalidAlgorithmParameterException @@ -155,9 +156,17 @@ } Date date() { if (!gotDate) { - date = params.getDate(); - if (date == null) - date = new Date(); + // use timestamp if checking signed code that is + // timestamped, otherwise use date parameter + if (timestamp != null && + (variant.equals(Validator.VAR_CODE_SIGNING) || + variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING))) { + date = timestamp.getTimestamp(); + } else { + date = params.getDate(); + if (date == null) + date = new Date(); + } gotDate = true; } return date; @@ -198,10 +207,6 @@ return params; } - Timestamp timestamp() { - return timestamp; - } - String variant() { return variant; } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java --- a/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -31,7 +31,6 @@ import java.util.*; import sun.security.provider.certpath.PKIX.ValidatorParams; -import sun.security.validator.Validator; import sun.security.x509.X509CertImpl; import sun.security.util.Debug; @@ -174,7 +173,7 @@ // add standard checkers that we will be using certPathCheckers.add(untrustedChecker); certPathCheckers.add(new AlgorithmChecker(anchor, null, params.date(), - params.timestamp(), params.variant())); + params.variant())); certPathCheckers.add(new KeyChecker(certPathLen, params.targetCertConstraints())); certPathCheckers.add(new ConstraintsChecker(certPathLen)); @@ -191,19 +190,7 @@ rootNode); certPathCheckers.add(pc); - // the time that the certificate validity period should be - // checked against - Date timeToCheck = null; - // use timestamp if checking signed code that is timestamped, otherwise - // use date parameter from PKIXParameters - if ((params.variant() == Validator.VAR_CODE_SIGNING || - params.variant() == Validator.VAR_PLUGIN_CODE_SIGNING) && - params.timestamp() != null) { - timeToCheck = params.timestamp().getTimestamp(); - } else { - timeToCheck = params.date(); - } - BasicChecker bc = new BasicChecker(anchor, timeToCheck, + BasicChecker bc = new BasicChecker(anchor, params.date(), params.sigProvider(), false); certPathCheckers.add(bc); diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/provider/certpath/RevocationChecker.java --- a/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Wed Apr 07 05:57:56 2021 +0100 @@ -579,7 +579,7 @@ approvedCRLs.addAll(DistributionPointFetcher.getCRLs( sel, signFlag, prevKey, prevCert, params.sigProvider(), certStores, reasonsMask, - anchors, null, params.variant())); + anchors, null, params.variant(), anchor)); } } catch (CertStoreException e) { if (e instanceof CertStoreTypeException) { @@ -853,7 +853,7 @@ if (DistributionPointFetcher.verifyCRL( certImpl, point, crl, reasonsMask, signFlag, prevKey, null, params.sigProvider(), anchors, - certStores, params.date(), params.variant())) + certStores, params.date(), params.variant(), anchor)) { results.add(crl); } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/ssl/SSLContextImpl.java --- a/src/share/classes/sun/security/ssl/SSLContextImpl.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/ssl/SSLContextImpl.java Wed Apr 07 05:57:56 2021 +0100 @@ -1374,7 +1374,7 @@ // A forward checker, need to check from trust to target if (checkedLength >= 0) { AlgorithmChecker checker = - new AlgorithmChecker(constraints, null, + new AlgorithmChecker(constraints, (checkClientTrusted ? Validator.VAR_TLS_CLIENT : Validator.VAR_TLS_SERVER)); checker.init(false); diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/ssl/X509KeyManagerImpl.java --- a/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java Wed Apr 07 05:57:56 2021 +0100 @@ -831,8 +831,7 @@ AlgorithmConstraints constraints, Certificate[] chain, String variant) { - AlgorithmChecker checker = - new AlgorithmChecker(constraints, null, variant); + AlgorithmChecker checker = new AlgorithmChecker(constraints, variant); try { checker.init(false); } catch (CertPathValidatorException cpve) { diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/util/AnchorCertificates.java --- a/src/share/classes/sun/security/util/AnchorCertificates.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/util/AnchorCertificates.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -35,6 +35,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,6 +48,7 @@ private static final Debug debug = Debug.getInstance("certpath"); private static final String HASH = "SHA-256"; private static Set certs = Collections.emptySet(); + private static Set certIssuers = Collections.emptySet(); static { AccessController.doPrivileged(new PrivilegedAction() { @@ -60,15 +62,16 @@ try (FileInputStream fis = new FileInputStream(f)) { cacerts.load(fis, null); certs = new HashSet<>(); + certIssuers = new HashSet<>(); Enumeration 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()); } } } @@ -84,10 +87,10 @@ } /** - * 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); @@ -99,5 +102,15 @@ 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() {} } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/util/ConstraintsParameters.java --- a/src/share/classes/sun/security/util/ConstraintsParameters.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/util/ConstraintsParameters.java Wed Apr 07 05:57:56 2021 +0100 @@ -25,160 +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.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 on passed between 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. - */ - // 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; - - /* - * New values that are checked against constraints that the current public - * API does not support. - */ - // 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]; +public interface ConstraintsParameters { - 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 true if a certificate chains back to a trusted JDK root CA. + */ + boolean anchorIsJdkCA(); - 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); - } + /** + * Returns the set of keys that should be checked against the + * constraints, or an empty set if there are no keys to be checked. + */ + Set getKeys(); - 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; - } + /** + * Returns the date that should be checked against the constraints, or + * null if not set. + */ + Date getDate(); - 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 : CurveDB.getNamesByOID(nc.getObjectId())); - } else { - return EMPTYLIST; - } - } + /** + * Returns the Validator variant. + */ + String getVariant(); - 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 an extended message used in exceptions. See + * DisabledAlgorithmConstraints for usage. + */ + String extendedExceptionMsg(); } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java --- a/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java Wed Apr 07 05:57:56 2021 +0100 @@ -29,14 +29,19 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.security.AlgorithmParameters; import java.security.CryptoPrimitive; -import java.security.AlgorithmParameters; 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.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -46,6 +51,7 @@ import java.util.Map; import java.util.Set; import java.util.Collection; +import java.util.Collections; import java.util.StringTokenizer; import java.util.TimeZone; import java.util.regex.Pattern; @@ -80,9 +86,27 @@ 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 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. * @@ -123,7 +147,7 @@ disabledAlgorithms.addAll(ecindex, getAlgorithms(PROPERTY_DISABLED_EC_CURVES)); } - algorithmConstraints = new Constraints(disabledAlgorithms); + algorithmConstraints = new Constraints(propertyName, disabledAlgorithms); } /* @@ -168,32 +192,54 @@ 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); + } + } + + 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 + } } - 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 permitsPSSParams(AlgorithmParameters ap, + ConstraintsParameters cp) throws CertPathValidatorException { - /* - * 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. - */ + 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 " + @@ -206,15 +252,14 @@ 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 getNamedCurveFromKey(Key key) { + if (key instanceof ECKey) { + NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams()); + return (nc == null ? Collections.emptyList() + : Arrays.asList(CurveDB.getNamesByOID(nc.getObjectId()))); + } else { + return Collections.emptyList(); } - return false; } // Check algorithm constraints with key and algorithm @@ -238,8 +283,8 @@ 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; } @@ -276,7 +321,7 @@ "denyAfter\\s+(\\d{4})-(\\d{2})-(\\d{2})"); } - public Constraints(List constraintArray) { + public Constraints(String propertyName, List constraintArray) { for (String constraintEntry : constraintArray) { if (constraintEntry == null || constraintEntry.isEmpty()) { continue; @@ -391,7 +436,7 @@ } } - // Get applicable constraints based off the signature algorithm + // Get applicable constraints based off the algorithm private List getConstraints(String algorithm) { return constraintsMap.get(algorithm.toUpperCase(Locale.ENGLISH)); } @@ -435,13 +480,12 @@ 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 @@ -451,13 +495,10 @@ algorithms.add(algorithm); } - // Attempt to add the public key algorithm if cert provided - if (cert != null) { - algorithms.add(cert.getPublicKey().getAlgorithm()); + for (Key key : cp.getKeys()) { + algorithms.add(key.getAlgorithm()); } - if (cp.getKey() != null) { - algorithms.add(cp.getKey().getAlgorithm()); - } + // Check all applicable constraints for (String alg : algorithms) { List list = getConstraints(alg); @@ -548,7 +589,7 @@ * {@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. * */ @@ -597,14 +638,6 @@ 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() : ".")); - } } /* @@ -628,14 +661,15 @@ 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); } } @@ -700,15 +734,10 @@ 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)) { @@ -718,8 +747,8 @@ 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); } } @@ -756,19 +785,27 @@ @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() + "\""); // Because usage checking can come from many places // a stack trace is very helpful. @@ -777,13 +814,13 @@ (new Exception()).printStackTrace(ps); debug.println(ba.toString()); } - 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); } } @@ -837,31 +874,25 @@ } /* - * 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; + 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); } - throw new CertPathValidatorException( - "Algorithm constraints check failed on keysize limits. " + - algorithm + " " + KeyUtil.getKeySize(key) + "bit key" + - extendedMsg(cp), - null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); } } @@ -938,7 +969,7 @@ 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); } diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/util/JarConstraintsParameters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/security/util/JarConstraintsParameters.java Wed Apr 07 05:57:56 2021 +0100 @@ -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 keys; + // The certs in the signers' chains that are issued by the trust anchor + private final Set 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 chain = + (List)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 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(); + } +} diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/util/ManifestEntryVerifier.java --- a/src/share/classes/sun/security/util/ManifestEntryVerifier.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/util/ManifestEntryVerifier.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -27,13 +27,12 @@ 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 @@ 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(); @@ -232,6 +248,38 @@ 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 verifiedSigners, + Map 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); + } + } + } + // for the toHex function private static final char[] hexc = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/util/SignatureFileVerifier.java --- a/src/share/classes/sun/security/util/SignatureFileVerifier.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/util/SignatureFileVerifier.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -32,7 +32,6 @@ 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; @@ -45,6 +44,7 @@ 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; @@ -59,10 +59,6 @@ /* Are we debugging ? */ private static final Debug debug = Debug.getInstance("jar"); - private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK = - new DisabledAlgorithmConstraints( - DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); - private ArrayList signerCache; private static final String ATTR_DIGEST = @@ -92,13 +88,13 @@ /* for generating certpath objects */ private CertificateFactory certificateFactory = null; - /** Algorithms that have been checked if they are weak. */ - private Map permittedAlgs= new HashMap<>(); + /** Algorithms that have been previously checked against disabled + * constraints. + */ + private Map 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; + /** ConstraintsParameters for checking disabled algorithms */ + private JarConstraintsParameters params; /** * Create the named SignatureFileVerifier. @@ -291,32 +287,23 @@ 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 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> entries = @@ -367,13 +354,14 @@ * 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 { - 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) { diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/validator/PKIXValidator.java --- a/src/share/classes/sun/security/validator/PKIXValidator.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/validator/PKIXValidator.java Wed Apr 07 05:57:56 2021 +0100 @@ -239,7 +239,7 @@ // add a new algorithm constraints checker if (constraints != null) { pkixParameters.addCertPathChecker( - new AlgorithmChecker(constraints, null, variant)); + new AlgorithmChecker(constraints, variant)); } // attach it to the PKIXBuilderParameters. diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/validator/SimpleValidator.java --- a/src/share/classes/sun/security/validator/SimpleValidator.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/validator/SimpleValidator.java Wed Apr 07 05:57:56 2021 +0100 @@ -162,7 +162,7 @@ AlgorithmChecker appAlgChecker = null; if (constraints != null) { appAlgChecker = new AlgorithmChecker(anchor, constraints, null, - null, variant); + variant); } // verify top down, starting at the certificate issued by diff -r f396f4a7ee5d -r 412d2b1381a4 src/share/classes/sun/security/x509/AlgorithmId.java --- a/src/share/classes/sun/security/x509/AlgorithmId.java Wed Apr 07 05:55:34 2021 +0100 +++ b/src/share/classes/sun/security/x509/AlgorithmId.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, 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 @@ -81,6 +81,7 @@ */ protected DerValue params; + private transient byte[] encodedParams; /** * Constructs an algorithm ID which will be initialized @@ -109,6 +110,18 @@ algid = oid; algParams = algparams; constructedFromDer = false; + if (algParams != null) { + try { + encodedParams = algParams.getEncoded(); + } catch (IOException ioe) { + // It should be safe to ignore this. + // This exception can occur if AlgorithmParameters was not + // initialized (which should not occur), or if it was + // initialized with bogus parameters, which should have + // been detected when init was called. + assert false; + } + } } private AlgorithmId(ObjectIdentifier oid, DerValue params) @@ -116,6 +129,7 @@ this.algid = oid; this.params = params; if (this.params != null) { + encodedParams = params.toByteArray(); decodeParams(); } } @@ -134,7 +148,7 @@ } // Decode (parse) the parameters - algParams.init(params.toByteArray()); + algParams.init(encodedParams.clone()); } /** @@ -153,6 +167,7 @@ * * @exception IOException on encoding error. */ + @Override public void derEncode (OutputStream out) throws IOException { DerOutputStream bytes = new DerOutputStream(); DerOutputStream tmp = new DerOutputStream(); @@ -160,8 +175,8 @@ bytes.putOID(algid); // Setup params from algParams since no DER encoding is given if (constructedFromDer == false) { - if (algParams != null) { - params = new DerValue(algParams.getEncoded()); + if (encodedParams != null) { + params = new DerValue(encodedParams); } else { params = null; } @@ -248,7 +263,7 @@ if ((params != null) && algid.equals((Object)specifiedWithECDSA_oid)) { try { AlgorithmId paramsId = - AlgorithmId.parse(new DerValue(params.toByteArray())); + AlgorithmId.parse(new DerValue(encodedParams)); String paramsName = paramsId.getName(); algName = makeSigAlg(paramsName, "EC"); } catch (IOException e) { @@ -266,6 +281,10 @@ * Returns the DER encoded parameter, which can then be * used to initialize java.security.AlgorithmParameters. * + * Note that this* method should always return a new array as it is called + * directly by the JDK implementation of X509Certificate.getSigAlgParams() + * and X509CRL.getSigAlgParams(). + * * Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method * returns null because {@link #getName()} has already returned the "full" * signature algorithm (Ex: SHA256withECDSA). @@ -273,9 +292,9 @@ * @return DER encoded parameters, or null not present. */ public byte[] getEncodedParams() throws IOException { - return (params == null || algid.equals(specifiedWithECDSA_oid)) + return (encodedParams == null || algid.equals(specifiedWithECDSA_oid)) ? null - : params.toByteArray(); + : encodedParams.clone(); } /** @@ -283,9 +302,8 @@ * with the same parameters. */ public boolean equals(AlgorithmId other) { - boolean paramsEqual = - (params == null ? other.params == null : params.equals(other.params)); - return (algid.equals((Object)other.algid) && paramsEqual); + return algid.equals((Object)other.algid) && + Arrays.equals(encodedParams, other.encodedParams); } /** @@ -295,6 +313,7 @@ * * @param other preferably an AlgorithmId, else an ObjectIdentifier */ + @Override public boolean equals(Object other) { if (this == other) { return true; @@ -321,11 +340,11 @@ * * @return a hashcode for this AlgorithmId. */ + @Override public int hashCode() { - StringBuilder sbuf = new StringBuilder(); - sbuf.append(algid.toString()); - sbuf.append(paramsToString()); - return sbuf.toString().hashCode(); + int hashCode = algid.hashCode(); + hashCode = 31 * hashCode + Arrays.hashCode(encodedParams); + return hashCode; } /** @@ -333,10 +352,10 @@ * This may be redefined by subclasses which parse those parameters. */ protected String paramsToString() { - if (params == null) { + if (encodedParams == null) { return ""; } else if (algParams != null) { - return algParams.toString(); + return ", " + algParams.toString(); } else { return ", params unparsed"; } @@ -345,6 +364,7 @@ /** * Returns a string describing the algorithm and its parameters. */ + @Override public String toString() { return getName() + paramsToString(); } diff -r f396f4a7ee5d -r 412d2b1381a4 test/java/security/cert/X509Certificate/GetSigAlgParams.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/security/cert/X509Certificate/GetSigAlgParams.java Wed Apr 07 05:57:56 2021 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, 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. + * + * 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. + */ + +/* + * @test + * @bug 8259428 + * @summary Verify X509Certificate.getSigAlgParams() returns new array each + * time it is called + */ + +import java.security.cert.X509Certificate; +import sun.security.tools.keytool.CertAndKeyGen; +import sun.security.x509.X500Name; + +public class GetSigAlgParams { + + public static void main(String[] args) throws Exception { + + CertAndKeyGen cakg = new CertAndKeyGen("RSASSA-PSS", "RSASSA-PSS"); + cakg.generate(1024); + X509Certificate c = cakg.getSelfCertificate(new X500Name("CN=Me"), 100); + if (c.getSigAlgParams() == c.getSigAlgParams()) { + throw new Exception("Encoded params are the same byte array"); + } + } +} diff -r f396f4a7ee5d -r 412d2b1381a4 test/sun/security/pkcs/pkcs8/PKCS8Test.java --- a/test/sun/security/pkcs/pkcs8/PKCS8Test.java Wed Apr 07 05:55:34 2021 +0100 +++ b/test/sun/security/pkcs/pkcs8/PKCS8Test.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -53,7 +53,7 @@ static final DerOutputStream derOutput = new DerOutputStream(); static final String FORMAT = "PKCS#8"; - static final String EXPECTED_ALG_ID_CHRS = "DSA\n\tp: 02\n\tq: 03\n" + static final String EXPECTED_ALG_ID_CHRS = "DSA, \n\tp: 02\n\tq: 03\n" + "\tg: 04\n"; static final String ALGORITHM = "DSA"; static final String EXCEPTION_MESSAGE = "version mismatch: (supported: " diff -r f396f4a7ee5d -r 412d2b1381a4 test/sun/security/tools/jarsigner/TimestampCheck.java --- a/test/sun/security/tools/jarsigner/TimestampCheck.java Wed Apr 07 05:55:34 2021 +0100 +++ b/test/sun/security/tools/jarsigner/TimestampCheck.java Wed Apr 07 05:57:56 2021 +0100 @@ -450,7 +450,7 @@ sign("tsdisabled", "-digestalg", "MD5", "-sigalg", "MD5withRSA", "-tsadigestalg", "MD5") .shouldHaveExitValue(68) - .shouldContain("The timestamp is invalid. Without a valid timestamp") + .shouldContain("TSA certificate chain is invalid") .shouldMatch("MD5.*-digestalg.*is disabled") .shouldMatch("MD5.*-tsadigestalg.*is disabled") .shouldMatch("MD5withRSA.*-sigalg.*is disabled"); @@ -458,7 +458,6 @@ signVerbose("tsdisabled", "unsigned.jar", "tsdisabled2.jar", "signer") .shouldHaveExitValue(64) - .shouldContain("The timestamp is invalid. Without a valid timestamp") .shouldContain("TSA certificate chain is invalid"); // Disabled timestamp is an error and jar treated unsigned @@ -771,7 +770,7 @@ .shouldMatch("Timestamp signature algorithm: .*key.*(disabled)"); verify(file, "-J-Djava.security.debug=jar") .shouldHaveExitValue(16) - .shouldMatch("SignatureException:.*disabled"); + .shouldMatch("SignatureException:.*keysize"); } static void checkHalfWeak(String file) throws Exception { diff -r f396f4a7ee5d -r 412d2b1381a4 test/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java --- a/test/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java Wed Apr 07 05:55:34 2021 +0100 +++ b/test/sun/security/x509/AlgorithmId/AlgorithmIdEqualsHashCode.java Wed Apr 07 05:57:56 2021 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, 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 @@ -24,12 +24,16 @@ /* * @test * @author Gary Ellison - * @bug 4170635 + * @bug 4170635 8258247 * @summary Verify equals()/hashCode() contract honored */ import java.io.*; +import java.security.AlgorithmParameters; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import sun.security.util.DerValue; import sun.security.x509.*; public class AlgorithmIdEqualsHashCode { @@ -40,7 +44,6 @@ AlgorithmId ai2 = AlgorithmId.get("DH"); AlgorithmId ai3 = AlgorithmId.get("DH"); - // supposedly transitivity is broken // System.out.println(ai1.equals(ai2)); // System.out.println(ai2.equals(ai3)); @@ -56,5 +59,42 @@ else throw new Exception("Failed equals()/hashCode() contract"); + // check that AlgorithmIds with same name but different params + // are not equal + AlgorithmParameters algParams1 = + AlgorithmParameters.getInstance("RSASSA-PSS"); + AlgorithmParameters algParams2 = + AlgorithmParameters.getInstance("RSASSA-PSS"); + algParams1.init(new PSSParameterSpec("SHA-1", "MGF1", + MGF1ParameterSpec.SHA1, 20, PSSParameterSpec.TRAILER_FIELD_BC)); + algParams2.init(new PSSParameterSpec("SHA-256", "MGF1", + MGF1ParameterSpec.SHA1, 20, PSSParameterSpec.TRAILER_FIELD_BC)); + ai1 = new AlgorithmId(AlgorithmId.RSASSA_PSS_oid, algParams1); + ai2 = new AlgorithmId(AlgorithmId.RSASSA_PSS_oid, algParams2); + if (ai1.equals(ai2)) { + throw new Exception("Failed equals() contract"); + } else { + System.out.println("PASSED equals() test"); + } + + // check that two AlgorithmIds created with the same parameters but + // one with DER encoded parameters and the other with + // AlgorithmParameters are equal + byte[] encoded = ai1.encode(); + ai3 = AlgorithmId.parse(new DerValue(encoded)); + if (!ai1.equals(ai3)) { + throw new Exception("Failed equals() contract"); + } else { + System.out.println("PASSED equals() test"); + } + + // check that two AlgorithmIds created with different parameters but + // one with DER encoded parameters and the other with + // AlgorithmParameters are not equal + if (ai2.equals(ai3)) { + throw new Exception("Failed equals() contract"); + } else { + System.out.println("PASSED equals() test"); + } } }