| /* |
| * Copyright (c) 2009, 2016, 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.AlgorithmConstraints; |
| import java.security.CryptoPrimitive; |
| import java.security.Timestamp; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.Set; |
| import java.util.EnumSet; |
| import java.math.BigInteger; |
| import java.security.PublicKey; |
| import java.security.KeyFactory; |
| import java.security.AlgorithmParameters; |
| import java.security.GeneralSecurityException; |
| import java.security.cert.Certificate; |
| import java.security.cert.X509CRL; |
| import java.security.cert.X509Certificate; |
| import java.security.cert.PKIXCertPathChecker; |
| import java.security.cert.TrustAnchor; |
| import java.security.cert.CRLException; |
| import java.security.cert.CertificateException; |
| import java.security.cert.CertPathValidatorException; |
| import java.security.cert.CertPathValidatorException.BasicReason; |
| import java.security.cert.PKIXReason; |
| import java.security.interfaces.DSAParams; |
| import java.security.interfaces.DSAPublicKey; |
| import java.security.spec.DSAPublicKeySpec; |
| |
| import sun.security.util.AnchorCertificates; |
| import sun.security.util.CertConstraintParameters; |
| import sun.security.util.Debug; |
| import sun.security.util.DisabledAlgorithmConstraints; |
| import sun.security.x509.X509CertImpl; |
| import sun.security.x509.X509CRLImpl; |
| import sun.security.x509.AlgorithmId; |
| |
| /** |
| * A {@code PKIXCertPathChecker} implementation to check whether a |
| * specified certificate contains the required algorithm constraints. |
| * <p> |
| * Certificate fields such as the subject public key, the signature |
| * algorithm, key usage, extended key usage, etc. need to conform to |
| * the specified algorithm constraints. |
| * |
| * @see PKIXCertPathChecker |
| * @see PKIXParameters |
| */ |
| public final class AlgorithmChecker extends PKIXCertPathChecker { |
| private static final Debug debug = Debug.getInstance("certpath"); |
| |
| private final AlgorithmConstraints constraints; |
| private final PublicKey trustedPubKey; |
| private final Date pkixdate; |
| private PublicKey prevPubKey; |
| private final Timestamp jarTimestamp; |
| |
| private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = |
| Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); |
| |
| private static final Set<CryptoPrimitive> KU_PRIMITIVE_SET = |
| Collections.unmodifiableSet(EnumSet.of( |
| CryptoPrimitive.SIGNATURE, |
| CryptoPrimitive.KEY_ENCAPSULATION, |
| CryptoPrimitive.PUBLIC_KEY_ENCRYPTION, |
| 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; |
| |
| /** |
| * Create a new {@code AlgorithmChecker} with the algorithm |
| * constraints specified in security property |
| * "jdk.certpath.disabledAlgorithms". |
| * |
| * @param anchor the trust anchor selected to validate the target |
| * certificate |
| */ |
| public AlgorithmChecker(TrustAnchor anchor) { |
| this(anchor, certPathDefaultConstraints, null); |
| } |
| |
| /** |
| * Create a new {@code AlgorithmChecker} with the |
| * given {@code TrustAnchor} and {@code AlgorithmConstraints}. |
| * |
| * @param anchor the trust anchor selected to validate the target |
| * certificate |
| * @param constraints the algorithm constraints (or null) |
| * |
| * @throws IllegalArgumentException if the {@code anchor} is null |
| */ |
| public AlgorithmChecker(TrustAnchor anchor, |
| AlgorithmConstraints constraints) { |
| this(anchor, constraints, null); |
| } |
| |
| /** |
| * Create a new {@code AlgorithmChecker} with the |
| * given {@code AlgorithmConstraints}. |
| * <p> |
| * Note that this constructor will be used to check a certification |
| * path where the trust anchor is unknown, or a certificate list which may |
| * contain the trust anchor. This constructor is used by SunJSSE. |
| * |
| * @param constraints the algorithm constraints (or null) |
| */ |
| public AlgorithmChecker(AlgorithmConstraints constraints) { |
| this.prevPubKey = null; |
| this.trustedPubKey = null; |
| this.constraints = constraints; |
| this.pkixdate = null; |
| this.jarTimestamp = null; |
| } |
| |
| /** |
| * Create a new {@code AlgorithmChecker} with the given |
| * {@code Timestamp}. |
| * <p> |
| * Note that this constructor will be used to check a certification |
| * path for signed JAR files that are timestamped. |
| * |
| * @param jarTimestamp Timestamp passed for JAR timestamp constraint |
| * checking. Set to null if not applicable. |
| */ |
| public AlgorithmChecker(Timestamp jarTimestamp) { |
| this.prevPubKey = null; |
| this.trustedPubKey = null; |
| this.constraints = certPathDefaultConstraints; |
| if (jarTimestamp == null) { |
| throw new IllegalArgumentException( |
| "Timestamp cannot be null"); |
| } |
| this.pkixdate = jarTimestamp.getTimestamp(); |
| this.jarTimestamp = jarTimestamp; |
| } |
| |
| /** |
| * Create a new {@code AlgorithmChecker} with the |
| * given {@code TrustAnchor} and {@code AlgorithmConstraints}. |
| * |
| * @param anchor the trust anchor selected to validate the target |
| * certificate |
| * @param constraints the algorithm constraints (or null) |
| * @param pkixdate Date the constraints are checked against. The value is |
| * either the PKIXParameter date or null for the current date. |
| * |
| * @throws IllegalArgumentException if the {@code anchor} is null |
| */ |
| public AlgorithmChecker(TrustAnchor anchor, |
| AlgorithmConstraints constraints, |
| Date pkixdate) { |
| |
| 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(); |
| } |
| } else { |
| this.trustedPubKey = null; |
| if (debug != null) { |
| debug.println("TrustAnchor is null, trustedMatch is false."); |
| } |
| } |
| |
| this.prevPubKey = trustedPubKey; |
| this.constraints = constraints; |
| this.pkixdate = pkixdate; |
| this.jarTimestamp = null; |
| } |
| |
| /** |
| * Create a new {@code AlgorithmChecker} with the |
| * given {@code TrustAnchor} and {@code PKIXParameter} date. |
| * |
| * @param anchor the trust anchor selected to validate the target |
| * certificate |
| * @param pkixdate Date the constraints are checked against. The value is |
| * either the PKIXParameter date or null for the current date. |
| * |
| * @throws IllegalArgumentException if the {@code anchor} is null |
| */ |
| public AlgorithmChecker(TrustAnchor anchor, Date pkixdate) { |
| this(anchor, certPathDefaultConstraints, pkixdate); |
| } |
| |
| // 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); |
| } |
| |
| Timestamp getJarTimestamp() { |
| return jarTimestamp; |
| } |
| |
| @Override |
| public void init(boolean forward) throws CertPathValidatorException { |
| // Note that this class does not support forward mode. |
| if (!forward) { |
| if (trustedPubKey != null) { |
| prevPubKey = trustedPubKey; |
| } else { |
| prevPubKey = null; |
| } |
| } else { |
| throw new |
| CertPathValidatorException("forward checking not supported"); |
| } |
| } |
| |
| @Override |
| public boolean isForwardCheckingSupported() { |
| // Note that as this class does not support forward mode, the method |
| // will always returns false. |
| return false; |
| } |
| |
| @Override |
| public Set<String> getSupportedExtensions() { |
| return null; |
| } |
| |
| @Override |
| public void check(Certificate cert, |
| Collection<String> unresolvedCritExts) |
| throws CertPathValidatorException { |
| |
| if (!(cert instanceof X509Certificate) || constraints == null) { |
| // ignore the check for non-x.509 certificate or null constraints |
| return; |
| } |
| |
| // check the key usage and key size |
| boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage(); |
| if (keyUsage != null && keyUsage.length < 9) { |
| throw new CertPathValidatorException( |
| "incorrect KeyUsage extension", |
| null, null, -1, PKIXReason.INVALID_KEY_USAGE); |
| } |
| |
| // Assume all key usage bits are set if key usage is not present |
| Set<CryptoPrimitive> primitives = KU_PRIMITIVE_SET; |
| |
| if (keyUsage != null) { |
| primitives = EnumSet.noneOf(CryptoPrimitive.class); |
| |
| if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { |
| // keyUsage[0]: KeyUsage.digitalSignature |
| // keyUsage[1]: KeyUsage.nonRepudiation |
| // keyUsage[5]: KeyUsage.keyCertSign |
| // keyUsage[6]: KeyUsage.cRLSign |
| primitives.add(CryptoPrimitive.SIGNATURE); |
| } |
| |
| if (keyUsage[2]) { // KeyUsage.keyEncipherment |
| primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); |
| } |
| |
| if (keyUsage[3]) { // KeyUsage.dataEncipherment |
| primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); |
| } |
| |
| if (keyUsage[4]) { // KeyUsage.keyAgreement |
| primitives.add(CryptoPrimitive.KEY_AGREEMENT); |
| } |
| |
| // KeyUsage.encipherOnly and KeyUsage.decipherOnly are |
| // undefined in the absence of the keyAgreement bit. |
| |
| if (primitives.isEmpty()) { |
| throw new CertPathValidatorException( |
| "incorrect KeyUsage extension bits", |
| null, null, -1, PKIXReason.INVALID_KEY_USAGE); |
| } |
| } |
| |
| PublicKey currPubKey = cert.getPublicKey(); |
| |
| if (constraints instanceof DisabledAlgorithmConstraints) { |
| // Check against DisabledAlgorithmConstraints certpath constraints. |
| // permits() will throw exception on failure. |
| ((DisabledAlgorithmConstraints)constraints).permits(primitives, |
| new CertConstraintParameters((X509Certificate)cert, |
| trustedMatch, pkixdate, jarTimestamp)); |
| // If there is no previous key, set one and exit |
| if (prevPubKey == null) { |
| prevPubKey = currPubKey; |
| return; |
| } |
| } |
| |
| X509CertImpl x509Cert; |
| AlgorithmId algorithmId; |
| try { |
| x509Cert = X509CertImpl.toImpl((X509Certificate)cert); |
| algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); |
| } catch (CertificateException ce) { |
| throw new CertPathValidatorException(ce); |
| } |
| |
| AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); |
| String currSigAlg = x509Cert.getSigAlgName(); |
| |
| // If 'constraints' is not of DisabledAlgorithmConstraints, check all |
| // everything individually |
| if (!(constraints instanceof DisabledAlgorithmConstraints)) { |
| // Check the current signature algorithm |
| if (!constraints.permits( |
| SIGNATURE_PRIMITIVE_SET, |
| currSigAlg, currSigAlgParams)) { |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on signature " + |
| "algorithm: " + currSigAlg, null, null, -1, |
| BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| |
| if (!constraints.permits(primitives, currPubKey)) { |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on keysize: " + |
| sun.security.util.KeyUtil.getKeySize(currPubKey), |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| } |
| |
| // Check with previous cert for signature algorithm and public key |
| if (prevPubKey != null) { |
| if (!constraints.permits( |
| SIGNATURE_PRIMITIVE_SET, |
| currSigAlg, prevPubKey, currSigAlgParams)) { |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on " + |
| "signature algorithm: " + currSigAlg, |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| |
| // Inherit key parameters from previous key |
| if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { |
| // Inherit DSA parameters from previous key |
| if (!(prevPubKey instanceof DSAPublicKey)) { |
| throw new CertPathValidatorException("Input key is not " + |
| "of a appropriate type for inheriting parameters"); |
| } |
| |
| DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); |
| if (params == null) { |
| throw new CertPathValidatorException( |
| "Key parameters missing from public key."); |
| } |
| |
| try { |
| BigInteger y = ((DSAPublicKey)currPubKey).getY(); |
| KeyFactory kf = KeyFactory.getInstance("DSA"); |
| DSAPublicKeySpec ks = new DSAPublicKeySpec(y, |
| params.getP(), |
| params.getQ(), |
| params.getG()); |
| currPubKey = kf.generatePublic(ks); |
| } catch (GeneralSecurityException e) { |
| throw new CertPathValidatorException("Unable to generate " + |
| "key with inherited parameters: " + e.getMessage(), e); |
| } |
| } |
| } |
| |
| // reset the previous public key |
| prevPubKey = currPubKey; |
| |
| // check the extended key usage, ignore the check now |
| // List<String> extendedKeyUsages = x509Cert.getExtendedKeyUsage(); |
| |
| // DO NOT remove any unresolved critical extensions |
| } |
| |
| /** |
| * Try to set the trust anchor of the checker. |
| * <p> |
| * If there is no trust anchor specified and the checker has not started, |
| * set the trust anchor. |
| * |
| * @param anchor the trust anchor selected to validate the target |
| * certificate |
| */ |
| void trySetTrustAnchor(TrustAnchor anchor) { |
| // Don't bother if the check has started or trust anchor has already |
| // specified. |
| if (prevPubKey == null) { |
| if (anchor == null) { |
| throw new IllegalArgumentException( |
| "The trust anchor cannot be null"); |
| } |
| |
| // 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(); |
| } |
| } |
| } |
| |
| /** |
| * Check the signature algorithm with the specified public key. |
| * |
| * @param key the public key to verify the CRL signature |
| * @param crl the target CRL |
| */ |
| static void check(PublicKey key, X509CRL crl) |
| throws CertPathValidatorException { |
| |
| X509CRLImpl x509CRLImpl = null; |
| try { |
| x509CRLImpl = X509CRLImpl.toImpl(crl); |
| } catch (CRLException ce) { |
| throw new CertPathValidatorException(ce); |
| } |
| |
| AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); |
| check(key, algorithmId); |
| } |
| |
| /** |
| * Check the signature algorithm with the specified public key. |
| * |
| * @param key the public key to verify the CRL signature |
| * @param algorithmId signature algorithm Algorithm ID |
| */ |
| static void check(PublicKey key, AlgorithmId algorithmId) |
| throws CertPathValidatorException { |
| String sigAlgName = algorithmId.getName(); |
| AlgorithmParameters sigAlgParams = algorithmId.getParameters(); |
| |
| if (!certPathDefaultConstraints.permits( |
| SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on signature algorithm: " + |
| sigAlgName + " is disabled", |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| } |
| |
| } |
| |