Home | History | Annotate | Download | only in certpath
      1 /*
      2  * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.security.provider.certpath;
     27 
     28 import java.security.AlgorithmConstraints;
     29 import java.security.CryptoPrimitive;
     30 import java.util.Collection;
     31 import java.util.Collections;
     32 import java.util.Set;
     33 import java.util.EnumSet;
     34 import java.math.BigInteger;
     35 import java.security.PublicKey;
     36 import java.security.KeyFactory;
     37 import java.security.AlgorithmParameters;
     38 import java.security.GeneralSecurityException;
     39 import java.security.cert.Certificate;
     40 import java.security.cert.X509CRL;
     41 import java.security.cert.X509Certificate;
     42 import java.security.cert.PKIXCertPathChecker;
     43 import java.security.cert.TrustAnchor;
     44 import java.security.cert.CRLException;
     45 import java.security.cert.CertificateException;
     46 import java.security.cert.CertPathValidatorException;
     47 import java.security.cert.CertPathValidatorException.BasicReason;
     48 import java.security.cert.PKIXReason;
     49 import java.security.interfaces.DSAParams;
     50 import java.security.interfaces.DSAPublicKey;
     51 import java.security.spec.DSAPublicKeySpec;
     52 
     53 import sun.security.util.AnchorCertificates;
     54 import sun.security.util.CertConstraintParameters;
     55 import sun.security.util.Debug;
     56 import sun.security.util.DisabledAlgorithmConstraints;
     57 import sun.security.x509.X509CertImpl;
     58 import sun.security.x509.X509CRLImpl;
     59 import sun.security.x509.AlgorithmId;
     60 
     61 /**
     62  * A <code>PKIXCertPathChecker</code> implementation to check whether a
     63  * specified certificate contains the required algorithm constraints.
     64  * <p>
     65  * Certificate fields such as the subject public key, the signature
     66  * algorithm, key usage, extended key usage, etc. need to conform to
     67  * the specified algorithm constraints.
     68  *
     69  * @see PKIXCertPathChecker
     70  * @see PKIXParameters
     71  */
     72 final public class AlgorithmChecker extends PKIXCertPathChecker {
     73     private static final Debug debug = Debug.getInstance("certpath");
     74 
     75     private final AlgorithmConstraints constraints;
     76     private final PublicKey trustedPubKey;
     77     private PublicKey prevPubKey;
     78 
     79     private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
     80         Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
     81 
     82     private final static Set<CryptoPrimitive> KU_PRIMITIVE_SET =
     83         Collections.unmodifiableSet(EnumSet.of(
     84             CryptoPrimitive.SIGNATURE,
     85             CryptoPrimitive.KEY_ENCAPSULATION,
     86             CryptoPrimitive.PUBLIC_KEY_ENCRYPTION,
     87             CryptoPrimitive.KEY_AGREEMENT));
     88 
     89     private final static DisabledAlgorithmConstraints
     90         certPathDefaultConstraints = new DisabledAlgorithmConstraints(
     91             DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
     92 
     93     // If there is no "cacerts" keyword, then disable anchor checking
     94     private static final boolean publicCALimits =
     95             certPathDefaultConstraints.checkProperty("jdkCA");
     96 
     97     // If anchor checking enabled, this will be true if the trust anchor
     98     // has a match in the cacerts file
     99     private boolean trustedMatch = false;
    100 
    101     /**
    102      * Create a new <code>AlgorithmChecker</code> with the algorithm
    103      * constraints specified in security property
    104      * "jdk.certpath.disabledAlgorithms".
    105      *
    106      * @param anchor the trust anchor selected to validate the target
    107      *     certificate
    108      */
    109     public AlgorithmChecker(TrustAnchor anchor) {
    110         this(anchor, certPathDefaultConstraints);
    111     }
    112 
    113     /**
    114      * Create a new <code>AlgorithmChecker</code> with the
    115      * given {@code AlgorithmConstraints}.
    116      * <p>
    117      * Note that this constructor will be used to check a certification
    118      * path where the trust anchor is unknown, or a certificate list which may
    119      * contain the trust anchor. This constructor is used by SunJSSE.
    120      *
    121      * @param constraints the algorithm constraints (or null)
    122      */
    123     public AlgorithmChecker(AlgorithmConstraints constraints) {
    124         this.prevPubKey = null;
    125         this.trustedPubKey = null;
    126         this.constraints = constraints;
    127     }
    128 
    129     /**
    130      * Create a new <code>AlgorithmChecker</code> with the
    131      * given <code>TrustAnchor</code> and <code>AlgorithmConstraints</code>.
    132      *
    133      * @param anchor the trust anchor selected to validate the target
    134      *     certificate
    135      * @param constraints the algorithm constraints (or null)
    136      *
    137      * @throws IllegalArgumentException if the <code>anchor</code> is null
    138      */
    139     public AlgorithmChecker(TrustAnchor anchor,
    140             AlgorithmConstraints constraints) {
    141 
    142         if (anchor == null) {
    143             throw new IllegalArgumentException(
    144                         "The trust anchor cannot be null");
    145         }
    146 
    147         if (anchor.getTrustedCert() != null) {
    148             this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
    149             // Check for anchor certificate restrictions
    150             trustedMatch = checkFingerprint(anchor.getTrustedCert());
    151             if (trustedMatch && debug != null) {
    152                 debug.println("trustedMatch = true");
    153             }
    154         } else {
    155             this.trustedPubKey = anchor.getCAPublicKey();
    156         }
    157 
    158         this.prevPubKey = trustedPubKey;
    159         this.constraints = constraints;
    160     }
    161 
    162     // Check this 'cert' for restrictions in the AnchorCertificates
    163     // trusted certificates list
    164     private static boolean checkFingerprint(X509Certificate cert) {
    165         if (!publicCALimits) {
    166             return false;
    167         }
    168 
    169         if (debug != null) {
    170             debug.println("AlgorithmChecker.contains: " + cert.getSigAlgName());
    171         }
    172         return AnchorCertificates.contains(cert);
    173     }
    174 
    175     @Override
    176     public void init(boolean forward) throws CertPathValidatorException {
    177         //  Note that this class does not support forward mode.
    178         if (!forward) {
    179             if (trustedPubKey != null) {
    180                 prevPubKey = trustedPubKey;
    181             } else {
    182                 prevPubKey = null;
    183             }
    184         } else {
    185             throw new
    186                 CertPathValidatorException("forward checking not supported");
    187         }
    188     }
    189 
    190     @Override
    191     public boolean isForwardCheckingSupported() {
    192         //  Note that as this class does not support forward mode, the method
    193         //  will always returns false.
    194         return false;
    195     }
    196 
    197     @Override
    198     public Set<String> getSupportedExtensions() {
    199         return null;
    200     }
    201 
    202     @Override
    203     public void check(Certificate cert,
    204             Collection<String> unresolvedCritExts)
    205             throws CertPathValidatorException {
    206 
    207         if (!(cert instanceof X509Certificate) || constraints == null) {
    208             // ignore the check for non-x.509 certificate or null constraints
    209             return;
    210         }
    211 
    212         // check the key usage and key size
    213         boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage();
    214         if (keyUsage != null && keyUsage.length < 9) {
    215             throw new CertPathValidatorException(
    216                 "incorrect KeyUsage extension",
    217                 null, null, -1, PKIXReason.INVALID_KEY_USAGE);
    218         }
    219 
    220         // Assume all key usage bits are set if key usage is not present
    221         Set<CryptoPrimitive> primitives = KU_PRIMITIVE_SET;
    222 
    223         if (keyUsage != null) {
    224             primitives = EnumSet.noneOf(CryptoPrimitive.class);
    225 
    226             if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) {
    227                 // keyUsage[0]: KeyUsage.digitalSignature
    228                 // keyUsage[1]: KeyUsage.nonRepudiation
    229                 // keyUsage[5]: KeyUsage.keyCertSign
    230                 // keyUsage[6]: KeyUsage.cRLSign
    231                 primitives.add(CryptoPrimitive.SIGNATURE);
    232             }
    233 
    234             if (keyUsage[2]) {      // KeyUsage.keyEncipherment
    235                 primitives.add(CryptoPrimitive.KEY_ENCAPSULATION);
    236             }
    237 
    238             if (keyUsage[3]) {      // KeyUsage.dataEncipherment
    239                 primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION);
    240             }
    241 
    242             if (keyUsage[4]) {      // KeyUsage.keyAgreement
    243                 primitives.add(CryptoPrimitive.KEY_AGREEMENT);
    244             }
    245 
    246             // KeyUsage.encipherOnly and KeyUsage.decipherOnly are
    247             // undefined in the absence of the keyAgreement bit.
    248 
    249             if (primitives.isEmpty()) {
    250                 throw new CertPathValidatorException(
    251                     "incorrect KeyUsage extension bits",
    252                     null, null, -1, PKIXReason.INVALID_KEY_USAGE);
    253             }
    254         }
    255 
    256         PublicKey currPubKey = cert.getPublicKey();
    257 
    258         if (constraints instanceof DisabledAlgorithmConstraints) {
    259             // Check against DisabledAlgorithmConstraints certpath constraints.
    260             // permits() will throw exception on failure.
    261             ((DisabledAlgorithmConstraints)constraints).permits(primitives,
    262                 new CertConstraintParameters((X509Certificate)cert,
    263                         trustedMatch));
    264             // If there is no previous key, set one and exit
    265             if (prevPubKey == null) {
    266                 prevPubKey = currPubKey;
    267                 return;
    268             }
    269         }
    270 
    271         X509CertImpl x509Cert;
    272         AlgorithmId algorithmId;
    273         try {
    274             x509Cert = X509CertImpl.toImpl((X509Certificate)cert);
    275             algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG);
    276         } catch (CertificateException ce) {
    277             throw new CertPathValidatorException(ce);
    278         }
    279 
    280         AlgorithmParameters currSigAlgParams = algorithmId.getParameters();
    281         String currSigAlg = x509Cert.getSigAlgName();
    282 
    283         // If 'constraints' is not of DisabledAlgorithmConstraints, check all
    284         // everything individually
    285         if (!(constraints instanceof DisabledAlgorithmConstraints)) {
    286             // Check the current signature algorithm
    287             if (!constraints.permits(
    288                     SIGNATURE_PRIMITIVE_SET,
    289                     currSigAlg, currSigAlgParams)) {
    290                 throw new CertPathValidatorException(
    291                         "Algorithm constraints check failed on signature " +
    292                                 "algorithm: " + currSigAlg, null, null, -1,
    293                         BasicReason.ALGORITHM_CONSTRAINED);
    294             }
    295 
    296         if (!constraints.permits(primitives, currPubKey)) {
    297             throw new CertPathValidatorException(
    298                         "Algorithm constraints check failed on keysize: " +
    299                                 sun.security.util.KeyUtil.getKeySize(currPubKey),
    300                 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
    301         }
    302         }
    303 
    304         // Check with previous cert for signature algorithm and public key
    305         if (prevPubKey != null) {
    306                 if (!constraints.permits(
    307                         SIGNATURE_PRIMITIVE_SET,
    308                         currSigAlg, prevPubKey, currSigAlgParams)) {
    309                     throw new CertPathValidatorException(
    310                     "Algorithm constraints check failed on " +
    311                             "signature algorithm: " + currSigAlg,
    312                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
    313                 }
    314 
    315             // Inherit key parameters from previous key
    316             if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) {
    317                 // Inherit DSA parameters from previous key
    318                 if (!(prevPubKey instanceof DSAPublicKey)) {
    319                     throw new CertPathValidatorException("Input key is not " +
    320                         "of a appropriate type for inheriting parameters");
    321                 }
    322 
    323                 DSAParams params = ((DSAPublicKey)prevPubKey).getParams();
    324                 if (params == null) {
    325                     throw new CertPathValidatorException(
    326                         "Key parameters missing from public key.");
    327                 }
    328 
    329                 try {
    330                     BigInteger y = ((DSAPublicKey)currPubKey).getY();
    331                     KeyFactory kf = KeyFactory.getInstance("DSA");
    332                     DSAPublicKeySpec ks = new DSAPublicKeySpec(y,
    333                                                        params.getP(),
    334                                                        params.getQ(),
    335                                                        params.getG());
    336                     currPubKey = kf.generatePublic(ks);
    337                 } catch (GeneralSecurityException e) {
    338                     throw new CertPathValidatorException("Unable to generate " +
    339                         "key with inherited parameters: " + e.getMessage(), e);
    340                 }
    341             }
    342         }
    343 
    344         // reset the previous public key
    345         prevPubKey = currPubKey;
    346 
    347         // check the extended key usage, ignore the check now
    348         // List<String> extendedKeyUsages = x509Cert.getExtendedKeyUsage();
    349 
    350         // DO NOT remove any unresolved critical extensions
    351     }
    352 
    353     /**
    354      * Try to set the trust anchor of the checker.
    355      * <p>
    356      * If there is no trust anchor specified and the checker has not started,
    357      * set the trust anchor.
    358      *
    359      * @param anchor the trust anchor selected to validate the target
    360      *     certificate
    361      */
    362     void trySetTrustAnchor(TrustAnchor anchor) {
    363         // Don't bother if the check has started or trust anchor has already
    364         // specified.
    365         if (prevPubKey == null) {
    366             if (anchor == null) {
    367                 throw new IllegalArgumentException(
    368                         "The trust anchor cannot be null");
    369             }
    370 
    371             // Don't bother to change the trustedPubKey.
    372             if (anchor.getTrustedCert() != null) {
    373                 prevPubKey = anchor.getTrustedCert().getPublicKey();
    374                 // Check for anchor certificate restrictions
    375                 trustedMatch = checkFingerprint(anchor.getTrustedCert());
    376                 if (trustedMatch && debug != null) {
    377                     debug.println("trustedMatch = true");
    378                 }
    379             } else {
    380                 prevPubKey = anchor.getCAPublicKey();
    381             }
    382         }
    383     }
    384 
    385     /**
    386      * Check the signature algorithm with the specified public key.
    387      *
    388      * @param key the public key to verify the CRL signature
    389      * @param crl the target CRL
    390      */
    391     static void check(PublicKey key, X509CRL crl)
    392                         throws CertPathValidatorException {
    393 
    394         X509CRLImpl x509CRLImpl = null;
    395         try {
    396             x509CRLImpl = X509CRLImpl.toImpl(crl);
    397         } catch (CRLException ce) {
    398             throw new CertPathValidatorException(ce);
    399         }
    400 
    401         AlgorithmId algorithmId = x509CRLImpl.getSigAlgId();
    402         check(key, algorithmId);
    403     }
    404 
    405     /**
    406      * Check the signature algorithm with the specified public key.
    407      *
    408      * @param key the public key to verify the CRL signature
    409      * @param crl the target CRL
    410      */
    411     static void check(PublicKey key, AlgorithmId algorithmId)
    412                         throws CertPathValidatorException {
    413         String sigAlgName = algorithmId.getName();
    414         AlgorithmParameters sigAlgParams = algorithmId.getParameters();
    415 
    416         if (!certPathDefaultConstraints.permits(
    417                 SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) {
    418             throw new CertPathValidatorException(
    419                 "Algorithm constraints check failed on signature algorithm: " +
    420                 sigAlgName + " is disabled",
    421                 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
    422         }
    423     }
    424 
    425 }
    426