157 private static CRLReason[] values = CRLReason.values(); |
154 private static CRLReason[] values = CRLReason.values(); |
158 |
155 |
159 /* |
156 /* |
160 * Create an OCSP response from its ASN.1 DER encoding. |
157 * Create an OCSP response from its ASN.1 DER encoding. |
161 */ |
158 */ |
162 // used by OCSPChecker |
159 OCSPResponse(byte[] bytes, Date dateCheckedAgainst, |
163 OCSPResponse(byte[] bytes, PKIXParameters params, |
|
164 X509Certificate responderCert) |
160 X509Certificate responderCert) |
165 throws IOException, CertPathValidatorException { |
161 throws IOException, CertPathValidatorException { |
166 |
162 |
167 try { |
163 // OCSPResponse |
168 int responseStatus; |
164 if (dump) { |
169 ObjectIdentifier responseType; |
165 HexDumpEncoder hexEnc = new HexDumpEncoder(); |
170 int version; |
166 System.out.println("OCSPResponse bytes are..."); |
171 CertificateIssuerName responderName = null; |
167 System.out.println(hexEnc.encode(bytes)); |
172 Date producedAtDate; |
168 } |
173 AlgorithmId sigAlgId; |
169 DerValue der = new DerValue(bytes); |
174 byte[] ocspNonce; |
170 if (der.tag != DerValue.tag_Sequence) { |
175 |
171 throw new IOException("Bad encoding in OCSP response: " + |
176 // OCSPResponse |
172 "expected ASN.1 SEQUENCE tag."); |
177 if (dump) { |
173 } |
178 HexDumpEncoder hexEnc = new HexDumpEncoder(); |
174 DerInputStream derIn = der.getData(); |
179 System.out.println("OCSPResponse bytes are..."); |
175 |
180 System.out.println(hexEnc.encode(bytes)); |
176 // responseStatus |
181 } |
177 int status = derIn.getEnumerated(); |
182 DerValue der = new DerValue(bytes); |
178 if (status >= 0 && status < rsvalues.length) { |
183 if (der.tag != DerValue.tag_Sequence) { |
179 responseStatus = rsvalues[status]; |
184 throw new IOException("Bad encoding in OCSP response: " + |
180 } else { |
185 "expected ASN.1 SEQUENCE tag."); |
181 // unspecified responseStatus |
186 } |
182 throw new IOException("Unknown OCSPResponse status: " + status); |
187 DerInputStream derIn = der.getData(); |
183 } |
188 |
184 if (DEBUG != null) { |
189 // responseStatus |
185 DEBUG.println("OCSP response status: " + responseStatus); |
190 responseStatus = derIn.getEnumerated(); |
186 } |
|
187 if (responseStatus != ResponseStatus.SUCCESSFUL) { |
|
188 // no need to continue, responseBytes are not set. |
|
189 singleResponseMap = Collections.emptyMap(); |
|
190 return; |
|
191 } |
|
192 |
|
193 // responseBytes |
|
194 der = derIn.getDerValue(); |
|
195 if (!der.isContextSpecific((byte)0)) { |
|
196 throw new IOException("Bad encoding in responseBytes element " + |
|
197 "of OCSP response: expected ASN.1 context specific tag 0."); |
|
198 } |
|
199 DerValue tmp = der.data.getDerValue(); |
|
200 if (tmp.tag != DerValue.tag_Sequence) { |
|
201 throw new IOException("Bad encoding in responseBytes element " + |
|
202 "of OCSP response: expected ASN.1 SEQUENCE tag."); |
|
203 } |
|
204 |
|
205 // responseType |
|
206 derIn = tmp.data; |
|
207 ObjectIdentifier responseType = derIn.getOID(); |
|
208 if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) { |
191 if (DEBUG != null) { |
209 if (DEBUG != null) { |
192 DEBUG.println("OCSP response: " + |
210 DEBUG.println("OCSP response type: basic"); |
193 responseToText(responseStatus)); |
211 } |
194 } |
212 } else { |
195 if (responseStatus != OCSP_RESPONSE_OK) { |
213 if (DEBUG != null) { |
196 throw new CertPathValidatorException( |
214 DEBUG.println("OCSP response type: " + responseType); |
197 "OCSP Response Failure: " + |
215 } |
198 responseToText(responseStatus)); |
216 throw new IOException("Unsupported OCSP response type: " + |
199 } |
217 responseType); |
200 |
218 } |
201 // responseBytes |
219 |
202 der = derIn.getDerValue(); |
220 // BasicOCSPResponse |
203 if (! der.isContextSpecific((byte)0)) { |
221 DerInputStream basicOCSPResponse = |
204 throw new IOException("Bad encoding in responseBytes element " + |
222 new DerInputStream(derIn.getOctetString()); |
205 "of OCSP response: expected ASN.1 context specific tag 0."); |
223 |
206 }; |
224 DerValue[] seqTmp = basicOCSPResponse.getSequence(2); |
207 DerValue tmp = der.data.getDerValue(); |
225 if (seqTmp.length < 3) { |
208 if (tmp.tag != DerValue.tag_Sequence) { |
226 throw new IOException("Unexpected BasicOCSPResponse value"); |
209 throw new IOException("Bad encoding in responseBytes element " + |
227 } |
210 "of OCSP response: expected ASN.1 SEQUENCE tag."); |
228 |
211 } |
229 DerValue responseData = seqTmp[0]; |
212 |
230 |
213 // responseType |
231 // Need the DER encoded ResponseData to verify the signature later |
214 derIn = tmp.data; |
232 byte[] responseDataDer = seqTmp[0].toByteArray(); |
215 responseType = derIn.getOID(); |
233 |
216 if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) { |
234 // tbsResponseData |
217 if (DEBUG != null) { |
235 if (responseData.tag != DerValue.tag_Sequence) { |
218 DEBUG.println("OCSP response type: basic"); |
236 throw new IOException("Bad encoding in tbsResponseData " + |
219 } |
237 "element of OCSP response: expected ASN.1 SEQUENCE tag."); |
220 } else { |
238 } |
221 if (DEBUG != null) { |
239 DerInputStream seqDerIn = responseData.data; |
222 DEBUG.println("OCSP response type: " + responseType); |
240 DerValue seq = seqDerIn.getDerValue(); |
223 } |
241 |
224 throw new IOException("Unsupported OCSP response type: " + |
242 // version |
225 responseType); |
243 if (seq.isContextSpecific((byte)0)) { |
226 } |
244 // seq[0] is version |
227 |
245 if (seq.isConstructed() && seq.isContextSpecific()) { |
228 // BasicOCSPResponse |
246 //System.out.println ("version is available"); |
229 DerInputStream basicOCSPResponse = |
247 seq = seq.data.getDerValue(); |
230 new DerInputStream(derIn.getOctetString()); |
248 int version = seq.getInteger(); |
231 |
249 if (seq.data.available() != 0) { |
232 DerValue[] seqTmp = basicOCSPResponse.getSequence(2); |
250 throw new IOException("Bad encoding in version " + |
233 DerValue responseData = seqTmp[0]; |
251 " element of OCSP response: bad format"); |
234 |
252 } |
235 // Need the DER encoded ResponseData to verify the signature later |
253 seq = seqDerIn.getDerValue(); |
236 byte[] responseDataDer = seqTmp[0].toByteArray(); |
254 } |
237 |
255 } |
238 // tbsResponseData |
256 |
239 if (responseData.tag != DerValue.tag_Sequence) { |
257 // responderID |
240 throw new IOException("Bad encoding in tbsResponseData " + |
258 short tag = (byte)(seq.tag & 0x1f); |
241 " element of OCSP response: expected ASN.1 SEQUENCE tag."); |
259 if (tag == NAME_TAG) { |
242 } |
260 if (DEBUG != null) { |
243 DerInputStream seqDerIn = responseData.data; |
261 X500Name responderName = new X500Name(seq.getData()); |
244 DerValue seq = seqDerIn.getDerValue(); |
262 DEBUG.println("OCSP Responder name: " + responderName); |
245 |
263 } |
246 // version |
264 } else if (tag == KEY_TAG) { |
247 if (seq.isContextSpecific((byte)0)) { |
265 // Ignore, for now |
248 // seq[0] is version |
266 } else { |
249 if (seq.isConstructed() && seq.isContextSpecific()) { |
267 throw new IOException("Bad encoding in responderID element of " + |
250 //System.out.println ("version is available"); |
268 "OCSP response: expected ASN.1 context specific tag 0 or 1"); |
251 seq = seq.data.getDerValue(); |
269 } |
252 version = seq.getInteger(); |
270 |
253 if (seq.data.available() != 0) { |
271 // producedAt |
254 throw new IOException("Bad encoding in version " + |
272 seq = seqDerIn.getDerValue(); |
255 " element of OCSP response: bad format"); |
273 if (DEBUG != null) { |
256 } |
274 Date producedAtDate = seq.getGeneralizedTime(); |
257 seq = seqDerIn.getDerValue(); |
275 DEBUG.println("OCSP response produced at: " + producedAtDate); |
258 } |
276 } |
259 } |
277 |
260 |
278 // responses |
261 // responderID |
279 DerValue[] singleResponseDer = seqDerIn.getSequence(1); |
262 short tag = (byte)(seq.tag & 0x1f); |
280 singleResponseMap |
263 if (tag == NAME_TAG) { |
281 = new HashMap<CertId, SingleResponse>(singleResponseDer.length); |
264 responderName = new CertificateIssuerName(seq.getData()); |
282 if (DEBUG != null) { |
265 if (DEBUG != null) { |
283 DEBUG.println("OCSP number of SingleResponses: " |
266 DEBUG.println("OCSP Responder name: " + responderName); |
284 + singleResponseDer.length); |
267 } |
285 } |
268 } else if (tag == KEY_TAG) { |
286 for (int i = 0; i < singleResponseDer.length; i++) { |
269 // Ignore, for now |
287 SingleResponse singleResponse |
270 } else { |
288 = new SingleResponse(singleResponseDer[i]); |
271 throw new IOException("Bad encoding in responderID element " + |
289 singleResponseMap.put(singleResponse.getCertId(), singleResponse); |
272 "of OCSP response: expected ASN.1 context specific tag 0 " + |
290 } |
273 "or 1"); |
291 |
274 } |
292 // responseExtensions |
275 |
293 if (seqDerIn.available() > 0) { |
276 // producedAt |
|
277 seq = seqDerIn.getDerValue(); |
294 seq = seqDerIn.getDerValue(); |
278 producedAtDate = seq.getGeneralizedTime(); |
295 if (seq.isContextSpecific((byte)1)) { |
279 |
296 DerValue[] responseExtDer = seq.data.getSequence(3); |
280 // responses |
297 for (int i = 0; i < responseExtDer.length; i++) { |
281 DerValue[] singleResponseDer = seqDerIn.getSequence(1); |
298 Extension responseExtension |
282 // Examine only the first response |
299 = new Extension(responseExtDer[i]); |
283 singleResponse = new SingleResponse(singleResponseDer[0]); |
300 if (DEBUG != null) { |
284 |
301 DEBUG.println("OCSP extension: " + responseExtension); |
285 // responseExtensions |
302 } |
286 if (seqDerIn.available() > 0) { |
303 if (responseExtension.getExtensionId().equals( |
287 seq = seqDerIn.getDerValue(); |
304 OCSP_NONCE_EXTENSION_OID)) { |
288 if (seq.isContextSpecific((byte)1)) { |
305 /* |
289 DerValue[] responseExtDer = seq.data.getSequence(3); |
306 ocspNonce = |
290 Extension[] responseExtension = |
307 responseExtension[i].getExtensionValue(); |
291 new Extension[responseExtDer.length]; |
308 */ |
292 for (int i = 0; i < responseExtDer.length; i++) { |
309 } else if (responseExtension.isCritical()) { |
293 responseExtension[i] = new Extension(responseExtDer[i]); |
310 throw new IOException( |
294 if (DEBUG != null) { |
311 "Unsupported OCSP critical extension: " + |
295 DEBUG.println("OCSP extension: " + |
312 responseExtension.getExtensionId()); |
296 responseExtension[i]); |
313 } |
297 } |
314 } |
298 if ((responseExtension[i].getExtensionId()).equals( |
315 } |
299 OCSP_NONCE_EXTENSION_OID)) { |
316 } |
300 ocspNonce = |
317 |
301 responseExtension[i].getExtensionValue(); |
318 // signatureAlgorithmId |
302 |
319 AlgorithmId sigAlgId = AlgorithmId.parse(seqTmp[1]); |
303 } else if (responseExtension[i].isCritical()) { |
320 |
304 throw new IOException( |
321 // signature |
305 "Unsupported OCSP critical extension: " + |
322 byte[] signature = seqTmp[2].getBitString(); |
306 responseExtension[i].getExtensionId()); |
323 X509CertImpl[] x509Certs = null; |
307 } |
324 |
308 } |
325 // if seq[3] is available , then it is a sequence of certificates |
309 } |
326 if (seqTmp.length > 3) { |
310 } |
327 // certs are available |
311 |
328 DerValue seqCert = seqTmp[3]; |
312 // signatureAlgorithmId |
329 if (!seqCert.isContextSpecific((byte)0)) { |
313 sigAlgId = AlgorithmId.parse(seqTmp[1]); |
330 throw new IOException("Bad encoding in certs element of " + |
314 |
331 "OCSP response: expected ASN.1 context specific tag 0."); |
315 // signature |
332 } |
316 byte[] signature = seqTmp[2].getBitString(); |
333 DerValue[] certs = seqCert.getData().getSequence(3); |
317 X509CertImpl[] x509Certs = null; |
334 x509Certs = new X509CertImpl[certs.length]; |
318 |
335 try { |
319 // if seq[3] is available , then it is a sequence of certificates |
|
320 if (seqTmp.length > 3) { |
|
321 // certs are available |
|
322 DerValue seqCert = seqTmp[3]; |
|
323 if (! seqCert.isContextSpecific((byte)0)) { |
|
324 throw new IOException("Bad encoding in certs element " + |
|
325 "of OCSP response: expected ASN.1 context specific tag 0."); |
|
326 } |
|
327 DerValue[] certs = (seqCert.getData()).getSequence(3); |
|
328 x509Certs = new X509CertImpl[certs.length]; |
|
329 for (int i = 0; i < certs.length; i++) { |
336 for (int i = 0; i < certs.length; i++) { |
330 x509Certs[i] = new X509CertImpl(certs[i].toByteArray()); |
337 x509Certs[i] = new X509CertImpl(certs[i].toByteArray()); |
331 } |
338 } |
332 } |
339 } catch (CertificateException ce) { |
333 |
340 throw new IOException("Bad encoding in X509 Certificate", ce); |
334 // Check whether the cert returned by the responder is trusted |
341 } |
335 if (x509Certs != null && x509Certs[0] != null) { |
342 } |
336 X509CertImpl cert = x509Certs[0]; |
343 |
337 |
344 // Check whether the cert returned by the responder is trusted |
338 // First check if the cert matches the responder cert which |
345 if (x509Certs != null && x509Certs[0] != null) { |
339 // was set locally. |
346 X509CertImpl cert = x509Certs[0]; |
340 if (cert.equals(responderCert)) { |
347 |
341 // cert is trusted, now verify the signed response |
348 // First check if the cert matches the responder cert which |
342 |
349 // was set locally. |
343 // Next check if the cert was issued by the responder cert |
350 if (cert.equals(responderCert)) { |
344 // which was set locally. |
351 // cert is trusted, now verify the signed response |
345 } else if (cert.getIssuerX500Principal().equals( |
352 |
346 responderCert.getSubjectX500Principal())) { |
353 // Next check if the cert was issued by the responder cert |
347 |
354 // which was set locally. |
348 // Check for the OCSPSigning key purpose |
355 } else if (cert.getIssuerX500Principal().equals( |
|
356 responderCert.getSubjectX500Principal())) { |
|
357 |
|
358 // Check for the OCSPSigning key purpose |
|
359 try { |
349 List<String> keyPurposes = cert.getExtendedKeyUsage(); |
360 List<String> keyPurposes = cert.getExtendedKeyUsage(); |
350 if (keyPurposes == null || |
361 if (keyPurposes == null || |
351 !keyPurposes.contains(KP_OCSP_SIGNING_OID)) { |
362 !keyPurposes.contains(KP_OCSP_SIGNING_OID)) { |
352 if (DEBUG != null) { |
|
353 DEBUG.println("Responder's certificate is not " + |
|
354 "valid for signing OCSP responses."); |
|
355 } |
|
356 throw new CertPathValidatorException( |
363 throw new CertPathValidatorException( |
357 "Responder's certificate not valid for signing " + |
364 "Responder's certificate not valid for signing " + |
358 "OCSP responses"); |
365 "OCSP responses"); |
359 } |
366 } |
360 |
367 } catch (CertificateParsingException cpe) { |
361 // check the validity |
368 // assume cert is not valid for signing |
362 try { |
369 throw new CertPathValidatorException( |
363 Date dateCheckedAgainst = params.getDate(); |
370 "Responder's certificate not valid for signing " + |
364 if (dateCheckedAgainst == null) { |
371 "OCSP responses", cpe); |
365 cert.checkValidity(); |
372 } |
366 } else { |
373 |
367 cert.checkValidity(dateCheckedAgainst); |
374 // check the validity |
368 } |
375 try { |
369 } catch (GeneralSecurityException e) { |
376 if (dateCheckedAgainst == null) { |
370 if (DEBUG != null) { |
377 cert.checkValidity(); |
371 DEBUG.println("Responder's certificate is not " + |
|
372 "within the validity period."); |
|
373 } |
|
374 throw new CertPathValidatorException( |
|
375 "Responder's certificate not within the " + |
|
376 "validity period"); |
|
377 } |
|
378 |
|
379 // check for revocation |
|
380 // |
|
381 // A CA may specify that an OCSP client can trust a |
|
382 // responder for the lifetime of the responder's |
|
383 // certificate. The CA does so by including the |
|
384 // extension id-pkix-ocsp-nocheck. |
|
385 // |
|
386 Extension noCheck = |
|
387 cert.getExtension(PKIXExtensions.OCSPNoCheck_Id); |
|
388 if (noCheck != null) { |
|
389 if (DEBUG != null) { |
|
390 DEBUG.println("Responder's certificate includes " + |
|
391 "the extension id-pkix-ocsp-nocheck."); |
|
392 } |
|
393 } else { |
378 } else { |
394 // we should do the revocating checking of the |
379 cert.checkValidity(dateCheckedAgainst); |
395 // authorized responder in a future update. |
380 } |
396 } |
381 } catch (GeneralSecurityException e) { |
397 |
382 throw new CertPathValidatorException( |
398 // verify the signature |
383 "Responder's certificate not within the " + |
399 try { |
384 "validity period", e); |
400 cert.verify(responderCert.getPublicKey()); |
385 } |
401 responderCert = cert; |
386 |
402 // cert is trusted, now verify the signed response |
387 // check for revocation |
403 |
388 // |
404 } catch (GeneralSecurityException e) { |
389 // A CA may specify that an OCSP client can trust a |
405 responderCert = null; |
390 // responder for the lifetime of the responder's |
|
391 // certificate. The CA does so by including the |
|
392 // extension id-pkix-ocsp-nocheck. |
|
393 // |
|
394 Extension noCheck = |
|
395 cert.getExtension(PKIXExtensions.OCSPNoCheck_Id); |
|
396 if (noCheck != null) { |
|
397 if (DEBUG != null) { |
|
398 DEBUG.println("Responder's certificate includes " + |
|
399 "the extension id-pkix-ocsp-nocheck."); |
406 } |
400 } |
407 } else { |
401 } else { |
408 if (DEBUG != null) { |
402 // we should do the revocation checking of the |
409 DEBUG.println("Responder's certificate is not " + |
403 // authorized responder in a future update. |
410 "authorized to sign OCSP responses."); |
404 } |
411 } |
405 |
412 throw new CertPathValidatorException( |
406 // verify the signature |
413 "Responder's certificate not authorized to sign " + |
407 try { |
414 "OCSP responses"); |
408 cert.verify(responderCert.getPublicKey()); |
415 } |
409 responderCert = cert; |
416 } |
410 // cert is trusted, now verify the signed response |
417 |
411 |
418 // Confirm that the signed response was generated using the public |
412 } catch (GeneralSecurityException e) { |
419 // key from the trusted responder cert |
413 responderCert = null; |
420 if (responderCert != null) { |
|
421 |
|
422 if (! verifyResponse(responseDataDer, responderCert, |
|
423 sigAlgId, signature, params)) { |
|
424 if (DEBUG != null) { |
|
425 DEBUG.println("Error verifying OCSP Responder's " + |
|
426 "signature"); |
|
427 } |
|
428 throw new CertPathValidatorException( |
|
429 "Error verifying OCSP Responder's signature"); |
|
430 } |
414 } |
431 } else { |
415 } else { |
432 // Need responder's cert in order to verify the signature |
|
433 if (DEBUG != null) { |
|
434 DEBUG.println("Unable to verify OCSP Responder's " + |
|
435 "signature"); |
|
436 } |
|
437 throw new CertPathValidatorException( |
416 throw new CertPathValidatorException( |
438 "Unable to verify OCSP Responder's signature"); |
417 "Responder's certificate is not authorized to sign " + |
439 } |
418 "OCSP responses"); |
440 } catch (CertPathValidatorException cpve) { |
419 } |
441 throw cpve; |
420 } |
442 } catch (Exception e) { |
421 |
443 throw new CertPathValidatorException(e); |
422 // Confirm that the signed response was generated using the public |
444 } |
423 // key from the trusted responder cert |
|
424 if (responderCert != null) { |
|
425 if (!verifyResponse(responseDataDer, responderCert, |
|
426 sigAlgId, signature)) { |
|
427 throw new CertPathValidatorException( |
|
428 "Error verifying OCSP Responder's signature"); |
|
429 } |
|
430 } else { |
|
431 // Need responder's cert in order to verify the signature |
|
432 throw new CertPathValidatorException( |
|
433 "Unable to verify OCSP Responder's signature"); |
|
434 } |
|
435 } |
|
436 |
|
437 /** |
|
438 * Returns the OCSP ResponseStatus. |
|
439 */ |
|
440 ResponseStatus getResponseStatus() { |
|
441 return responseStatus; |
445 } |
442 } |
446 |
443 |
447 /* |
444 /* |
448 * Verify the signature of the OCSP response. |
445 * Verify the signature of the OCSP response. |
449 * The responder's cert is implicitly trusted. |
446 * The responder's cert is implicitly trusted. |
450 */ |
447 */ |
451 private boolean verifyResponse(byte[] responseData, X509Certificate cert, |
448 private boolean verifyResponse(byte[] responseData, X509Certificate cert, |
452 AlgorithmId sigAlgId, byte[] signBytes, PKIXParameters params) |
449 AlgorithmId sigAlgId, byte[] signBytes) |
453 throws SignatureException { |
450 throws CertPathValidatorException { |
454 |
451 |
455 try { |
452 try { |
456 |
|
457 Signature respSignature = Signature.getInstance(sigAlgId.getName()); |
453 Signature respSignature = Signature.getInstance(sigAlgId.getName()); |
458 respSignature.initVerify(cert); |
454 respSignature.initVerify(cert); |
459 respSignature.update(responseData); |
455 respSignature.update(responseData); |
460 |
456 |
461 if (respSignature.verify(signBytes)) { |
457 if (respSignature.verify(signBytes)) { |
470 "Error verifying signature of OCSP Responder"); |
466 "Error verifying signature of OCSP Responder"); |
471 } |
467 } |
472 return false; |
468 return false; |
473 } |
469 } |
474 } catch (InvalidKeyException ike) { |
470 } catch (InvalidKeyException ike) { |
475 throw new SignatureException(ike); |
471 throw new CertPathValidatorException(ike); |
476 |
|
477 } catch (NoSuchAlgorithmException nsae) { |
472 } catch (NoSuchAlgorithmException nsae) { |
478 throw new SignatureException(nsae); |
473 throw new CertPathValidatorException(nsae); |
|
474 } catch (SignatureException se) { |
|
475 throw new CertPathValidatorException(se); |
479 } |
476 } |
480 } |
477 } |
481 |
478 |
482 /* |
479 /** |
483 * Return the revocation status code for a given certificate. |
480 * Returns the SingleResponse of the specified CertId, or null if |
|
481 * there is no response for that CertId. |
484 */ |
482 */ |
485 // used by OCSPChecker |
483 SingleResponse getSingleResponse(CertId certId) { |
486 int getCertStatus(SerialNumber sn) { |
484 return singleResponseMap.get(certId); |
487 // ignore serial number for now; if we support multiple |
|
488 // requests/responses then it will be used |
|
489 return singleResponse.getStatus(); |
|
490 } |
|
491 |
|
492 // used by OCSPChecker |
|
493 CertId getCertId() { |
|
494 return singleResponse.getCertId(); |
|
495 } |
|
496 |
|
497 Date getRevocationTime() { |
|
498 return singleResponse.getRevocationTime(); |
|
499 } |
|
500 |
|
501 CRLReason getRevocationReason() { |
|
502 return singleResponse.getRevocationReason(); |
|
503 } |
|
504 |
|
505 Map<String, java.security.cert.Extension> getSingleExtensions() { |
|
506 return singleResponse.getSingleExtensions(); |
|
507 } |
|
508 |
|
509 /* |
|
510 * Map an OCSP response status code to a string. |
|
511 */ |
|
512 static private String responseToText(int status) { |
|
513 switch (status) { |
|
514 case 0: |
|
515 return "Successful"; |
|
516 case 1: |
|
517 return "Malformed request"; |
|
518 case 2: |
|
519 return "Internal error"; |
|
520 case 3: |
|
521 return "Try again later"; |
|
522 case 4: |
|
523 return "Unused status code"; |
|
524 case 5: |
|
525 return "Request must be signed"; |
|
526 case 6: |
|
527 return "Request is unauthorized"; |
|
528 default: |
|
529 return ("Unknown status code: " + status); |
|
530 } |
|
531 } |
|
532 |
|
533 /* |
|
534 * Map a certificate's revocation status code to a string. |
|
535 */ |
|
536 // used by OCSPChecker |
|
537 static String certStatusToText(int certStatus) { |
|
538 switch (certStatus) { |
|
539 case 0: |
|
540 return "Good"; |
|
541 case 1: |
|
542 return "Revoked"; |
|
543 case 2: |
|
544 return "Unknown"; |
|
545 default: |
|
546 return ("Unknown certificate status code: " + certStatus); |
|
547 } |
|
548 } |
485 } |
549 |
486 |
550 /* |
487 /* |
551 * A class representing a single OCSP response. |
488 * A class representing a single OCSP response. |
552 */ |
489 */ |
553 private class SingleResponse { |
490 final static class SingleResponse implements OCSP.RevocationStatus { |
554 private CertId certId; |
491 private final CertId certId; |
555 private int certStatus; |
492 private final CertStatus certStatus; |
556 private Date thisUpdate; |
493 private final Date thisUpdate; |
557 private Date nextUpdate; |
494 private final Date nextUpdate; |
558 private Date revocationTime; |
495 private final Date revocationTime; |
559 private CRLReason revocationReason = CRLReason.UNSPECIFIED; |
496 private final CRLReason revocationReason; |
560 private HashMap<String, java.security.cert.Extension> singleExtensions; |
497 private final Map<String, java.security.cert.Extension> singleExtensions; |
561 |
498 |
562 private SingleResponse(DerValue der) throws IOException { |
499 private SingleResponse(DerValue der) throws IOException { |
563 if (der.tag != DerValue.tag_Sequence) { |
500 if (der.tag != DerValue.tag_Sequence) { |
564 throw new IOException("Bad ASN.1 encoding in SingleResponse"); |
501 throw new IOException("Bad ASN.1 encoding in SingleResponse"); |
565 } |
502 } |
566 DerInputStream tmp = der.data; |
503 DerInputStream tmp = der.data; |
567 |
504 |
568 certId = new CertId(tmp.getDerValue().data); |
505 certId = new CertId(tmp.getDerValue().data); |
569 DerValue derVal = tmp.getDerValue(); |
506 DerValue derVal = tmp.getDerValue(); |
570 short tag = (byte)(derVal.tag & 0x1f); |
507 short tag = (byte)(derVal.tag & 0x1f); |
571 if (tag == CERT_STATUS_GOOD) { |
508 if (tag == CERT_STATUS_REVOKED) { |
572 certStatus = CERT_STATUS_GOOD; |
509 certStatus = CertStatus.REVOKED; |
573 } else if (tag == CERT_STATUS_REVOKED) { |
|
574 certStatus = CERT_STATUS_REVOKED; |
|
575 revocationTime = derVal.data.getGeneralizedTime(); |
510 revocationTime = derVal.data.getGeneralizedTime(); |
576 if (derVal.data.available() != 0) { |
511 if (derVal.data.available() != 0) { |
577 int reason = derVal.getEnumerated(); |
512 DerValue dv = derVal.data.getDerValue(); |
578 // if reason out-of-range just leave as UNSPECIFIED |
513 tag = (byte)(dv.tag & 0x1f); |
579 if (reason >= 0 && reason < values.length) { |
514 if (tag == 0) { |
580 revocationReason = values[reason]; |
515 int reason = dv.data.getEnumerated(); |
581 } |
516 // if reason out-of-range just leave as UNSPECIFIED |
|
517 if (reason >= 0 && reason < values.length) { |
|
518 revocationReason = values[reason]; |
|
519 } else { |
|
520 revocationReason = CRLReason.UNSPECIFIED; |
|
521 } |
|
522 } else { |
|
523 revocationReason = CRLReason.UNSPECIFIED; |
|
524 } |
|
525 } else { |
|
526 revocationReason = CRLReason.UNSPECIFIED; |
582 } |
527 } |
583 // RevokedInfo |
528 // RevokedInfo |
584 if (DEBUG != null) { |
529 if (DEBUG != null) { |
585 DEBUG.println("Revocation time: " + revocationTime); |
530 DEBUG.println("Revocation time: " + revocationTime); |
586 DEBUG.println("Revocation reason: " + revocationReason); |
531 DEBUG.println("Revocation reason: " + revocationReason); |
587 } |
532 } |
588 |
|
589 } else if (tag == CERT_STATUS_UNKNOWN) { |
|
590 certStatus = CERT_STATUS_UNKNOWN; |
|
591 |
|
592 } else { |
533 } else { |
593 throw new IOException("Invalid certificate status"); |
534 revocationTime = null; |
|
535 revocationReason = CRLReason.UNSPECIFIED; |
|
536 if (tag == CERT_STATUS_GOOD) { |
|
537 certStatus = CertStatus.GOOD; |
|
538 } else if (tag == CERT_STATUS_UNKNOWN) { |
|
539 certStatus = CertStatus.UNKNOWN; |
|
540 } else { |
|
541 throw new IOException("Invalid certificate status"); |
|
542 } |
594 } |
543 } |
595 |
544 |
596 thisUpdate = tmp.getGeneralizedTime(); |
545 thisUpdate = tmp.getGeneralizedTime(); |
597 |
546 |
598 if (tmp.available() == 0) { |
547 if (tmp.available() == 0) { |
599 // we are done |
548 // we are done |
|
549 nextUpdate = null; |
600 } else { |
550 } else { |
601 derVal = tmp.getDerValue(); |
551 derVal = tmp.getDerValue(); |
602 tag = (byte)(derVal.tag & 0x1f); |
552 tag = (byte)(derVal.tag & 0x1f); |
603 if (tag == 0) { |
553 if (tag == 0) { |
604 // next update |
554 // next update |