1 /* 2 * Copyright (c) 2003, 2014, 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.*; 29 import java.security.*; 30 import java.security.cert.CertificateException; 31 import java.security.cert.CertificateParsingException; 32 import java.security.cert.CertPathValidatorException; 33 import java.security.cert.CertPathValidatorException.BasicReason; 34 import java.security.cert.CRLReason; 35 import java.security.cert.TrustAnchor; 36 import java.security.cert.X509Certificate; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.Date; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 import javax.security.auth.x500.X500Principal; 45 46 import sun.misc.HexDumpEncoder; 47 import sun.security.action.GetIntegerAction; 48 import sun.security.x509.*; 49 import sun.security.util.*; 50 51 /** 52 * This class is used to process an OCSP response. 53 * The OCSP Response is defined 54 * in RFC 2560 and the ASN.1 encoding is as follows: 55 * <pre> 56 * 57 * OCSPResponse ::= SEQUENCE { 58 * responseStatus OCSPResponseStatus, 59 * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } 60 * 61 * OCSPResponseStatus ::= ENUMERATED { 62 * successful (0), --Response has valid confirmations 63 * malformedRequest (1), --Illegal confirmation request 64 * internalError (2), --Internal error in issuer 65 * tryLater (3), --Try again later 66 * --(4) is not used 67 * sigRequired (5), --Must sign the request 68 * unauthorized (6) --Request unauthorized 69 * } 70 * 71 * ResponseBytes ::= SEQUENCE { 72 * responseType OBJECT IDENTIFIER, 73 * response OCTET STRING } 74 * 75 * BasicOCSPResponse ::= SEQUENCE { 76 * tbsResponseData ResponseData, 77 * signatureAlgorithm AlgorithmIdentifier, 78 * signature BIT STRING, 79 * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } 80 * 81 * The value for signature SHALL be computed on the hash of the DER 82 * encoding ResponseData. 83 * 84 * ResponseData ::= SEQUENCE { 85 * version [0] EXPLICIT Version DEFAULT v1, 86 * responderID ResponderID, 87 * producedAt GeneralizedTime, 88 * responses SEQUENCE OF SingleResponse, 89 * responseExtensions [1] EXPLICIT Extensions OPTIONAL } 90 * 91 * ResponderID ::= CHOICE { 92 * byName [1] Name, 93 * byKey [2] KeyHash } 94 * 95 * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key 96 * (excluding the tag and length fields) 97 * 98 * SingleResponse ::= SEQUENCE { 99 * certID CertID, 100 * certStatus CertStatus, 101 * thisUpdate GeneralizedTime, 102 * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, 103 * singleExtensions [1] EXPLICIT Extensions OPTIONAL } 104 * 105 * CertStatus ::= CHOICE { 106 * good [0] IMPLICIT NULL, 107 * revoked [1] IMPLICIT RevokedInfo, 108 * unknown [2] IMPLICIT UnknownInfo } 109 * 110 * RevokedInfo ::= SEQUENCE { 111 * revocationTime GeneralizedTime, 112 * revocationReason [0] EXPLICIT CRLReason OPTIONAL } 113 * 114 * UnknownInfo ::= NULL -- this can be replaced with an enumeration 115 * 116 * </pre> 117 * 118 * @author Ram Marti 119 */ 120 121 public final class OCSPResponse { 122 123 public enum ResponseStatus { 124 SUCCESSFUL, // Response has valid confirmations 125 MALFORMED_REQUEST, // Illegal request 126 INTERNAL_ERROR, // Internal error in responder 127 TRY_LATER, // Try again later 128 UNUSED, // is not used 129 SIG_REQUIRED, // Must sign the request 130 UNAUTHORIZED // Request unauthorized 131 }; 132 private static ResponseStatus[] rsvalues = ResponseStatus.values(); 133 134 private static final Debug debug = Debug.getInstance("certpath"); 135 private static final boolean dump = debug != null && Debug.isOn("ocsp"); 136 private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID = 137 ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); 138 private static final int CERT_STATUS_GOOD = 0; 139 private static final int CERT_STATUS_REVOKED = 1; 140 private static final int CERT_STATUS_UNKNOWN = 2; 141 142 // ResponderID CHOICE tags 143 private static final int NAME_TAG = 1; 144 private static final int KEY_TAG = 2; 145 146 // Object identifier for the OCSPSigning key purpose 147 private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; 148 149 // Default maximum clock skew in milliseconds (15 minutes) 150 // allowed when checking validity of OCSP responses 151 private static final int DEFAULT_MAX_CLOCK_SKEW = 900000; 152 153 /** 154 * Integer value indicating the maximum allowable clock skew, in seconds, 155 * to be used for the OCSP check. 156 */ 157 private static final int MAX_CLOCK_SKEW = initializeClockSkew(); 158 159 /** 160 * Initialize the maximum allowable clock skew by getting the OCSP 161 * clock skew system property. If the property has not been set, or if its 162 * value is negative, set the skew to the default. 163 */ 164 private static int initializeClockSkew() { 165 Integer tmp = java.security.AccessController.doPrivileged( 166 new GetIntegerAction("com.sun.security.ocsp.clockSkew")); 167 if (tmp == null || tmp < 0) { 168 return DEFAULT_MAX_CLOCK_SKEW; 169 } 170 // Convert to milliseconds, as the system property will be 171 // specified in seconds 172 return tmp * 1000; 173 } 174 175 // an array of all of the CRLReasons (used in SingleResponse) 176 private static CRLReason[] values = CRLReason.values(); 177 178 private final ResponseStatus responseStatus; 179 private final Map<CertId, SingleResponse> singleResponseMap; 180 private final AlgorithmId sigAlgId; 181 private final byte[] signature; 182 private final byte[] tbsResponseData; 183 private final byte[] responseNonce; 184 private List<X509CertImpl> certs; 185 private X509CertImpl signerCert = null; 186 private X500Principal responderName = null; 187 private KeyIdentifier responderKeyId = null; 188 189 /* 190 * Create an OCSP response from its ASN.1 DER encoding. 191 */ 192 OCSPResponse(byte[] bytes) throws IOException { 193 if (dump) { 194 HexDumpEncoder hexEnc = new HexDumpEncoder(); 195 debug.println("OCSPResponse bytes...\n\n" + 196 hexEnc.encode(bytes) + "\n"); 197 } 198 DerValue der = new DerValue(bytes); 199 if (der.tag != DerValue.tag_Sequence) { 200 throw new IOException("Bad encoding in OCSP response: " + 201 "expected ASN.1 SEQUENCE tag."); 202 } 203 DerInputStream derIn = der.getData(); 204 205 // responseStatus 206 int status = derIn.getEnumerated(); 207 if (status >= 0 && status < rsvalues.length) { 208 responseStatus = rsvalues[status]; 209 } else { 210 // unspecified responseStatus 211 throw new IOException("Unknown OCSPResponse status: " + status); 212 } 213 if (debug != null) { 214 debug.println("OCSP response status: " + responseStatus); 215 } 216 if (responseStatus != ResponseStatus.SUCCESSFUL) { 217 // no need to continue, responseBytes are not set. 218 singleResponseMap = Collections.emptyMap(); 219 certs = new ArrayList<X509CertImpl>(); 220 sigAlgId = null; 221 signature = null; 222 tbsResponseData = null; 223 responseNonce = null; 224 return; 225 } 226 227 // responseBytes 228 der = derIn.getDerValue(); 229 if (!der.isContextSpecific((byte)0)) { 230 throw new IOException("Bad encoding in responseBytes element " + 231 "of OCSP response: expected ASN.1 context specific tag 0."); 232 } 233 DerValue tmp = der.data.getDerValue(); 234 if (tmp.tag != DerValue.tag_Sequence) { 235 throw new IOException("Bad encoding in responseBytes element " + 236 "of OCSP response: expected ASN.1 SEQUENCE tag."); 237 } 238 239 // responseType 240 derIn = tmp.data; 241 ObjectIdentifier responseType = derIn.getOID(); 242 if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) { 243 if (debug != null) { 244 debug.println("OCSP response type: basic"); 245 } 246 } else { 247 if (debug != null) { 248 debug.println("OCSP response type: " + responseType); 249 } 250 throw new IOException("Unsupported OCSP response type: " + 251 responseType); 252 } 253 254 // BasicOCSPResponse 255 DerInputStream basicOCSPResponse = 256 new DerInputStream(derIn.getOctetString()); 257 258 DerValue[] seqTmp = basicOCSPResponse.getSequence(2); 259 if (seqTmp.length < 3) { 260 throw new IOException("Unexpected BasicOCSPResponse value"); 261 } 262 263 DerValue responseData = seqTmp[0]; 264 265 // Need the DER encoded ResponseData to verify the signature later 266 tbsResponseData = seqTmp[0].toByteArray(); 267 268 // tbsResponseData 269 if (responseData.tag != DerValue.tag_Sequence) { 270 throw new IOException("Bad encoding in tbsResponseData " + 271 "element of OCSP response: expected ASN.1 SEQUENCE tag."); 272 } 273 DerInputStream seqDerIn = responseData.data; 274 DerValue seq = seqDerIn.getDerValue(); 275 276 // version 277 if (seq.isContextSpecific((byte)0)) { 278 // seq[0] is version 279 if (seq.isConstructed() && seq.isContextSpecific()) { 280 //System.out.println ("version is available"); 281 seq = seq.data.getDerValue(); 282 int version = seq.getInteger(); 283 if (seq.data.available() != 0) { 284 throw new IOException("Bad encoding in version " + 285 " element of OCSP response: bad format"); 286 } 287 seq = seqDerIn.getDerValue(); 288 } 289 } 290 291 // responderID 292 short tag = (byte)(seq.tag & 0x1f); 293 if (tag == NAME_TAG) { 294 responderName = new X500Principal(seq.getData().toByteArray()); 295 if (debug != null) { 296 debug.println("Responder's name: " + responderName); 297 } 298 } else if (tag == KEY_TAG) { 299 responderKeyId = new KeyIdentifier(seq.getData().getOctetString()); 300 if (debug != null) { 301 debug.println("Responder's key ID: " + 302 Debug.toString(responderKeyId.getIdentifier())); 303 } 304 } else { 305 throw new IOException("Bad encoding in responderID element of " + 306 "OCSP response: expected ASN.1 context specific tag 0 or 1"); 307 } 308 309 // producedAt 310 seq = seqDerIn.getDerValue(); 311 if (debug != null) { 312 Date producedAtDate = seq.getGeneralizedTime(); 313 debug.println("OCSP response produced at: " + producedAtDate); 314 } 315 316 // responses 317 DerValue[] singleResponseDer = seqDerIn.getSequence(1); 318 singleResponseMap = new HashMap<>(singleResponseDer.length); 319 if (debug != null) { 320 debug.println("OCSP number of SingleResponses: " 321 + singleResponseDer.length); 322 } 323 for (int i = 0; i < singleResponseDer.length; i++) { 324 SingleResponse singleResponse = 325 new SingleResponse(singleResponseDer[i]); 326 singleResponseMap.put(singleResponse.getCertId(), singleResponse); 327 } 328 329 // responseExtensions 330 byte[] nonce = null; 331 if (seqDerIn.available() > 0) { 332 seq = seqDerIn.getDerValue(); 333 if (seq.isContextSpecific((byte)1)) { 334 DerValue[] responseExtDer = seq.data.getSequence(3); 335 for (int i = 0; i < responseExtDer.length; i++) { 336 Extension ext = new Extension(responseExtDer[i]); 337 if (debug != null) { 338 debug.println("OCSP extension: " + ext); 339 } 340 // Only the NONCE extension is recognized 341 if (ext.getExtensionId().equals((Object) 342 OCSP.NONCE_EXTENSION_OID)) 343 { 344 nonce = ext.getExtensionValue(); 345 } else if (ext.isCritical()) { 346 throw new IOException( 347 "Unsupported OCSP critical extension: " + 348 ext.getExtensionId()); 349 } 350 } 351 } 352 } 353 responseNonce = nonce; 354 355 // signatureAlgorithmId 356 sigAlgId = AlgorithmId.parse(seqTmp[1]); 357 358 // signature 359 signature = seqTmp[2].getBitString(); 360 361 // if seq[3] is available , then it is a sequence of certificates 362 if (seqTmp.length > 3) { 363 // certs are available 364 DerValue seqCert = seqTmp[3]; 365 if (!seqCert.isContextSpecific((byte)0)) { 366 throw new IOException("Bad encoding in certs element of " + 367 "OCSP response: expected ASN.1 context specific tag 0."); 368 } 369 DerValue[] derCerts = seqCert.getData().getSequence(3); 370 certs = new ArrayList<X509CertImpl>(derCerts.length); 371 try { 372 for (int i = 0; i < derCerts.length; i++) { 373 X509CertImpl cert = 374 new X509CertImpl(derCerts[i].toByteArray()); 375 certs.add(cert); 376 377 if (debug != null) { 378 debug.println("OCSP response cert #" + (i + 1) + ": " + 379 cert.getSubjectX500Principal()); 380 } 381 } 382 } catch (CertificateException ce) { 383 throw new IOException("Bad encoding in X509 Certificate", ce); 384 } 385 } else { 386 certs = new ArrayList<X509CertImpl>(); 387 } 388 } 389 390 void verify(List<CertId> certIds, X509Certificate issuerCert, 391 X509Certificate responderCert, Date date, byte[] nonce) 392 throws CertPathValidatorException 393 { 394 switch (responseStatus) { 395 case SUCCESSFUL: 396 break; 397 case TRY_LATER: 398 case INTERNAL_ERROR: 399 throw new CertPathValidatorException( 400 "OCSP response error: " + responseStatus, null, null, -1, 401 BasicReason.UNDETERMINED_REVOCATION_STATUS); 402 case UNAUTHORIZED: 403 default: 404 throw new CertPathValidatorException("OCSP response error: " + 405 responseStatus); 406 } 407 408 // Check that the response includes a response for all of the 409 // certs that were supplied in the request 410 for (CertId certId : certIds) { 411 SingleResponse sr = getSingleResponse(certId); 412 if (sr == null) { 413 if (debug != null) { 414 debug.println("No response found for CertId: " + certId); 415 } 416 throw new CertPathValidatorException( 417 "OCSP response does not include a response for a " + 418 "certificate supplied in the OCSP request"); 419 } 420 if (debug != null) { 421 debug.println("Status of certificate (with serial number " + 422 certId.getSerialNumber() + ") is: " + sr.getCertStatus()); 423 } 424 } 425 426 // Locate the signer cert 427 if (signerCert == null) { 428 // Add the Issuing CA cert and/or Trusted Responder cert to the list 429 // of certs from the OCSP response 430 try { 431 certs.add(X509CertImpl.toImpl(issuerCert)); 432 if (responderCert != null) { 433 certs.add(X509CertImpl.toImpl(responderCert)); 434 } 435 } catch (CertificateException ce) { 436 throw new CertPathValidatorException( 437 "Invalid issuer or trusted responder certificate", ce); 438 } 439 440 if (responderName != null) { 441 for (X509CertImpl cert : certs) { 442 if (cert.getSubjectX500Principal().equals(responderName)) { 443 signerCert = cert; 444 break; 445 } 446 } 447 } else if (responderKeyId != null) { 448 for (X509CertImpl cert : certs) { 449 // Match responder's key identifier against the cert's SKID 450 // This will match if the SKID is encoded using the 160-bit 451 // SHA-1 hash method as defined in RFC 5280. 452 KeyIdentifier certKeyId = cert.getSubjectKeyId(); 453 if (certKeyId != null && responderKeyId.equals(certKeyId)) { 454 signerCert = cert; 455 break; 456 } else { 457 // The certificate does not have a SKID or may have 458 // been using a different algorithm (ex: see RFC 7093). 459 // Check if the responder's key identifier matches 460 // against a newly generated key identifier of the 461 // cert's public key using the 160-bit SHA-1 method. 462 try { 463 certKeyId = new KeyIdentifier(cert.getPublicKey()); 464 } catch (IOException e) { 465 // ignore 466 } 467 if (responderKeyId.equals(certKeyId)) { 468 signerCert = cert; 469 break; 470 } 471 } 472 } 473 } 474 } 475 476 // Check whether the signer cert returned by the responder is trusted 477 if (signerCert != null) { 478 // Check if the response is signed by the issuing CA 479 if (signerCert.equals(issuerCert)) { 480 if (debug != null) { 481 debug.println("OCSP response is signed by the target's " + 482 "Issuing CA"); 483 } 484 // cert is trusted, now verify the signed response 485 486 // Check if the response is signed by a trusted responder 487 } else if (signerCert.equals(responderCert)) { 488 if (debug != null) { 489 debug.println("OCSP response is signed by a Trusted " + 490 "Responder"); 491 } 492 // cert is trusted, now verify the signed response 493 494 // Check if the response is signed by an authorized responder 495 } else if (signerCert.getIssuerX500Principal().equals( 496 issuerCert.getSubjectX500Principal())) { 497 498 // Check for the OCSPSigning key purpose 499 try { 500 List<String> keyPurposes = signerCert.getExtendedKeyUsage(); 501 if (keyPurposes == null || 502 !keyPurposes.contains(KP_OCSP_SIGNING_OID)) { 503 throw new CertPathValidatorException( 504 "Responder's certificate not valid for signing " + 505 "OCSP responses"); 506 } 507 } catch (CertificateParsingException cpe) { 508 // assume cert is not valid for signing 509 throw new CertPathValidatorException( 510 "Responder's certificate not valid for signing " + 511 "OCSP responses", cpe); 512 } 513 514 // Check algorithm constraints specified in security property 515 // "jdk.certpath.disabledAlgorithms". 516 AlgorithmChecker algChecker = new AlgorithmChecker( 517 new TrustAnchor(issuerCert, null)); 518 algChecker.init(false); 519 algChecker.check(signerCert, Collections.<String>emptySet()); 520 521 // check the validity 522 try { 523 if (date == null) { 524 signerCert.checkValidity(); 525 } else { 526 signerCert.checkValidity(date); 527 } 528 } catch (CertificateException e) { 529 throw new CertPathValidatorException( 530 "Responder's certificate not within the " + 531 "validity period", e); 532 } 533 534 // check for revocation 535 // 536 // A CA may specify that an OCSP client can trust a 537 // responder for the lifetime of the responder's 538 // certificate. The CA does so by including the 539 // extension id-pkix-ocsp-nocheck. 540 // 541 Extension noCheck = 542 signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id); 543 if (noCheck != null) { 544 if (debug != null) { 545 debug.println("Responder's certificate includes " + 546 "the extension id-pkix-ocsp-nocheck."); 547 } 548 } else { 549 // we should do the revocation checking of the 550 // authorized responder in a future update. 551 } 552 553 // verify the signature 554 try { 555 signerCert.verify(issuerCert.getPublicKey()); 556 if (debug != null) { 557 debug.println("OCSP response is signed by an " + 558 "Authorized Responder"); 559 } 560 // cert is trusted, now verify the signed response 561 562 } catch (GeneralSecurityException e) { 563 signerCert = null; 564 } 565 } else { 566 throw new CertPathValidatorException( 567 "Responder's certificate is not authorized to sign " + 568 "OCSP responses"); 569 } 570 } 571 572 // Confirm that the signed response was generated using the public 573 // key from the trusted responder cert 574 if (signerCert != null) { 575 // Check algorithm constraints specified in security property 576 // "jdk.certpath.disabledAlgorithms". 577 AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId); 578 579 if (!verifySignature(signerCert)) { 580 throw new CertPathValidatorException( 581 "Error verifying OCSP Response's signature"); 582 } 583 } else { 584 // Need responder's cert in order to verify the signature 585 throw new CertPathValidatorException( 586 "Unable to verify OCSP Response's signature"); 587 } 588 589 // Check freshness of OCSPResponse 590 if (nonce != null) { 591 if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) { 592 throw new CertPathValidatorException("Nonces don't match"); 593 } 594 } 595 596 long now = (date == null) ? System.currentTimeMillis() : date.getTime(); 597 Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW); 598 Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW); 599 for (SingleResponse sr : singleResponseMap.values()) { 600 if (debug != null) { 601 String until = ""; 602 if (sr.nextUpdate != null) { 603 until = " until " + sr.nextUpdate; 604 } 605 debug.println("Response's validity interval is from " + 606 sr.thisUpdate + until); 607 } 608 609 // Check that the test date is within the validity interval 610 if ((sr.thisUpdate != null && nowPlusSkew.before(sr.thisUpdate)) || 611 (sr.nextUpdate != null && nowMinusSkew.after(sr.nextUpdate))) 612 { 613 throw new CertPathValidatorException( 614 "Response is unreliable: its validity " + 615 "interval is out-of-date"); 616 } 617 } 618 } 619 620 /** 621 * Returns the OCSP ResponseStatus. 622 */ 623 ResponseStatus getResponseStatus() { 624 return responseStatus; 625 } 626 627 /* 628 * Verify the signature of the OCSP response. 629 */ 630 private boolean verifySignature(X509Certificate cert) 631 throws CertPathValidatorException { 632 633 try { 634 Signature respSignature = Signature.getInstance(sigAlgId.getName()); 635 respSignature.initVerify(cert.getPublicKey()); 636 respSignature.update(tbsResponseData); 637 638 if (respSignature.verify(signature)) { 639 if (debug != null) { 640 debug.println("Verified signature of OCSP Response"); 641 } 642 return true; 643 644 } else { 645 if (debug != null) { 646 debug.println( 647 "Error verifying signature of OCSP Response"); 648 } 649 return false; 650 } 651 } catch (InvalidKeyException | NoSuchAlgorithmException | 652 SignatureException e) 653 { 654 throw new CertPathValidatorException(e); 655 } 656 } 657 658 /** 659 * Returns the SingleResponse of the specified CertId, or null if 660 * there is no response for that CertId. 661 */ 662 SingleResponse getSingleResponse(CertId certId) { 663 return singleResponseMap.get(certId); 664 } 665 666 /* 667 * Returns the certificate for the authority that signed the OCSP response. 668 */ 669 X509Certificate getSignerCertificate() { 670 return signerCert; // set in verify() 671 } 672 673 /* 674 * A class representing a single OCSP response. 675 */ 676 final static class SingleResponse implements OCSP.RevocationStatus { 677 private final CertId certId; 678 private final CertStatus certStatus; 679 private final Date thisUpdate; 680 private final Date nextUpdate; 681 private final Date revocationTime; 682 private final CRLReason revocationReason; 683 private final Map<String, java.security.cert.Extension> singleExtensions; 684 685 private SingleResponse(DerValue der) throws IOException { 686 if (der.tag != DerValue.tag_Sequence) { 687 throw new IOException("Bad ASN.1 encoding in SingleResponse"); 688 } 689 DerInputStream tmp = der.data; 690 691 certId = new CertId(tmp.getDerValue().data); 692 DerValue derVal = tmp.getDerValue(); 693 short tag = (byte)(derVal.tag & 0x1f); 694 if (tag == CERT_STATUS_REVOKED) { 695 certStatus = CertStatus.REVOKED; 696 revocationTime = derVal.data.getGeneralizedTime(); 697 if (derVal.data.available() != 0) { 698 DerValue dv = derVal.data.getDerValue(); 699 tag = (byte)(dv.tag & 0x1f); 700 if (tag == 0) { 701 int reason = dv.data.getEnumerated(); 702 // if reason out-of-range just leave as UNSPECIFIED 703 if (reason >= 0 && reason < values.length) { 704 revocationReason = values[reason]; 705 } else { 706 revocationReason = CRLReason.UNSPECIFIED; 707 } 708 } else { 709 revocationReason = CRLReason.UNSPECIFIED; 710 } 711 } else { 712 revocationReason = CRLReason.UNSPECIFIED; 713 } 714 // RevokedInfo 715 if (debug != null) { 716 debug.println("Revocation time: " + revocationTime); 717 debug.println("Revocation reason: " + revocationReason); 718 } 719 } else { 720 revocationTime = null; 721 revocationReason = CRLReason.UNSPECIFIED; 722 if (tag == CERT_STATUS_GOOD) { 723 certStatus = CertStatus.GOOD; 724 } else if (tag == CERT_STATUS_UNKNOWN) { 725 certStatus = CertStatus.UNKNOWN; 726 } else { 727 throw new IOException("Invalid certificate status"); 728 } 729 } 730 731 thisUpdate = tmp.getGeneralizedTime(); 732 733 if (tmp.available() == 0) { 734 // we are done 735 nextUpdate = null; 736 } else { 737 derVal = tmp.getDerValue(); 738 tag = (byte)(derVal.tag & 0x1f); 739 if (tag == 0) { 740 // next update 741 nextUpdate = derVal.data.getGeneralizedTime(); 742 743 if (tmp.available() == 0) { 744 // we are done 745 } else { 746 derVal = tmp.getDerValue(); 747 tag = (byte)(derVal.tag & 0x1f); 748 } 749 } else { 750 nextUpdate = null; 751 } 752 } 753 // singleExtensions 754 if (tmp.available() > 0) { 755 derVal = tmp.getDerValue(); 756 if (derVal.isContextSpecific((byte)1)) { 757 DerValue[] singleExtDer = derVal.data.getSequence(3); 758 singleExtensions = 759 new HashMap<String, java.security.cert.Extension> 760 (singleExtDer.length); 761 for (int i = 0; i < singleExtDer.length; i++) { 762 Extension ext = new Extension(singleExtDer[i]); 763 if (debug != null) { 764 debug.println("OCSP single extension: " + ext); 765 } 766 // We don't support any extensions yet. Therefore, if it 767 // is critical we must throw an exception because we 768 // don't know how to process it. 769 if (ext.isCritical()) { 770 throw new IOException( 771 "Unsupported OCSP critical extension: " + 772 ext.getExtensionId()); 773 } 774 singleExtensions.put(ext.getId(), ext); 775 } 776 } else { 777 singleExtensions = Collections.emptyMap(); 778 } 779 } else { 780 singleExtensions = Collections.emptyMap(); 781 } 782 } 783 784 /* 785 * Return the certificate's revocation status code 786 */ 787 @Override public CertStatus getCertStatus() { 788 return certStatus; 789 } 790 791 private CertId getCertId() { 792 return certId; 793 } 794 795 @Override public Date getRevocationTime() { 796 return (Date) revocationTime.clone(); 797 } 798 799 @Override public CRLReason getRevocationReason() { 800 return revocationReason; 801 } 802 803 @Override 804 public Map<String, java.security.cert.Extension> getSingleExtensions() { 805 return Collections.unmodifiableMap(singleExtensions); 806 } 807 808 /** 809 * Construct a string representation of a single OCSP response. 810 */ 811 @Override public String toString() { 812 StringBuilder sb = new StringBuilder(); 813 sb.append("SingleResponse: \n"); 814 sb.append(certId); 815 sb.append("\nCertStatus: "+ certStatus + "\n"); 816 if (certStatus == CertStatus.REVOKED) { 817 sb.append("revocationTime is " + revocationTime + "\n"); 818 sb.append("revocationReason is " + revocationReason + "\n"); 819 } 820 sb.append("thisUpdate is " + thisUpdate + "\n"); 821 if (nextUpdate != null) { 822 sb.append("nextUpdate is " + nextUpdate + "\n"); 823 } 824 return sb.toString(); 825 } 826 } 827 } 828