Showing the changes to java/pkg/sun/security/provider/certpath/OCSPChecker.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,446 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2003, 2010, 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.math.BigInteger; | |
| 29 | +import java.util.*; | |
| 30 | +import java.security.AccessController; | |
| 31 | +import java.security.PrivilegedAction; | |
| 32 | +import java.security.Security; | |
| 33 | +import java.security.cert.*; | |
| 34 | +import java.security.cert.CertPathValidatorException.BasicReason; | |
| 35 | +import java.net.URI; | |
| 36 | +import java.net.URISyntaxException; | |
| 37 | +import javax.security.auth.x500.X500Principal; | |
| 38 | + | |
| 39 | +import static sun.security.provider.certpath.OCSP.*; | |
| 40 | +import sun.security.util.Debug; | |
| 41 | +import sun.security.x509.*; | |
| 42 | + | |
| 43 | +/** | |
| 44 | + * OCSPChecker is a <code>PKIXCertPathChecker</code> that uses the | |
| 45 | + * Online Certificate Status Protocol (OCSP) as specified in RFC 2560 | |
| 46 | + * <a href="http://www.ietf.org/rfc/rfc2560.txt"> | |
| 47 | + * http://www.ietf.org/rfc/rfc2560.txt</a>. | |
| 48 | + * | |
| 49 | + * @author Ram Marti | |
| 50 | + */ | |
| 51 | +class OCSPChecker extends PKIXCertPathChecker { | |
| 52 | + | |
| 53 | + static final String OCSP_ENABLE_PROP = "ocsp.enable"; | |
| 54 | + static final String OCSP_URL_PROP = "ocsp.responderURL"; | |
| 55 | + static final String OCSP_CERT_SUBJECT_PROP = | |
| 56 | + "ocsp.responderCertSubjectName"; | |
| 57 | + static final String OCSP_CERT_ISSUER_PROP = "ocsp.responderCertIssuerName"; | |
| 58 | + static final String OCSP_CERT_NUMBER_PROP = | |
| 59 | + "ocsp.responderCertSerialNumber"; | |
| 60 | + | |
| 61 | + private static final String HEX_DIGITS = "0123456789ABCDEFabcdef"; | |
| 62 | + private static final Debug DEBUG = Debug.getInstance("certpath"); | |
| 63 | + private static final boolean dump = false; | |
| 64 | + | |
| 65 | + private int remainingCerts; | |
| 66 | + | |
| 67 | + private X509Certificate[] certs; | |
| 68 | + | |
| 69 | + private CertPath cp; | |
| 70 | + | |
| 71 | + private PKIXParameters pkixParams; | |
| 72 | + | |
| 73 | + private boolean onlyEECert = false; | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * Default Constructor | |
| 77 | + * | |
| 78 | + * @param certPath the X509 certification path | |
| 79 | + * @param pkixParams the input PKIX parameter set | |
| 80 | + * @throws CertPathValidatorException if OCSPChecker can not be created | |
| 81 | + */ | |
| 82 | + OCSPChecker(CertPath certPath, PKIXParameters pkixParams) | |
| 83 | + throws CertPathValidatorException { | |
| 84 | + this(certPath, pkixParams, false); | |
| 85 | + } | |
| 86 | + | |
| 87 | + OCSPChecker(CertPath certPath, PKIXParameters pkixParams, boolean onlyEECert) | |
| 88 | + throws CertPathValidatorException { | |
| 89 | + | |
| 90 | + this.cp = certPath; | |
| 91 | + this.pkixParams = pkixParams; | |
| 92 | + this.onlyEECert = onlyEECert; | |
| 93 | + List<? extends Certificate> tmp = cp.getCertificates(); | |
| 94 | + certs = tmp.toArray(new X509Certificate[tmp.size()]); | |
| 95 | + init(false); | |
| 96 | + } | |
| 97 | + | |
| 98 | + /** | |
| 99 | + * Initializes the internal state of the checker from parameters | |
| 100 | + * specified in the constructor | |
| 101 | + */ | |
| 102 | + @Override | |
| 103 | + public void init(boolean forward) throws CertPathValidatorException { | |
| 104 | + if (!forward) { | |
| 105 | + remainingCerts = certs.length + 1; | |
| 106 | + } else { | |
| 107 | + throw new CertPathValidatorException( | |
| 108 | + "Forward checking not supported"); | |
| 109 | + } | |
| 110 | + } | |
| 111 | + | |
| 112 | + @Override public boolean isForwardCheckingSupported() { | |
| 113 | + return false; | |
| 114 | + } | |
| 115 | + | |
| 116 | + @Override public Set<String> getSupportedExtensions() { | |
| 117 | + return Collections.<String>emptySet(); | |
| 118 | + } | |
| 119 | + | |
| 120 | + /** | |
| 121 | + * Sends an OCSPRequest for the certificate to the OCSP Server and | |
| 122 | + * processes the response back from the OCSP Server. | |
| 123 | + * | |
| 124 | + * @param cert the Certificate | |
| 125 | + * @param unresolvedCritExts the unresolved critical extensions | |
| 126 | + * @exception CertPathValidatorException Exception is thrown if the | |
| 127 | + * certificate has been revoked. | |
| 128 | + */ | |
| 129 | + @Override | |
| 130 | + public void check(Certificate cert, Collection<String> unresolvedCritExts) | |
| 131 | + throws CertPathValidatorException { | |
| 132 | + | |
| 133 | + // Decrement the certificate counter | |
| 134 | + remainingCerts--; | |
| 135 | + | |
| 136 | + X509CertImpl currCertImpl = null; | |
| 137 | + try { | |
| 138 | + currCertImpl = X509CertImpl.toImpl((X509Certificate)cert); | |
| 139 | + } catch (CertificateException ce) { | |
| 140 | + throw new CertPathValidatorException(ce); | |
| 141 | + } | |
| 142 | + | |
| 143 | + if (onlyEECert && currCertImpl.getBasicConstraints() != -1) { | |
| 144 | + if (DEBUG != null) { | |
| 145 | + DEBUG.println("Skipping revocation check, not end entity cert"); | |
| 146 | + } | |
| 147 | + return; | |
| 148 | + } | |
| 149 | + | |
| 150 | + /* | |
| 151 | + * OCSP security property values, in the following order: | |
| 152 | + * 1. ocsp.responderURL | |
| 153 | + * 2. ocsp.responderCertSubjectName | |
| 154 | + * 3. ocsp.responderCertIssuerName | |
| 155 | + * 4. ocsp.responderCertSerialNumber | |
| 156 | + */ | |
| 157 | + // should cache these properties to avoid calling every time? | |
| 158 | + String[] properties = getOCSPProperties(); | |
| 159 | + | |
| 160 | + // Check whether OCSP is feasible before seeking cert information | |
| 161 | + URI uri = getOCSPServerURI(currCertImpl, properties[0]); | |
| 162 | + | |
| 163 | + // When responder's subject name is set then the issuer/serial | |
| 164 | + // properties are ignored | |
| 165 | + X500Principal responderSubjectName = null; | |
| 166 | + X500Principal responderIssuerName = null; | |
| 167 | + BigInteger responderSerialNumber = null; | |
| 168 | + if (properties[1] != null) { | |
| 169 | + responderSubjectName = new X500Principal(properties[1]); | |
| 170 | + } else if (properties[2] != null && properties[3] != null) { | |
| 171 | + responderIssuerName = new X500Principal(properties[2]); | |
| 172 | + // remove colon or space separators | |
| 173 | + String value = stripOutSeparators(properties[3]); | |
| 174 | + responderSerialNumber = new BigInteger(value, 16); | |
| 175 | + } else if (properties[2] != null || properties[3] != null) { | |
| 176 | + throw new CertPathValidatorException( | |
| 177 | + "Must specify both ocsp.responderCertIssuerName and " + | |
| 178 | + "ocsp.responderCertSerialNumber properties"); | |
| 179 | + } | |
| 180 | + | |
| 181 | + // If the OCSP responder cert properties are set then the | |
| 182 | + // identified cert must be located in the trust anchors or | |
| 183 | + // in the cert stores. | |
| 184 | + boolean seekResponderCert = false; | |
| 185 | + if (responderSubjectName != null || responderIssuerName != null) { | |
| 186 | + seekResponderCert = true; | |
| 187 | + } | |
| 188 | + | |
| 189 | + // Set the issuer certificate to the next cert in the chain | |
| 190 | + // (unless we're processing the final cert). | |
| 191 | + X509Certificate issuerCert = null; | |
| 192 | + boolean seekIssuerCert = true; | |
| 193 | + X509Certificate responderCert = null; | |
| 194 | + if (remainingCerts < certs.length) { | |
| 195 | + issuerCert = certs[remainingCerts]; | |
| 196 | + seekIssuerCert = false; // done | |
| 197 | + | |
| 198 | + // By default, the OCSP responder's cert is the same as the | |
| 199 | + // issuer of the cert being validated. | |
| 200 | + if (!seekResponderCert) { | |
| 201 | + responderCert = issuerCert; | |
| 202 | + if (DEBUG != null) { | |
| 203 | + DEBUG.println("Responder's certificate is the same " + | |
| 204 | + "as the issuer of the certificate being validated"); | |
| 205 | + } | |
| 206 | + } | |
| 207 | + } | |
| 208 | + | |
| 209 | + // Check anchor certs for: | |
| 210 | + // - the issuer cert (of the cert being validated) | |
| 211 | + // - the OCSP responder's cert | |
| 212 | + if (seekIssuerCert || seekResponderCert) { | |
| 213 | + | |
| 214 | + if (DEBUG != null && seekResponderCert) { | |
| 215 | + DEBUG.println("Searching trust anchors for responder's " + | |
| 216 | + "certificate"); | |
| 217 | + } | |
| 218 | + | |
| 219 | + // Extract the anchor certs | |
| 220 | + Iterator<TrustAnchor> anchors | |
| 221 | + = pkixParams.getTrustAnchors().iterator(); | |
| 222 | + if (!anchors.hasNext()) { | |
| 223 | + throw new CertPathValidatorException( | |
| 224 | + "Must specify at least one trust anchor"); | |
| 225 | + } | |
| 226 | + | |
| 227 | + X500Principal certIssuerName = | |
| 228 | + currCertImpl.getIssuerX500Principal(); | |
| 229 | + while (anchors.hasNext() && (seekIssuerCert || seekResponderCert)) { | |
| 230 | + | |
| 231 | + TrustAnchor anchor = anchors.next(); | |
| 232 | + X509Certificate anchorCert = anchor.getTrustedCert(); | |
| 233 | + X500Principal anchorSubjectName = | |
| 234 | + anchorCert.getSubjectX500Principal(); | |
| 235 | + | |
| 236 | + if (dump) { | |
| 237 | + System.out.println("Issuer DN is " + certIssuerName); | |
| 238 | + System.out.println("Subject DN is " + anchorSubjectName); | |
| 239 | + } | |
| 240 | + | |
| 241 | + // Check if anchor cert is the issuer cert | |
| 242 | + if (seekIssuerCert && | |
| 243 | + certIssuerName.equals(anchorSubjectName)) { | |
| 244 | + | |
| 245 | + issuerCert = anchorCert; | |
| 246 | + seekIssuerCert = false; // done | |
| 247 | + | |
| 248 | + // By default, the OCSP responder's cert is the same as | |
| 249 | + // the issuer of the cert being validated. | |
| 250 | + if (!seekResponderCert && responderCert == null) { | |
| 251 | + responderCert = anchorCert; | |
| 252 | + if (DEBUG != null) { | |
| 253 | + DEBUG.println("Responder's certificate is the" + | |
| 254 | + " same as the issuer of the certificate " + | |
| 255 | + "being validated"); | |
| 256 | + } | |
| 257 | + } | |
| 258 | + } | |
| 259 | + | |
| 260 | + // Check if anchor cert is the responder cert | |
| 261 | + if (seekResponderCert) { | |
| 262 | + // Satisfy the responder subject name property only, or | |
| 263 | + // satisfy the responder issuer name and serial number | |
| 264 | + // properties only | |
| 265 | + if ((responderSubjectName != null && | |
| 266 | + responderSubjectName.equals(anchorSubjectName)) || | |
| 267 | + (responderIssuerName != null && | |
| 268 | + responderSerialNumber != null && | |
| 269 | + responderIssuerName.equals( | |
| 270 | + anchorCert.getIssuerX500Principal()) && | |
| 271 | + responderSerialNumber.equals( | |
| 272 | + anchorCert.getSerialNumber()))) { | |
| 273 | + | |
| 274 | + responderCert = anchorCert; | |
| 275 | + seekResponderCert = false; // done | |
| 276 | + } | |
| 277 | + } | |
| 278 | + } | |
| 279 | + if (issuerCert == null) { | |
| 280 | + throw new CertPathValidatorException( | |
| 281 | + "No trusted certificate for " + currCertImpl.getIssuerDN()); | |
| 282 | + } | |
| 283 | + | |
| 284 | + // Check cert stores if responder cert has not yet been found | |
| 285 | + if (seekResponderCert) { | |
| 286 | + if (DEBUG != null) { | |
| 287 | + DEBUG.println("Searching cert stores for responder's " + | |
| 288 | + "certificate"); | |
| 289 | + } | |
| 290 | + X509CertSelector filter = null; | |
| 291 | + if (responderSubjectName != null) { | |
| 292 | + filter = new X509CertSelector(); | |
| 293 | + filter.setSubject(responderSubjectName); | |
| 294 | + } else if (responderIssuerName != null && | |
| 295 | + responderSerialNumber != null) { | |
| 296 | + filter = new X509CertSelector(); | |
| 297 | + filter.setIssuer(responderIssuerName); | |
| 298 | + filter.setSerialNumber(responderSerialNumber); | |
| 299 | + } | |
| 300 | + if (filter != null) { | |
| 301 | + List<CertStore> certStores = pkixParams.getCertStores(); | |
| 302 | + for (CertStore certStore : certStores) { | |
| 303 | + Iterator i = null; | |
| 304 | + try { | |
| 305 | + i = certStore.getCertificates(filter).iterator(); | |
| 306 | + } catch (CertStoreException cse) { | |
| 307 | + // ignore and try next certStore | |
| 308 | + if (DEBUG != null) { | |
| 309 | + DEBUG.println("CertStore exception:" + cse); | |
| 310 | + } | |
| 311 | + continue; | |
| 312 | + } | |
| 313 | + if (i.hasNext()) { | |
| 314 | + responderCert = (X509Certificate) i.next(); | |
| 315 | + seekResponderCert = false; // done | |
| 316 | + break; | |
| 317 | + } | |
| 318 | + } | |
| 319 | + } | |
| 320 | + } | |
| 321 | + } | |
| 322 | + | |
| 323 | + // Could not find the certificate identified in the OCSP properties | |
| 324 | + if (seekResponderCert) { | |
| 325 | + throw new CertPathValidatorException( | |
| 326 | + "Cannot find the responder's certificate " + | |
| 327 | + "(set using the OCSP security properties)."); | |
| 328 | + } | |
| 329 | + | |
| 330 | + // The algorithm constraints of the OCSP trusted responder certificate | |
| 331 | + // does not need to be checked in this code. The constraints will be | |
| 332 | + // checked when the responder's certificate is validated. | |
| 333 | + | |
| 334 | + CertId certId = null; | |
| 335 | + OCSPResponse response = null; | |
| 336 | + try { | |
| 337 | + certId = new CertId | |
| 338 | + (issuerCert, currCertImpl.getSerialNumberObject()); | |
| 339 | + response = OCSP.check(Collections.singletonList(certId), uri, | |
| 340 | + responderCert, pkixParams.getDate()); | |
| 341 | + } catch (Exception e) { | |
| 342 | + if (e instanceof CertPathValidatorException) { | |
| 343 | + throw (CertPathValidatorException) e; | |
| 344 | + } else { | |
| 345 | + // Wrap exceptions in CertPathValidatorException so that | |
| 346 | + // we can fallback to CRLs, if enabled. | |
| 347 | + throw new CertPathValidatorException(e); | |
| 348 | + } | |
| 349 | + } | |
| 350 | + | |
| 351 | + RevocationStatus rs = (RevocationStatus) response.getSingleResponse(certId); | |
| 352 | + RevocationStatus.CertStatus certStatus = rs.getCertStatus(); | |
| 353 | + if (certStatus == RevocationStatus.CertStatus.REVOKED) { | |
| 354 | + Throwable t = new CertificateRevokedException( | |
| 355 | + rs.getRevocationTime(), rs.getRevocationReason(), | |
| 356 | + responderCert.getSubjectX500Principal(), | |
| 357 | + rs.getSingleExtensions()); | |
| 358 | + throw new CertPathValidatorException(t.getMessage(), t, | |
| 359 | + null, -1, BasicReason.REVOKED); | |
| 360 | + } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) { | |
| 361 | + throw new CertPathValidatorException( | |
| 362 | + "Certificate's revocation status is unknown", null, cp, | |
| 363 | + remainingCerts, BasicReason.UNDETERMINED_REVOCATION_STATUS); | |
| 364 | + } | |
| 365 | + } | |
| 366 | + | |
| 367 | + /* | |
| 368 | + * The OCSP security property values are in the following order: | |
| 369 | + * 1. ocsp.responderURL | |
| 370 | + * 2. ocsp.responderCertSubjectName | |
| 371 | + * 3. ocsp.responderCertIssuerName | |
| 372 | + * 4. ocsp.responderCertSerialNumber | |
| 373 | + */ | |
| 374 | + private static URI getOCSPServerURI(X509CertImpl currCertImpl, | |
| 375 | + String responderURL) throws CertPathValidatorException { | |
| 376 | + | |
| 377 | + if (responderURL != null) { | |
| 378 | + try { | |
| 379 | + return new URI(responderURL); | |
| 380 | + } catch (URISyntaxException e) { | |
| 381 | + throw new CertPathValidatorException(e); | |
| 382 | + } | |
| 383 | + } | |
| 384 | + | |
| 385 | + // Examine the certificate's AuthorityInfoAccess extension | |
| 386 | + AuthorityInfoAccessExtension aia = | |
| 387 | + currCertImpl.getAuthorityInfoAccessExtension(); | |
| 388 | + if (aia == null) { | |
| 389 | + throw new CertPathValidatorException( | |
| 390 | + "Must specify the location of an OCSP Responder"); | |
| 391 | + } | |
| 392 | + | |
| 393 | + List<AccessDescription> descriptions = aia.getAccessDescriptions(); | |
| 394 | + for (AccessDescription description : descriptions) { | |
| 395 | + if (description.getAccessMethod().equals( | |
| 396 | + AccessDescription.Ad_OCSP_Id)) { | |
| 397 | + | |
| 398 | + GeneralName generalName = description.getAccessLocation(); | |
| 399 | + if (generalName.getType() == GeneralNameInterface.NAME_URI) { | |
| 400 | + URIName uri = (URIName) generalName.getName(); | |
| 401 | + return uri.getURI(); | |
| 402 | + } | |
| 403 | + } | |
| 404 | + } | |
| 405 | + | |
| 406 | + throw new CertPathValidatorException( | |
| 407 | + "Cannot find the location of the OCSP Responder"); | |
| 408 | + } | |
| 409 | + | |
| 410 | + /* | |
| 411 | + * Retrieves the values of the OCSP security properties. | |
| 412 | + */ | |
| 413 | + private static String[] getOCSPProperties() { | |
| 414 | + final String[] properties = new String[4]; | |
| 415 | + | |
| 416 | + AccessController.doPrivileged( | |
| 417 | + new PrivilegedAction<Void>() { | |
| 418 | + public Void run() { | |
| 419 | + properties[0] = Security.getProperty(OCSP_URL_PROP); | |
| 420 | + properties[1] = | |
| 421 | + Security.getProperty(OCSP_CERT_SUBJECT_PROP); | |
| 422 | + properties[2] = | |
| 423 | + Security.getProperty(OCSP_CERT_ISSUER_PROP); | |
| 424 | + properties[3] = | |
| 425 | + Security.getProperty(OCSP_CERT_NUMBER_PROP); | |
| 426 | + return null; | |
| 427 | + } | |
| 428 | + }); | |
| 429 | + | |
| 430 | + return properties; | |
| 431 | + } | |
| 432 | + | |
| 433 | + /* | |
| 434 | + * Removes any non-hexadecimal characters from a string. | |
| 435 | + */ | |
| 436 | + private static String stripOutSeparators(String value) { | |
| 437 | + char[] chars = value.toCharArray(); | |
| 438 | + StringBuilder hexNumber = new StringBuilder(); | |
| 439 | + for (int i = 0; i < chars.length; i++) { | |
| 440 | + if (HEX_DIGITS.indexOf(chars[i]) != -1) { | |
| 441 | + hexNumber.append(chars[i]); | |
| 442 | + } | |
| 443 | + } | |
| 444 | + return hexNumber.toString(); | |
| 445 | + } | |
| 446 | +} | |