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.io.IOException; 29 import java.security.cert.Certificate; 30 import java.security.cert.CertificateException; 31 import java.security.cert.CertPathValidatorException; 32 import java.security.cert.PKIXCertPathChecker; 33 import java.security.cert.PKIXReason; 34 import java.security.cert.X509Certificate; 35 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.HashSet; 38 import java.util.Set; 39 40 import sun.security.util.Debug; 41 import static sun.security.x509.PKIXExtensions.*; 42 import sun.security.x509.NameConstraintsExtension; 43 import sun.security.x509.X509CertImpl; 44 45 /** 46 * ConstraintsChecker is a <code>PKIXCertPathChecker</code> that checks 47 * constraints information on a PKIX certificate, namely basic constraints 48 * and name constraints. 49 * 50 * @since 1.4 51 * @author Yassir Elley 52 */ 53 class ConstraintsChecker extends PKIXCertPathChecker { 54 55 private static final Debug debug = Debug.getInstance("certpath"); 56 /* length of cert path */ 57 private final int certPathLength; 58 /* current maximum path length (as defined in PKIX) */ 59 private int maxPathLength; 60 /* current index of cert */ 61 private int i; 62 private NameConstraintsExtension prevNC; 63 64 private Set<String> supportedExts; 65 66 /** 67 * Creates a ConstraintsChecker. 68 * 69 * @param certPathLength the length of the certification path 70 */ 71 ConstraintsChecker(int certPathLength) { 72 this.certPathLength = certPathLength; 73 } 74 75 @Override 76 public void init(boolean forward) throws CertPathValidatorException { 77 if (!forward) { 78 i = 0; 79 maxPathLength = certPathLength; 80 prevNC = null; 81 } else { 82 throw new CertPathValidatorException 83 ("forward checking not supported"); 84 } 85 } 86 87 @Override 88 public boolean isForwardCheckingSupported() { 89 return false; 90 } 91 92 @Override 93 public Set<String> getSupportedExtensions() { 94 if (supportedExts == null) { 95 supportedExts = new HashSet<String>(2); 96 supportedExts.add(BasicConstraints_Id.toString()); 97 supportedExts.add(NameConstraints_Id.toString()); 98 supportedExts = Collections.unmodifiableSet(supportedExts); 99 } 100 return supportedExts; 101 } 102 103 /** 104 * Performs the basic constraints and name constraints 105 * checks on the certificate using its internal state. 106 * 107 * @param cert the <code>Certificate</code> to be checked 108 * @param unresCritExts a <code>Collection</code> of OID strings 109 * representing the current set of unresolved critical extensions 110 * @throws CertPathValidatorException if the specified certificate 111 * does not pass the check 112 */ 113 @Override 114 public void check(Certificate cert, Collection<String> unresCritExts) 115 throws CertPathValidatorException 116 { 117 X509Certificate currCert = (X509Certificate)cert; 118 119 i++; 120 // MUST run NC check second, since it depends on BC check to 121 // update remainingCerts 122 checkBasicConstraints(currCert); 123 verifyNameConstraints(currCert); 124 125 if (unresCritExts != null && !unresCritExts.isEmpty()) { 126 unresCritExts.remove(BasicConstraints_Id.toString()); 127 unresCritExts.remove(NameConstraints_Id.toString()); 128 } 129 } 130 131 /** 132 * Internal method to check the name constraints against a cert 133 */ 134 private void verifyNameConstraints(X509Certificate currCert) 135 throws CertPathValidatorException 136 { 137 String msg = "name constraints"; 138 if (debug != null) { 139 debug.println("---checking " + msg + "..."); 140 } 141 142 // check name constraints only if there is a previous name constraint 143 // and either the currCert is the final cert or the currCert is not 144 // self-issued 145 if (prevNC != null && ((i == certPathLength) || 146 !X509CertImpl.isSelfIssued(currCert))) { 147 if (debug != null) { 148 debug.println("prevNC = " + prevNC); 149 debug.println("currDN = " + currCert.getSubjectX500Principal()); 150 } 151 152 try { 153 if (!prevNC.verify(currCert)) { 154 throw new CertPathValidatorException(msg + " check failed", 155 null, null, -1, PKIXReason.INVALID_NAME); 156 } 157 } catch (IOException ioe) { 158 throw new CertPathValidatorException(ioe); 159 } 160 } 161 162 // merge name constraints regardless of whether cert is self-issued 163 prevNC = mergeNameConstraints(currCert, prevNC); 164 165 if (debug != null) 166 debug.println(msg + " verified."); 167 } 168 169 /** 170 * Helper to fold sets of name constraints together 171 */ 172 static NameConstraintsExtension mergeNameConstraints( 173 X509Certificate currCert, NameConstraintsExtension prevNC) 174 throws CertPathValidatorException 175 { 176 X509CertImpl currCertImpl; 177 try { 178 currCertImpl = X509CertImpl.toImpl(currCert); 179 } catch (CertificateException ce) { 180 throw new CertPathValidatorException(ce); 181 } 182 183 NameConstraintsExtension newConstraints = 184 currCertImpl.getNameConstraintsExtension(); 185 186 if (debug != null) { 187 debug.println("prevNC = " + prevNC); 188 debug.println("newNC = " + String.valueOf(newConstraints)); 189 } 190 191 // if there are no previous name constraints, we just return the 192 // new name constraints. 193 if (prevNC == null) { 194 if (debug != null) { 195 debug.println("mergedNC = " + String.valueOf(newConstraints)); 196 } 197 if (newConstraints == null) { 198 return newConstraints; 199 } else { 200 // Make sure we do a clone here, because we're probably 201 // going to modify this object later and we don't want to 202 // be sharing it with a Certificate object! 203 return (NameConstraintsExtension)newConstraints.clone(); 204 } 205 } else { 206 try { 207 // after merge, prevNC should contain the merged constraints 208 prevNC.merge(newConstraints); 209 } catch (IOException ioe) { 210 throw new CertPathValidatorException(ioe); 211 } 212 if (debug != null) { 213 debug.println("mergedNC = " + prevNC); 214 } 215 return prevNC; 216 } 217 } 218 219 /** 220 * Internal method to check that a given cert meets basic constraints. 221 */ 222 private void checkBasicConstraints(X509Certificate currCert) 223 throws CertPathValidatorException 224 { 225 String msg = "basic constraints"; 226 if (debug != null) { 227 debug.println("---checking " + msg + "..."); 228 debug.println("i = " + i); 229 debug.println("maxPathLength = " + maxPathLength); 230 } 231 232 /* check if intermediate cert */ 233 if (i < certPathLength) { 234 // RFC5280: If certificate i is a version 3 certificate, verify 235 // that the basicConstraints extension is present and that cA is 236 // set to TRUE. (If certificate i is a version 1 or version 2 237 // certificate, then the application MUST either verify that 238 // certificate i is a CA certificate through out-of-band means 239 // or reject the certificate. Conforming implementations may 240 // choose to reject all version 1 and version 2 intermediate 241 // certificates.) 242 // 243 // We choose to reject all version 1 and version 2 intermediate 244 // certificates except that it is self issued by the trust 245 // anchor in order to support key rollover or changes in 246 // certificate policies. 247 int pathLenConstraint = -1; 248 if (currCert.getVersion() < 3) { // version 1 or version 2 249 if (i == 1) { // issued by a trust anchor 250 if (X509CertImpl.isSelfIssued(currCert)) { 251 pathLenConstraint = Integer.MAX_VALUE; 252 } 253 } 254 } else { 255 pathLenConstraint = currCert.getBasicConstraints(); 256 } 257 258 if (pathLenConstraint == -1) { 259 throw new CertPathValidatorException 260 (msg + " check failed: this is not a CA certificate", 261 null, null, -1, PKIXReason.NOT_CA_CERT); 262 } 263 264 if (!X509CertImpl.isSelfIssued(currCert)) { 265 if (maxPathLength <= 0) { 266 throw new CertPathValidatorException 267 (msg + " check failed: pathLenConstraint violated - " 268 + "this cert must be the last cert in the " 269 + "certification path", null, null, -1, 270 PKIXReason.PATH_TOO_LONG); 271 } 272 maxPathLength--; 273 } 274 if (pathLenConstraint < maxPathLength) 275 maxPathLength = pathLenConstraint; 276 } 277 278 if (debug != null) { 279 debug.println("after processing, maxPathLength = " + maxPathLength); 280 debug.println(msg + " verified."); 281 } 282 } 283 284 /** 285 * Merges the specified maxPathLength with the pathLenConstraint 286 * obtained from the certificate. 287 * 288 * @param cert the <code>X509Certificate</code> 289 * @param maxPathLength the previous maximum path length 290 * @return the new maximum path length constraint (-1 means no more 291 * certificates can follow, Integer.MAX_VALUE means path length is 292 * unconstrained) 293 */ 294 static int mergeBasicConstraints(X509Certificate cert, int maxPathLength) { 295 296 int pathLenConstraint = cert.getBasicConstraints(); 297 298 if (!X509CertImpl.isSelfIssued(cert)) { 299 maxPathLength--; 300 } 301 302 if (pathLenConstraint < maxPathLength) { 303 maxPathLength = pathLenConstraint; 304 } 305 306 return maxPathLength; 307 } 308 } 309