OCSP.java revision 16661:00cd2ba50e10
1193326Sed/* 2193326Sed * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. 3193326Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4193326Sed * 5193326Sed * This code is free software; you can redistribute it and/or modify it 6193326Sed * under the terms of the GNU General Public License version 2 only, as 7193326Sed * published by the Free Software Foundation. Oracle designates this 8193326Sed * particular file as subject to the "Classpath" exception as provided 9193326Sed * by Oracle in the LICENSE file that accompanied this code. 10193326Sed * 11193326Sed * This code is distributed in the hope that it will be useful, but WITHOUT 12193326Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13193326Sed * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14193326Sed * version 2 for more details (a copy is included in the LICENSE file that 15249423Sdim * accompanied this code). 16193326Sed * 17218893Sdim * You should have received a copy of the GNU General Public License version 18198092Srdivacky * 2 along with this work; if not, write to the Free Software Foundation, 19193326Sed * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20193326Sed * 21226633Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22226633Sdim * or visit www.oracle.com if you need additional information or have any 23249423Sdim * questions. 24198092Srdivacky */ 25226633Sdimpackage sun.security.provider.certpath; 26249423Sdim 27226633Sdimimport java.io.InputStream; 28226633Sdimimport java.io.IOException; 29226633Sdimimport java.io.OutputStream; 30226633Sdimimport java.net.URI; 31226633Sdimimport java.net.URL; 32249423Sdimimport java.net.HttpURLConnection; 33249423Sdimimport java.security.cert.CertificateException; 34226633Sdimimport java.security.cert.CertPathValidatorException; 35226633Sdimimport java.security.cert.CertPathValidatorException.BasicReason; 36226633Sdimimport java.security.cert.CRLReason; 37226633Sdimimport java.security.cert.Extension; 38226633Sdimimport java.security.cert.TrustAnchor; 39226633Sdimimport java.security.cert.X509Certificate; 40226633Sdimimport java.util.Arrays; 41193326Sedimport java.util.Collections; 42198092Srdivackyimport java.util.Date; 43226633Sdimimport java.util.List; 44193326Sedimport java.util.Map; 45193326Sed 46226633Sdimimport sun.security.action.GetIntegerAction; 47226633Sdimimport sun.security.util.Debug; 48226633Sdimimport sun.security.validator.Validator; 49234353Sdimimport sun.security.x509.AccessDescription; 50226633Sdimimport sun.security.x509.AuthorityInfoAccessExtension; 51193326Sedimport sun.security.x509.GeneralName; 52226633Sdimimport sun.security.x509.GeneralNameInterface; 53193326Sedimport sun.security.x509.PKIXExtensions; 54193326Sedimport sun.security.x509.URIName; 55226633Sdimimport sun.security.x509.X509CertImpl; 56226633Sdim 57249423Sdim/** 58193326Sed * This is a class that checks the revocation status of a certificate(s) using 59198092Srdivacky * OCSP. It is not a PKIXCertPathChecker and therefore can be used outside of 60193326Sed * the CertPathValidator framework. It is useful when you want to 61193326Sed * just check the revocation status of a certificate, and you don't want to 62193326Sed * incur the overhead of validating all of the certificates in the 63226633Sdim * associated certificate chain. 64249423Sdim * 65193326Sed * @author Sean Mullan 66198092Srdivacky */ 67249423Sdimpublic final class OCSP { 68193326Sed 69193326Sed private static final Debug debug = Debug.getInstance("certpath"); 70193326Sed 71193326Sed private static final int DEFAULT_CONNECT_TIMEOUT = 15000; 72198092Srdivacky 73193326Sed /** 74193326Sed * Integer value indicating the timeout length, in seconds, to be 75193326Sed * used for the OCSP check. A timeout of zero is interpreted as 76193326Sed * an infinite timeout. 77193326Sed */ 78193326Sed private static final int CONNECT_TIMEOUT = initializeTimeout(); 79193326Sed 80193326Sed /** 81193326Sed * Initialize the timeout length by getting the OCSP timeout 82193326Sed * system property. If the property has not been set, or if its 83193326Sed * value is negative, set the timeout length to the default. 84193326Sed */ 85193326Sed private static int initializeTimeout() { 86193326Sed Integer tmp = java.security.AccessController.doPrivileged( 87193326Sed new GetIntegerAction("com.sun.security.ocsp.timeout")); 88198092Srdivacky if (tmp == null || tmp < 0) { 89234353Sdim return DEFAULT_CONNECT_TIMEOUT; 90249423Sdim } 91234353Sdim // Convert to milliseconds, as the system property will be 92234353Sdim // specified in seconds 93234353Sdim return tmp * 1000; 94234353Sdim } 95234353Sdim 96234353Sdim private OCSP() {} 97234353Sdim 98234353Sdim 99234353Sdim /** 100234353Sdim * Obtains the revocation status of a certificate using OCSP. 101234353Sdim * 102193326Sed * @param cert the certificate to be checked 103193326Sed * @param issuerCert the issuer certificate 104193326Sed * @param responderURI the URI of the OCSP responder 105193326Sed * @param responderCert the OCSP responder's certificate 106193326Sed * @param date the time the validity of the OCSP responder's certificate 107193326Sed * should be checked against. If null, the current time is used. 108193326Sed * @return the RevocationStatus 109193326Sed * @throws IOException if there is an exception connecting to or 110193326Sed * communicating with the OCSP responder 111193326Sed * @throws CertPathValidatorException if an exception occurs while 112193326Sed * encoding the OCSP Request or validating the OCSP Response 113193326Sed */ 114206084Srdivacky 115193326Sed // Called by com.sun.deploy.security.TrustDecider 116193326Sed public static RevocationStatus check(X509Certificate cert, 117193326Sed X509Certificate issuerCert, 118193326Sed URI responderURI, 119193326Sed X509Certificate responderCert, 120193326Sed Date date) 121193326Sed throws IOException, CertPathValidatorException 122193326Sed { 123193326Sed return check(cert, issuerCert, responderURI, responderCert, date, 124198398Srdivacky Collections.<Extension>emptyList(), Validator.VAR_GENERIC); 125193326Sed } 126193326Sed 127193326Sed 128193326Sed public static RevocationStatus check(X509Certificate cert, 129193326Sed X509Certificate issuerCert, URI responderURI, 130193326Sed X509Certificate responderCert, Date date, List<Extension> extensions, 131193326Sed String variant) 132193326Sed throws IOException, CertPathValidatorException 133193326Sed { 134193326Sed return check(cert, responderURI, null, issuerCert, responderCert, date, 135193326Sed extensions, variant); 136193326Sed } 137193326Sed 138193326Sed public static RevocationStatus check(X509Certificate cert, 139193326Sed URI responderURI, TrustAnchor anchor, X509Certificate issuerCert, 140193326Sed X509Certificate responderCert, Date date, 141193326Sed List<Extension> extensions, String variant) 142193326Sed throws IOException, CertPathValidatorException 143193326Sed { 144193326Sed CertId certId; 145193326Sed try { 146193326Sed X509CertImpl certImpl = X509CertImpl.toImpl(cert); 147193326Sed certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); 148193326Sed } catch (CertificateException | IOException e) { 149193326Sed throw new CertPathValidatorException 150193326Sed ("Exception while encoding OCSPRequest", e); 151193326Sed } 152193326Sed OCSPResponse ocspResponse = check(Collections.singletonList(certId), 153207619Srdivacky responderURI, new OCSPResponse.IssuerInfo(anchor, issuerCert), 154207619Srdivacky responderCert, date, extensions, variant); 155193326Sed return (RevocationStatus) ocspResponse.getSingleResponse(certId); 156193326Sed } 157193326Sed 158193326Sed /** 159249423Sdim * Checks the revocation status of a list of certificates using OCSP. 160249423Sdim * 161249423Sdim * @param certIds the CertIds to be checked 162249423Sdim * @param responderURI the URI of the OCSP responder 163193326Sed * @param issuerInfo the issuer's certificate and/or subject and public key 164198092Srdivacky * @param responderCert the OCSP responder's certificate 165193326Sed * @param date the time the validity of the OCSP responder's certificate 166193326Sed * should be checked against. If null, the current time is used. 167193326Sed * @param extensions zero or more OCSP extensions to be included in the 168198092Srdivacky * request. If no extensions are requested, an empty {@code List} must 169193326Sed * be used. A {@code null} value is not allowed. 170193326Sed * @return the OCSPResponse 171198092Srdivacky * @throws IOException if there is an exception connecting to or 172193326Sed * communicating with the OCSP responder 173193326Sed * @throws CertPathValidatorException if an exception occurs while 174198092Srdivacky * encoding the OCSP Request or validating the OCSP Response 175193326Sed */ 176193326Sed static OCSPResponse check(List<CertId> certIds, URI responderURI, 177193326Sed OCSPResponse.IssuerInfo issuerInfo, 178193326Sed X509Certificate responderCert, Date date, 179193326Sed List<Extension> extensions, String variant) 180193326Sed throws IOException, CertPathValidatorException 181198092Srdivacky { 182193326Sed byte[] nonce = null; 183198092Srdivacky for (Extension ext : extensions) { 184193326Sed if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { 185193326Sed nonce = ext.getValue(); 186193326Sed } 187193326Sed } 188193326Sed 189193326Sed OCSPResponse ocspResponse = null; 190193326Sed try { 191193326Sed byte[] response = getOCSPBytes(certIds, responderURI, extensions); 192193326Sed ocspResponse = new OCSPResponse(response); 193198092Srdivacky 194193326Sed // verify the response 195218893Sdim ocspResponse.verify(certIds, issuerInfo, responderCert, date, 196218893Sdim nonce, variant); 197218893Sdim } catch (IOException ioe) { 198218893Sdim throw new CertPathValidatorException( 199218893Sdim "Unable to determine revocation status due to network error", 200218893Sdim ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); 201234353Sdim } 202234353Sdim 203234353Sdim return ocspResponse; 204234353Sdim } 205234353Sdim 206234353Sdim 207234353Sdim /** 208234353Sdim * Send an OCSP request, then read and return the OCSP response bytes. 209234353Sdim * 210249423Sdim * @param certIds the CertIds to be checked 211234353Sdim * @param responderURI the URI of the OCSP responder 212234353Sdim * @param extensions zero or more OCSP extensions to be included in the 213234353Sdim * request. If no extensions are requested, an empty {@code List} must 214234353Sdim * be used. A {@code null} value is not allowed. 215234353Sdim * 216234353Sdim * @return the OCSP response bytes 217234353Sdim * 218234353Sdim * @throws IOException if there is an exception connecting to or 219234353Sdim * communicating with the OCSP responder 220234353Sdim */ 221234353Sdim public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI, 222234353Sdim List<Extension> extensions) throws IOException { 223198092Srdivacky OCSPRequest request = new OCSPRequest(certIds, extensions); 224193326Sed byte[] bytes = request.encodeBytes(); 225193326Sed 226193326Sed InputStream in = null; 227193326Sed OutputStream out = null; 228226633Sdim byte[] response = null; 229226633Sdim 230226633Sdim try { 231226633Sdim URL url = responderURI.toURL(); 232193326Sed if (debug != null) { 233198092Srdivacky debug.println("connecting to OCSP service at: " + url); 234193326Sed } 235193326Sed HttpURLConnection con = (HttpURLConnection)url.openConnection(); 236193326Sed con.setConnectTimeout(CONNECT_TIMEOUT); 237198092Srdivacky con.setReadTimeout(CONNECT_TIMEOUT); 238193326Sed con.setDoOutput(true); 239226633Sdim con.setDoInput(true); 240226633Sdim con.setRequestMethod("POST"); 241234353Sdim con.setRequestProperty 242193326Sed ("Content-type", "application/ocsp-request"); 243249423Sdim con.setRequestProperty 244249423Sdim ("Content-length", String.valueOf(bytes.length)); 245193326Sed out = con.getOutputStream(); 246207619Srdivacky out.write(bytes); 247249423Sdim out.flush(); 248249423Sdim // Check the response 249193326Sed if (debug != null && 250193326Sed con.getResponseCode() != HttpURLConnection.HTTP_OK) { 251193326Sed debug.println("Received HTTP error: " + con.getResponseCode() 252193326Sed + " - " + con.getResponseMessage()); 253193326Sed } 254193326Sed in = con.getInputStream(); 255193326Sed int contentLength = con.getContentLength(); 256193326Sed if (contentLength == -1) { 257193326Sed contentLength = Integer.MAX_VALUE; 258193326Sed } 259193326Sed response = new byte[contentLength > 2048 ? 2048 : contentLength]; 260193326Sed int total = 0; 261193326Sed while (total < contentLength) { 262193326Sed int count = in.read(response, total, response.length - total); 263193326Sed if (count < 0) 264193326Sed break; 265193326Sed 266194613Sed total += count; 267234353Sdim if (total >= response.length && total < contentLength) { 268193326Sed response = Arrays.copyOf(response, total * 2); 269193326Sed } 270193326Sed } 271234353Sdim response = Arrays.copyOf(response, total); 272193326Sed } finally { 273193326Sed if (in != null) { 274 try { 275 in.close(); 276 } catch (IOException ioe) { 277 throw ioe; 278 } 279 } 280 if (out != null) { 281 try { 282 out.close(); 283 } catch (IOException ioe) { 284 throw ioe; 285 } 286 } 287 } 288 return response; 289 } 290 291 /** 292 * Returns the URI of the OCSP Responder as specified in the 293 * certificate's Authority Information Access extension, or null if 294 * not specified. 295 * 296 * @param cert the certificate 297 * @return the URI of the OCSP Responder, or null if not specified 298 */ 299 // Called by com.sun.deploy.security.TrustDecider 300 public static URI getResponderURI(X509Certificate cert) { 301 try { 302 return getResponderURI(X509CertImpl.toImpl(cert)); 303 } catch (CertificateException ce) { 304 // treat this case as if the cert had no extension 305 return null; 306 } 307 } 308 309 static URI getResponderURI(X509CertImpl certImpl) { 310 311 // Examine the certificate's AuthorityInfoAccess extension 312 AuthorityInfoAccessExtension aia = 313 certImpl.getAuthorityInfoAccessExtension(); 314 if (aia == null) { 315 return null; 316 } 317 318 List<AccessDescription> descriptions = aia.getAccessDescriptions(); 319 for (AccessDescription description : descriptions) { 320 if (description.getAccessMethod().equals( 321 AccessDescription.Ad_OCSP_Id)) { 322 323 GeneralName generalName = description.getAccessLocation(); 324 if (generalName.getType() == GeneralNameInterface.NAME_URI) { 325 URIName uri = (URIName) generalName.getName(); 326 return uri.getURI(); 327 } 328 } 329 } 330 return null; 331 } 332 333 /** 334 * The Revocation Status of a certificate. 335 */ 336 public static interface RevocationStatus { 337 public enum CertStatus { GOOD, REVOKED, UNKNOWN }; 338 339 /** 340 * Returns the revocation status. 341 */ 342 CertStatus getCertStatus(); 343 /** 344 * Returns the time when the certificate was revoked, or null 345 * if it has not been revoked. 346 */ 347 Date getRevocationTime(); 348 /** 349 * Returns the reason the certificate was revoked, or null if it 350 * has not been revoked. 351 */ 352 CRLReason getRevocationReason(); 353 354 /** 355 * Returns a Map of additional extensions. 356 */ 357 Map<String, Extension> getSingleExtensions(); 358 } 359} 360