1 /*
2  * Copyright (c) 2000, 2012, 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.util.*;
29 import java.security.cert.*;
30 import java.security.cert.PKIXReason;
31 
32 import sun.security.util.Debug;
33 import static sun.security.x509.PKIXExtensions.*;
34 
35 /**
36  * KeyChecker is a <code>PKIXCertPathChecker</code> that checks that the
37  * keyCertSign bit is set in the keyUsage extension in an intermediate CA
38  * certificate. It also checks whether the final certificate in a
39  * certification path meets the specified target constraints specified as
40  * a CertSelector in the PKIXParameters passed to the CertPathValidator.
41  *
42  * @since       1.4
43  * @author      Yassir Elley
44  */
45 class KeyChecker extends PKIXCertPathChecker {
46 
47     private static final Debug debug = Debug.getInstance("certpath");
48     private final int certPathLen;
49     private final CertSelector targetConstraints;
50     private int remainingCerts;
51 
52     private Set<String> supportedExts;
53 
54     /**
55      * Creates a KeyChecker.
56      *
57      * @param certPathLen allowable cert path length
58      * @param targetCertSel a CertSelector object specifying the constraints
59      * on the target certificate
60      */
KeyChecker(int certPathLen, CertSelector targetCertSel)61     KeyChecker(int certPathLen, CertSelector targetCertSel) {
62         this.certPathLen = certPathLen;
63         this.targetConstraints = targetCertSel;
64     }
65 
66     /**
67      * Initializes the internal state of the checker from parameters
68      * specified in the constructor
69      */
70     @Override
init(boolean forward)71     public void init(boolean forward) throws CertPathValidatorException {
72         if (!forward) {
73             remainingCerts = certPathLen;
74         } else {
75             throw new CertPathValidatorException
76                 ("forward checking not supported");
77         }
78     }
79 
80     @Override
isForwardCheckingSupported()81     public boolean isForwardCheckingSupported() {
82         return false;
83     }
84 
85     @Override
getSupportedExtensions()86     public Set<String> getSupportedExtensions() {
87         if (supportedExts == null) {
88             supportedExts = new HashSet<String>(3);
89             supportedExts.add(KeyUsage_Id.toString());
90             supportedExts.add(ExtendedKeyUsage_Id.toString());
91             supportedExts.add(SubjectAlternativeName_Id.toString());
92             supportedExts = Collections.unmodifiableSet(supportedExts);
93         }
94         return supportedExts;
95     }
96 
97     /**
98      * Checks that keyUsage and target constraints are satisfied by
99      * the specified certificate.
100      *
101      * @param cert the Certificate
102      * @param unresolvedCritExts the unresolved critical extensions
103      * @throws CertPathValidatorException if certificate does not verify
104      */
105     @Override
check(Certificate cert, Collection<String> unresCritExts)106     public void check(Certificate cert, Collection<String> unresCritExts)
107         throws CertPathValidatorException
108     {
109         X509Certificate currCert = (X509Certificate)cert;
110 
111         remainingCerts--;
112 
113         // if final certificate, check that target constraints are satisfied
114         if (remainingCerts == 0) {
115             if (targetConstraints != null &&
116                 targetConstraints.match(currCert) == false) {
117                 throw new CertPathValidatorException("target certificate " +
118                     "constraints check failed");
119             }
120         } else {
121             // otherwise, verify that keyCertSign bit is set in CA certificate
122             verifyCAKeyUsage(currCert);
123         }
124 
125         // remove the extensions that we have checked
126         if (unresCritExts != null && !unresCritExts.isEmpty()) {
127             unresCritExts.remove(KeyUsage_Id.toString());
128             unresCritExts.remove(ExtendedKeyUsage_Id.toString());
129             unresCritExts.remove(SubjectAlternativeName_Id.toString());
130         }
131     }
132 
133     // the index of keyCertSign in the boolean KeyUsage array
134     private static final int KEY_CERT_SIGN = 5;
135     /**
136      * Verifies the key usage extension in a CA cert.
137      * The key usage extension, if present, must assert the keyCertSign bit.
138      * The extended key usage extension is not checked (see CR 4776794 for
139      * more information).
140      */
verifyCAKeyUsage(X509Certificate cert)141     static void verifyCAKeyUsage(X509Certificate cert)
142             throws CertPathValidatorException {
143         String msg = "CA key usage";
144         if (debug != null) {
145             debug.println("KeyChecker.verifyCAKeyUsage() ---checking " + msg
146                           + "...");
147         }
148 
149         boolean[] keyUsageBits = cert.getKeyUsage();
150 
151         // getKeyUsage returns null if the KeyUsage extension is not present
152         // in the certificate - in which case there is nothing to check
153         if (keyUsageBits == null) {
154             return;
155         }
156 
157         // throw an exception if the keyCertSign bit is not set
158         if (!keyUsageBits[KEY_CERT_SIGN]) {
159             throw new CertPathValidatorException
160                 (msg + " check failed: keyCertSign bit is not set", null,
161                  null, -1, PKIXReason.INVALID_KEY_USAGE);
162         }
163 
164         if (debug != null) {
165             debug.println("KeyChecker.verifyCAKeyUsage() " + msg
166                           + " verified.");
167         }
168     }
169 }
170