123 SIG_REQUIRED, // Must sign the request |
127 SIG_REQUIRED, // Must sign the request |
124 UNAUTHORIZED // Request unauthorized |
128 UNAUTHORIZED // Request unauthorized |
125 }; |
129 }; |
126 private static ResponseStatus[] rsvalues = ResponseStatus.values(); |
130 private static ResponseStatus[] rsvalues = ResponseStatus.values(); |
127 |
131 |
128 private static final Debug DEBUG = Debug.getInstance("certpath"); |
132 private static final Debug debug = Debug.getInstance("certpath"); |
129 private static final boolean dump = false; |
133 private static final boolean dump = false; |
130 private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID = |
134 private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID = |
131 ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); |
135 ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); |
132 private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID = |
|
133 ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 2}); |
|
134 |
|
135 private static final int CERT_STATUS_GOOD = 0; |
136 private static final int CERT_STATUS_GOOD = 0; |
136 private static final int CERT_STATUS_REVOKED = 1; |
137 private static final int CERT_STATUS_REVOKED = 1; |
137 private static final int CERT_STATUS_UNKNOWN = 2; |
138 private static final int CERT_STATUS_UNKNOWN = 2; |
138 |
139 |
139 // ResponderID CHOICE tags |
140 // ResponderID CHOICE tags |
141 private static final int KEY_TAG = 2; |
142 private static final int KEY_TAG = 2; |
142 |
143 |
143 // Object identifier for the OCSPSigning key purpose |
144 // Object identifier for the OCSPSigning key purpose |
144 private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; |
145 private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; |
145 |
146 |
146 private final ResponseStatus responseStatus; |
|
147 private final Map<CertId, SingleResponse> singleResponseMap; |
|
148 |
|
149 // Maximum clock skew in milliseconds (15 minutes) allowed when checking |
147 // Maximum clock skew in milliseconds (15 minutes) allowed when checking |
150 // validity of OCSP responses |
148 // validity of OCSP responses |
151 private static final long MAX_CLOCK_SKEW = 900000; |
149 private static final long MAX_CLOCK_SKEW = 900000; |
152 |
150 |
153 // an array of all of the CRLReasons (used in SingleResponse) |
151 // an array of all of the CRLReasons (used in SingleResponse) |
154 private static CRLReason[] values = CRLReason.values(); |
152 private static CRLReason[] values = CRLReason.values(); |
155 |
153 |
|
154 private final ResponseStatus responseStatus; |
|
155 private final Map<CertId, SingleResponse> singleResponseMap; |
|
156 private final List<X509CertImpl> certs; |
|
157 private final AlgorithmId sigAlgId; |
|
158 private final byte[] signature; |
|
159 private final byte[] tbsResponseData; |
|
160 private final X500Principal responderName; |
|
161 private final byte[] responderKey; |
|
162 private final byte[] responseNonce; |
|
163 |
156 /* |
164 /* |
157 * Create an OCSP response from its ASN.1 DER encoding. |
165 * Create an OCSP response from its ASN.1 DER encoding. |
158 */ |
166 */ |
159 OCSPResponse(byte[] bytes, Date dateCheckedAgainst, |
167 OCSPResponse(byte[] bytes) throws IOException { |
160 X509Certificate responderCert) |
|
161 throws IOException, CertPathValidatorException { |
|
162 |
|
163 // OCSPResponse |
|
164 if (dump) { |
168 if (dump) { |
165 HexDumpEncoder hexEnc = new HexDumpEncoder(); |
169 HexDumpEncoder hexEnc = new HexDumpEncoder(); |
166 System.out.println("OCSPResponse bytes are..."); |
170 System.out.println("OCSPResponse bytes are..."); |
167 System.out.println(hexEnc.encode(bytes)); |
171 System.out.println(hexEnc.encode(bytes)); |
168 } |
172 } |
255 } |
266 } |
256 |
267 |
257 // responderID |
268 // responderID |
258 short tag = (byte)(seq.tag & 0x1f); |
269 short tag = (byte)(seq.tag & 0x1f); |
259 if (tag == NAME_TAG) { |
270 if (tag == NAME_TAG) { |
260 if (DEBUG != null) { |
271 responderName = |
261 X500Name responderName = new X500Name(seq.getData()); |
272 new X500Principal(new ByteArrayInputStream(seq.toByteArray())); |
262 DEBUG.println("OCSP Responder name: " + responderName); |
273 if (debug != null) { |
263 } |
274 debug.println("OCSP Responder name: " + responderName); |
|
275 } |
|
276 responderKey = null; |
264 } else if (tag == KEY_TAG) { |
277 } else if (tag == KEY_TAG) { |
265 // Ignore, for now |
278 responderKey = seq.getOctetString(); |
|
279 responderName = null; |
266 } else { |
280 } else { |
267 throw new IOException("Bad encoding in responderID element of " + |
281 throw new IOException("Bad encoding in responderID element of " + |
268 "OCSP response: expected ASN.1 context specific tag 0 or 1"); |
282 "OCSP response: expected ASN.1 context specific tag 0 or 1"); |
269 } |
283 } |
270 |
284 |
271 // producedAt |
285 // producedAt |
272 seq = seqDerIn.getDerValue(); |
286 seq = seqDerIn.getDerValue(); |
273 if (DEBUG != null) { |
287 if (debug != null) { |
274 Date producedAtDate = seq.getGeneralizedTime(); |
288 Date producedAtDate = seq.getGeneralizedTime(); |
275 DEBUG.println("OCSP response produced at: " + producedAtDate); |
289 debug.println("OCSP response produced at: " + producedAtDate); |
276 } |
290 } |
277 |
291 |
278 // responses |
292 // responses |
279 DerValue[] singleResponseDer = seqDerIn.getSequence(1); |
293 DerValue[] singleResponseDer = seqDerIn.getSequence(1); |
280 singleResponseMap |
294 singleResponseMap = new HashMap<>(singleResponseDer.length); |
281 = new HashMap<CertId, SingleResponse>(singleResponseDer.length); |
295 if (debug != null) { |
282 if (DEBUG != null) { |
296 debug.println("OCSP number of SingleResponses: " |
283 DEBUG.println("OCSP number of SingleResponses: " |
297 + singleResponseDer.length); |
284 + singleResponseDer.length); |
|
285 } |
298 } |
286 for (int i = 0; i < singleResponseDer.length; i++) { |
299 for (int i = 0; i < singleResponseDer.length; i++) { |
287 SingleResponse singleResponse |
300 SingleResponse singleResponse = |
288 = new SingleResponse(singleResponseDer[i]); |
301 new SingleResponse(singleResponseDer[i]); |
289 singleResponseMap.put(singleResponse.getCertId(), singleResponse); |
302 singleResponseMap.put(singleResponse.getCertId(), singleResponse); |
290 } |
303 } |
291 |
304 |
292 // responseExtensions |
305 // responseExtensions |
|
306 byte[] nonce = null; |
293 if (seqDerIn.available() > 0) { |
307 if (seqDerIn.available() > 0) { |
294 seq = seqDerIn.getDerValue(); |
308 seq = seqDerIn.getDerValue(); |
295 if (seq.isContextSpecific((byte)1)) { |
309 if (seq.isContextSpecific((byte)1)) { |
296 DerValue[] responseExtDer = seq.data.getSequence(3); |
310 DerValue[] responseExtDer = seq.data.getSequence(3); |
297 for (int i = 0; i < responseExtDer.length; i++) { |
311 for (int i = 0; i < responseExtDer.length; i++) { |
298 Extension responseExtension |
312 Extension ext = new Extension(responseExtDer[i]); |
299 = new Extension(responseExtDer[i]); |
313 if (debug != null) { |
300 if (DEBUG != null) { |
314 debug.println("OCSP extension: " + ext); |
301 DEBUG.println("OCSP extension: " + responseExtension); |
|
302 } |
315 } |
303 if (responseExtension.getExtensionId().equals((Object) |
316 // Only the NONCE extension is recognized |
304 OCSP_NONCE_EXTENSION_OID)) { |
317 if (ext.getExtensionId().equals((Object) |
305 /* |
318 OCSP.NONCE_EXTENSION_OID)) |
306 ocspNonce = |
319 { |
307 responseExtension[i].getExtensionValue(); |
320 nonce = ext.getExtensionValue(); |
308 */ |
321 } else if (ext.isCritical()) { |
309 } else if (responseExtension.isCritical()) { |
|
310 throw new IOException( |
322 throw new IOException( |
311 "Unsupported OCSP critical extension: " + |
323 "Unsupported OCSP critical extension: " + |
312 responseExtension.getExtensionId()); |
324 ext.getExtensionId()); |
313 } |
325 } |
314 } |
326 } |
315 } |
327 } |
316 } |
328 } |
|
329 responseNonce = nonce; |
317 |
330 |
318 // signatureAlgorithmId |
331 // signatureAlgorithmId |
319 AlgorithmId sigAlgId = AlgorithmId.parse(seqTmp[1]); |
332 sigAlgId = AlgorithmId.parse(seqTmp[1]); |
320 |
333 |
321 // signature |
334 // signature |
322 byte[] signature = seqTmp[2].getBitString(); |
335 signature = seqTmp[2].getBitString(); |
323 X509CertImpl[] x509Certs = null; |
|
324 |
336 |
325 // if seq[3] is available , then it is a sequence of certificates |
337 // if seq[3] is available , then it is a sequence of certificates |
326 if (seqTmp.length > 3) { |
338 if (seqTmp.length > 3) { |
327 // certs are available |
339 // certs are available |
328 DerValue seqCert = seqTmp[3]; |
340 DerValue seqCert = seqTmp[3]; |
329 if (!seqCert.isContextSpecific((byte)0)) { |
341 if (!seqCert.isContextSpecific((byte)0)) { |
330 throw new IOException("Bad encoding in certs element of " + |
342 throw new IOException("Bad encoding in certs element of " + |
331 "OCSP response: expected ASN.1 context specific tag 0."); |
343 "OCSP response: expected ASN.1 context specific tag 0."); |
332 } |
344 } |
333 DerValue[] certs = seqCert.getData().getSequence(3); |
345 DerValue[] derCerts = seqCert.getData().getSequence(3); |
334 x509Certs = new X509CertImpl[certs.length]; |
346 certs = new ArrayList<X509CertImpl>(derCerts.length); |
335 try { |
347 try { |
336 for (int i = 0; i < certs.length; i++) { |
348 for (int i = 0; i < derCerts.length; i++) { |
337 x509Certs[i] = new X509CertImpl(certs[i].toByteArray()); |
349 certs.add(new X509CertImpl(derCerts[i].toByteArray())); |
338 } |
350 } |
339 } catch (CertificateException ce) { |
351 } catch (CertificateException ce) { |
340 throw new IOException("Bad encoding in X509 Certificate", ce); |
352 throw new IOException("Bad encoding in X509 Certificate", ce); |
341 } |
353 } |
342 } |
354 } else { |
|
355 certs = Collections.<X509CertImpl>emptyList(); |
|
356 } |
|
357 } |
|
358 |
|
359 void verify(List<CertId> certIds, X509Certificate responderCert, |
|
360 Date date, byte[] nonce) |
|
361 throws CertPathValidatorException |
|
362 { |
|
363 if (responseStatus != ResponseStatus.SUCCESSFUL) { |
|
364 throw new CertPathValidatorException |
|
365 ("OCSP response error: " + responseStatus); |
|
366 } |
|
367 |
|
368 // Check that the response includes a response for all of the |
|
369 // certs that were supplied in the request |
|
370 for (CertId certId : certIds) { |
|
371 SingleResponse sr = getSingleResponse(certId); |
|
372 if (sr == null) { |
|
373 if (debug != null) { |
|
374 debug.println("No response found for CertId: " + certId); |
|
375 } |
|
376 throw new CertPathValidatorException( |
|
377 "OCSP response does not include a response for a " + |
|
378 "certificate supplied in the OCSP request"); |
|
379 } |
|
380 if (debug != null) { |
|
381 debug.println("Status of certificate (with serial number " + |
|
382 certId.getSerialNumber() + ") is: " + sr.getCertStatus()); |
|
383 } |
|
384 } |
|
385 |
343 |
386 |
344 // Check whether the cert returned by the responder is trusted |
387 // Check whether the cert returned by the responder is trusted |
345 if (x509Certs != null && x509Certs[0] != null) { |
388 if (!certs.isEmpty()) { |
346 X509CertImpl cert = x509Certs[0]; |
389 X509CertImpl cert = certs.get(0); |
347 |
390 // First check if the cert matches the expected responder cert |
348 // First check if the cert matches the responder cert which |
|
349 // was set locally. |
|
350 if (cert.equals(responderCert)) { |
391 if (cert.equals(responderCert)) { |
351 // cert is trusted, now verify the signed response |
392 // cert is trusted, now verify the signed response |
352 |
393 |
353 // Next check if the cert was issued by the responder cert |
394 // Next check if the cert was issued by the responder cert |
354 // which was set locally. |
395 // which was set locally. |
355 } else if (cert.getIssuerX500Principal().equals( |
396 } else if (cert.getIssuerX500Principal().equals( |
356 responderCert.getSubjectX500Principal())) { |
397 responderCert.getSubjectX500Principal())) { |
357 |
398 |
358 // Check for the OCSPSigning key purpose |
399 // Check for the OCSPSigning key purpose |
359 try { |
400 try { |
360 List<String> keyPurposes = cert.getExtendedKeyUsage(); |
401 List<String> keyPurposes = cert.getExtendedKeyUsage(); |
361 if (keyPurposes == null || |
402 if (keyPurposes == null || |
431 if (responderCert != null) { |
472 if (responderCert != null) { |
432 // Check algorithm constraints specified in security property |
473 // Check algorithm constraints specified in security property |
433 // "jdk.certpath.disabledAlgorithms". |
474 // "jdk.certpath.disabledAlgorithms". |
434 AlgorithmChecker.check(responderCert.getPublicKey(), sigAlgId); |
475 AlgorithmChecker.check(responderCert.getPublicKey(), sigAlgId); |
435 |
476 |
436 if (!verifyResponse(responseDataDer, responderCert, |
477 if (!verifySignature(responderCert)) { |
437 sigAlgId, signature)) { |
|
438 throw new CertPathValidatorException( |
478 throw new CertPathValidatorException( |
439 "Error verifying OCSP Responder's signature"); |
479 "Error verifying OCSP Response's signature"); |
440 } |
480 } |
441 } else { |
481 } else { |
442 // Need responder's cert in order to verify the signature |
482 // Need responder's cert in order to verify the signature |
443 throw new CertPathValidatorException( |
483 throw new CertPathValidatorException( |
444 "Unable to verify OCSP Responder's signature"); |
484 "Unable to verify OCSP Response's signature"); |
|
485 } |
|
486 |
|
487 // Check freshness of OCSPResponse |
|
488 if (nonce != null) { |
|
489 if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) { |
|
490 throw new CertPathValidatorException("Nonces don't match"); |
|
491 } |
|
492 } |
|
493 |
|
494 long now = (date == null) ? System.currentTimeMillis() : date.getTime(); |
|
495 Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW); |
|
496 Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW); |
|
497 for (SingleResponse sr : singleResponseMap.values()) { |
|
498 if (debug != null) { |
|
499 String until = ""; |
|
500 if (sr.nextUpdate != null) { |
|
501 until = " until " + sr.nextUpdate; |
|
502 } |
|
503 debug.println("Response's validity interval is from " + |
|
504 sr.thisUpdate + until); |
|
505 } |
|
506 |
|
507 // Check that the test date is within the validity interval |
|
508 if ((sr.thisUpdate != null && nowPlusSkew.before(sr.thisUpdate)) || |
|
509 (sr.nextUpdate != null && nowMinusSkew.after(sr.nextUpdate))) |
|
510 { |
|
511 throw new CertPathValidatorException( |
|
512 "Response is unreliable: its validity " + |
|
513 "interval is out-of-date"); |
|
514 } |
445 } |
515 } |
446 } |
516 } |
447 |
517 |
448 /** |
518 /** |
449 * Returns the OCSP ResponseStatus. |
519 * Returns the OCSP ResponseStatus. |
454 |
524 |
455 /* |
525 /* |
456 * Verify the signature of the OCSP response. |
526 * Verify the signature of the OCSP response. |
457 * The responder's cert is implicitly trusted. |
527 * The responder's cert is implicitly trusted. |
458 */ |
528 */ |
459 private boolean verifyResponse(byte[] responseData, X509Certificate cert, |
529 private boolean verifySignature(X509Certificate cert) |
460 AlgorithmId sigAlgId, byte[] signBytes) |
|
461 throws CertPathValidatorException { |
530 throws CertPathValidatorException { |
462 |
531 |
463 try { |
532 try { |
464 Signature respSignature = Signature.getInstance(sigAlgId.getName()); |
533 Signature respSignature = Signature.getInstance(sigAlgId.getName()); |
465 respSignature.initVerify(cert); |
534 respSignature.initVerify(cert); |
466 respSignature.update(responseData); |
535 respSignature.update(tbsResponseData); |
467 |
536 |
468 if (respSignature.verify(signBytes)) { |
537 if (respSignature.verify(signature)) { |
469 if (DEBUG != null) { |
538 if (debug != null) { |
470 DEBUG.println("Verified signature of OCSP Responder"); |
539 debug.println("Verified signature of OCSP Response"); |
471 } |
540 } |
472 return true; |
541 return true; |
473 |
542 |
474 } else { |
543 } else { |
475 if (DEBUG != null) { |
544 if (debug != null) { |
476 DEBUG.println( |
545 debug.println( |
477 "Error verifying signature of OCSP Responder"); |
546 "Error verifying signature of OCSP Response"); |
478 } |
547 } |
479 return false; |
548 return false; |
480 } |
549 } |
481 } catch (InvalidKeyException ike) { |
550 } catch (InvalidKeyException | NoSuchAlgorithmException | |
482 throw new CertPathValidatorException(ike); |
551 SignatureException e) |
483 } catch (NoSuchAlgorithmException nsae) { |
552 { |
484 throw new CertPathValidatorException(nsae); |
553 throw new CertPathValidatorException(e); |
485 } catch (SignatureException se) { |
|
486 throw new CertPathValidatorException(se); |
|
487 } |
554 } |
488 } |
555 } |
489 |
556 |
490 /** |
557 /** |
491 * Returns the SingleResponse of the specified CertId, or null if |
558 * Returns the SingleResponse of the specified CertId, or null if |