1 /* 2 * Copyright (c) 2003, 2015, 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, 155 * in milliseconds, 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 */ initializeClockSkew()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 */ OCSPResponse(byte[] bytes)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 verify(List<CertId> certIds, X509Certificate issuerCert, X509Certificate responderCert, Date date, byte[] nonce)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 if (nonce != null) { 590 if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) { 591 throw new CertPathValidatorException("Nonces don't match"); 592 } 593 } 594 595 // Check freshness of OCSPResponse 596 597 long now = (date == null) ? System.currentTimeMillis() : date.getTime(); 598 Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW); 599 Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW); 600 for (SingleResponse sr : singleResponseMap.values()) { 601 if (debug != null) { 602 String until = ""; 603 if (sr.nextUpdate != null) { 604 until = " until " + sr.nextUpdate; 605 } 606 debug.println("OCSP response validity interval is from " + 607 sr.thisUpdate + until); 608 debug.println("Checking validity of OCSP response on: " + 609 new Date(now)); 610 } 611 612 // Check that the test date is within the validity interval: 613 // [ thisUpdate - MAX_CLOCK_SKEW, 614 // MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ] 615 if (nowPlusSkew.before(sr.thisUpdate) || 616 nowMinusSkew.after( 617 sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate)) 618 { 619 throw new CertPathValidatorException( 620 "Response is unreliable: its validity " + 621 "interval is out-of-date"); 622 } 623 } 624 } 625 626 /** 627 * Returns the OCSP ResponseStatus. 628 */ getResponseStatus()629 ResponseStatus getResponseStatus() { 630 return responseStatus; 631 } 632 633 /* 634 * Verify the signature of the OCSP response. 635 */ verifySignature(X509Certificate cert)636 private boolean verifySignature(X509Certificate cert) 637 throws CertPathValidatorException { 638 639 try { 640 Signature respSignature = Signature.getInstance(sigAlgId.getName()); 641 respSignature.initVerify(cert.getPublicKey()); 642 respSignature.update(tbsResponseData); 643 644 if (respSignature.verify(signature)) { 645 if (debug != null) { 646 debug.println("Verified signature of OCSP Response"); 647 } 648 return true; 649 650 } else { 651 if (debug != null) { 652 debug.println( 653 "Error verifying signature of OCSP Response"); 654 } 655 return false; 656 } 657 } catch (InvalidKeyException | NoSuchAlgorithmException | 658 SignatureException e) 659 { 660 throw new CertPathValidatorException(e); 661 } 662 } 663 664 /** 665 * Returns the SingleResponse of the specified CertId, or null if 666 * there is no response for that CertId. 667 */ getSingleResponse(CertId certId)668 SingleResponse getSingleResponse(CertId certId) { 669 return singleResponseMap.get(certId); 670 } 671 672 /* 673 * Returns the certificate for the authority that signed the OCSP response. 674 */ getSignerCertificate()675 X509Certificate getSignerCertificate() { 676 return signerCert; // set in verify() 677 } 678 679 /* 680 * A class representing a single OCSP response. 681 */ 682 final static class SingleResponse implements OCSP.RevocationStatus { 683 private final CertId certId; 684 private final CertStatus certStatus; 685 private final Date thisUpdate; 686 private final Date nextUpdate; 687 private final Date revocationTime; 688 private final CRLReason revocationReason; 689 private final Map<String, java.security.cert.Extension> singleExtensions; 690 SingleResponse(DerValue der)691 private SingleResponse(DerValue der) throws IOException { 692 if (der.tag != DerValue.tag_Sequence) { 693 throw new IOException("Bad ASN.1 encoding in SingleResponse"); 694 } 695 DerInputStream tmp = der.data; 696 697 certId = new CertId(tmp.getDerValue().data); 698 DerValue derVal = tmp.getDerValue(); 699 short tag = (byte)(derVal.tag & 0x1f); 700 if (tag == CERT_STATUS_REVOKED) { 701 certStatus = CertStatus.REVOKED; 702 revocationTime = derVal.data.getGeneralizedTime(); 703 if (derVal.data.available() != 0) { 704 DerValue dv = derVal.data.getDerValue(); 705 tag = (byte)(dv.tag & 0x1f); 706 if (tag == 0) { 707 int reason = dv.data.getEnumerated(); 708 // if reason out-of-range just leave as UNSPECIFIED 709 if (reason >= 0 && reason < values.length) { 710 revocationReason = values[reason]; 711 } else { 712 revocationReason = CRLReason.UNSPECIFIED; 713 } 714 } else { 715 revocationReason = CRLReason.UNSPECIFIED; 716 } 717 } else { 718 revocationReason = CRLReason.UNSPECIFIED; 719 } 720 // RevokedInfo 721 if (debug != null) { 722 debug.println("Revocation time: " + revocationTime); 723 debug.println("Revocation reason: " + revocationReason); 724 } 725 } else { 726 revocationTime = null; 727 revocationReason = CRLReason.UNSPECIFIED; 728 if (tag == CERT_STATUS_GOOD) { 729 certStatus = CertStatus.GOOD; 730 } else if (tag == CERT_STATUS_UNKNOWN) { 731 certStatus = CertStatus.UNKNOWN; 732 } else { 733 throw new IOException("Invalid certificate status"); 734 } 735 } 736 737 thisUpdate = tmp.getGeneralizedTime(); 738 739 if (tmp.available() == 0) { 740 // we are done 741 nextUpdate = null; 742 } else { 743 derVal = tmp.getDerValue(); 744 tag = (byte)(derVal.tag & 0x1f); 745 if (tag == 0) { 746 // next update 747 nextUpdate = derVal.data.getGeneralizedTime(); 748 749 if (tmp.available() == 0) { 750 // we are done 751 } else { 752 derVal = tmp.getDerValue(); 753 tag = (byte)(derVal.tag & 0x1f); 754 } 755 } else { 756 nextUpdate = null; 757 } 758 } 759 // singleExtensions 760 if (tmp.available() > 0) { 761 derVal = tmp.getDerValue(); 762 if (derVal.isContextSpecific((byte)1)) { 763 DerValue[] singleExtDer = derVal.data.getSequence(3); 764 singleExtensions = 765 new HashMap<String, java.security.cert.Extension> 766 (singleExtDer.length); 767 for (int i = 0; i < singleExtDer.length; i++) { 768 Extension ext = new Extension(singleExtDer[i]); 769 if (debug != null) { 770 debug.println("OCSP single extension: " + ext); 771 } 772 // We don't support any extensions yet. Therefore, if it 773 // is critical we must throw an exception because we 774 // don't know how to process it. 775 if (ext.isCritical()) { 776 throw new IOException( 777 "Unsupported OCSP critical extension: " + 778 ext.getExtensionId()); 779 } 780 singleExtensions.put(ext.getId(), ext); 781 } 782 } else { 783 singleExtensions = Collections.emptyMap(); 784 } 785 } else { 786 singleExtensions = Collections.emptyMap(); 787 } 788 } 789 790 /* 791 * Return the certificate's revocation status code 792 */ getCertStatus()793 @Override public CertStatus getCertStatus() { 794 return certStatus; 795 } 796 getCertId()797 private CertId getCertId() { 798 return certId; 799 } 800 getRevocationTime()801 @Override public Date getRevocationTime() { 802 return (Date) revocationTime.clone(); 803 } 804 getRevocationReason()805 @Override public CRLReason getRevocationReason() { 806 return revocationReason; 807 } 808 809 @Override getSingleExtensions()810 public Map<String, java.security.cert.Extension> getSingleExtensions() { 811 return Collections.unmodifiableMap(singleExtensions); 812 } 813 814 /** 815 * Construct a string representation of a single OCSP response. 816 */ toString()817 @Override public String toString() { 818 StringBuilder sb = new StringBuilder(); 819 sb.append("SingleResponse: \n"); 820 sb.append(certId); 821 sb.append("\nCertStatus: "+ certStatus + "\n"); 822 if (certStatus == CertStatus.REVOKED) { 823 sb.append("revocationTime is " + revocationTime + "\n"); 824 sb.append("revocationReason is " + revocationReason + "\n"); 825 } 826 sb.append("thisUpdate is " + thisUpdate + "\n"); 827 if (nextUpdate != null) { 828 sb.append("nextUpdate is " + nextUpdate + "\n"); 829 } 830 return sb.toString(); 831 } 832 } 833 } 834