1/* 2 * Copyright (c) 2003, 2017, 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 26package sun.security.provider.certpath; 27 28import java.io.*; 29import java.security.*; 30import java.security.cert.CertificateException; 31import java.security.cert.CertificateParsingException; 32import java.security.cert.CertPathValidatorException; 33import java.security.cert.CertPathValidatorException.BasicReason; 34import java.security.cert.CRLReason; 35import java.security.cert.TrustAnchor; 36import java.security.cert.X509Certificate; 37import java.util.ArrayList; 38import java.util.Arrays; 39import java.util.Collections; 40import java.util.Date; 41import java.util.HashMap; 42import java.util.List; 43import java.util.Map; 44import java.util.Set; 45import javax.security.auth.x500.X500Principal; 46 47import sun.security.util.HexDumpEncoder; 48import sun.security.action.GetIntegerAction; 49import sun.security.x509.*; 50import sun.security.util.*; 51 52/** 53 * This class is used to process an OCSP response. 54 * The OCSP Response is defined 55 * in RFC 2560 and the ASN.1 encoding is as follows: 56 * <pre> 57 * 58 * OCSPResponse ::= SEQUENCE { 59 * responseStatus OCSPResponseStatus, 60 * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } 61 * 62 * OCSPResponseStatus ::= ENUMERATED { 63 * successful (0), --Response has valid confirmations 64 * malformedRequest (1), --Illegal confirmation request 65 * internalError (2), --Internal error in issuer 66 * tryLater (3), --Try again later 67 * --(4) is not used 68 * sigRequired (5), --Must sign the request 69 * unauthorized (6) --Request unauthorized 70 * } 71 * 72 * ResponseBytes ::= SEQUENCE { 73 * responseType OBJECT IDENTIFIER, 74 * response OCTET STRING } 75 * 76 * BasicOCSPResponse ::= SEQUENCE { 77 * tbsResponseData ResponseData, 78 * signatureAlgorithm AlgorithmIdentifier, 79 * signature BIT STRING, 80 * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } 81 * 82 * The value for signature SHALL be computed on the hash of the DER 83 * encoding ResponseData. 84 * 85 * ResponseData ::= SEQUENCE { 86 * version [0] EXPLICIT Version DEFAULT v1, 87 * responderID ResponderID, 88 * producedAt GeneralizedTime, 89 * responses SEQUENCE OF SingleResponse, 90 * responseExtensions [1] EXPLICIT Extensions OPTIONAL } 91 * 92 * ResponderID ::= CHOICE { 93 * byName [1] Name, 94 * byKey [2] KeyHash } 95 * 96 * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key 97 * (excluding the tag and length fields) 98 * 99 * SingleResponse ::= SEQUENCE { 100 * certID CertID, 101 * certStatus CertStatus, 102 * thisUpdate GeneralizedTime, 103 * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, 104 * singleExtensions [1] EXPLICIT Extensions OPTIONAL } 105 * 106 * CertStatus ::= CHOICE { 107 * good [0] IMPLICIT NULL, 108 * revoked [1] IMPLICIT RevokedInfo, 109 * unknown [2] IMPLICIT UnknownInfo } 110 * 111 * RevokedInfo ::= SEQUENCE { 112 * revocationTime GeneralizedTime, 113 * revocationReason [0] EXPLICIT CRLReason OPTIONAL } 114 * 115 * UnknownInfo ::= NULL -- this can be replaced with an enumeration 116 * 117 * </pre> 118 * 119 * @author Ram Marti 120 */ 121 122public final class OCSPResponse { 123 124 public enum ResponseStatus { 125 SUCCESSFUL, // Response has valid confirmations 126 MALFORMED_REQUEST, // Illegal request 127 INTERNAL_ERROR, // Internal error in responder 128 TRY_LATER, // Try again later 129 UNUSED, // is not used 130 SIG_REQUIRED, // Must sign the request 131 UNAUTHORIZED // Request unauthorized 132 }; 133 private static final ResponseStatus[] rsvalues = ResponseStatus.values(); 134 135 private static final Debug debug = Debug.getInstance("certpath"); 136 private static final boolean dump = debug != null && Debug.isOn("ocsp"); 137 private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID = 138 ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); 139 private static final int CERT_STATUS_GOOD = 0; 140 private static final int CERT_STATUS_REVOKED = 1; 141 private static final int CERT_STATUS_UNKNOWN = 2; 142 143 // ResponderID CHOICE tags 144 private static final int NAME_TAG = 1; 145 private static final int KEY_TAG = 2; 146 147 // Object identifier for the OCSPSigning key purpose 148 private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; 149 150 // Default maximum clock skew in milliseconds (15 minutes) 151 // allowed when checking validity of OCSP responses 152 private static final int DEFAULT_MAX_CLOCK_SKEW = 900000; 153 154 /** 155 * Integer value indicating the maximum allowable clock skew, 156 * in milliseconds, to be used for the OCSP check. 157 */ 158 private static final int MAX_CLOCK_SKEW = initializeClockSkew(); 159 160 /** 161 * Initialize the maximum allowable clock skew by getting the OCSP 162 * clock skew system property. If the property has not been set, or if its 163 * value is negative, set the skew to the default. 164 */ 165 private static int initializeClockSkew() { 166 Integer tmp = java.security.AccessController.doPrivileged( 167 new GetIntegerAction("com.sun.security.ocsp.clockSkew")); 168 if (tmp == null || tmp < 0) { 169 return DEFAULT_MAX_CLOCK_SKEW; 170 } 171 // Convert to milliseconds, as the system property will be 172 // specified in seconds 173 return tmp * 1000; 174 } 175 176 // an array of all of the CRLReasons (used in SingleResponse) 177 private static final CRLReason[] values = CRLReason.values(); 178 179 private final ResponseStatus responseStatus; 180 private final Map<CertId, SingleResponse> singleResponseMap; 181 private final AlgorithmId sigAlgId; 182 private final byte[] signature; 183 private final byte[] tbsResponseData; 184 private final byte[] responseNonce; 185 private List<X509CertImpl> certs; 186 private X509CertImpl signerCert = null; 187 private final ResponderId respId; 188 private Date producedAtDate = null; 189 private final Map<String, java.security.cert.Extension> responseExtensions; 190 191 /* 192 * Create an OCSP response from its ASN.1 DER encoding. 193 * 194 * @param bytes The DER-encoded bytes for an OCSP response 195 */ 196 public OCSPResponse(byte[] bytes) throws IOException { 197 if (dump) { 198 HexDumpEncoder hexEnc = new HexDumpEncoder(); 199 debug.println("OCSPResponse bytes...\n\n" + 200 hexEnc.encode(bytes) + "\n"); 201 } 202 DerValue der = new DerValue(bytes); 203 if (der.tag != DerValue.tag_Sequence) { 204 throw new IOException("Bad encoding in OCSP response: " + 205 "expected ASN.1 SEQUENCE tag."); 206 } 207 DerInputStream derIn = der.getData(); 208 209 // responseStatus 210 int status = derIn.getEnumerated(); 211 if (status >= 0 && status < rsvalues.length) { 212 responseStatus = rsvalues[status]; 213 } else { 214 // unspecified responseStatus 215 throw new IOException("Unknown OCSPResponse status: " + status); 216 } 217 if (debug != null) { 218 debug.println("OCSP response status: " + responseStatus); 219 } 220 if (responseStatus != ResponseStatus.SUCCESSFUL) { 221 // no need to continue, responseBytes are not set. 222 singleResponseMap = Collections.emptyMap(); 223 certs = new ArrayList<X509CertImpl>(); 224 sigAlgId = null; 225 signature = null; 226 tbsResponseData = null; 227 responseNonce = null; 228 responseExtensions = Collections.emptyMap(); 229 respId = null; 230 return; 231 } 232 233 // responseBytes 234 der = derIn.getDerValue(); 235 if (!der.isContextSpecific((byte)0)) { 236 throw new IOException("Bad encoding in responseBytes element " + 237 "of OCSP response: expected ASN.1 context specific tag 0."); 238 } 239 DerValue tmp = der.data.getDerValue(); 240 if (tmp.tag != DerValue.tag_Sequence) { 241 throw new IOException("Bad encoding in responseBytes element " + 242 "of OCSP response: expected ASN.1 SEQUENCE tag."); 243 } 244 245 // responseType 246 derIn = tmp.data; 247 ObjectIdentifier responseType = derIn.getOID(); 248 if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) { 249 if (debug != null) { 250 debug.println("OCSP response type: basic"); 251 } 252 } else { 253 if (debug != null) { 254 debug.println("OCSP response type: " + responseType); 255 } 256 throw new IOException("Unsupported OCSP response type: " + 257 responseType); 258 } 259 260 // BasicOCSPResponse 261 DerInputStream basicOCSPResponse = 262 new DerInputStream(derIn.getOctetString()); 263 264 DerValue[] seqTmp = basicOCSPResponse.getSequence(2); 265 if (seqTmp.length < 3) { 266 throw new IOException("Unexpected BasicOCSPResponse value"); 267 } 268 269 DerValue responseData = seqTmp[0]; 270 271 // Need the DER encoded ResponseData to verify the signature later 272 tbsResponseData = seqTmp[0].toByteArray(); 273 274 // tbsResponseData 275 if (responseData.tag != DerValue.tag_Sequence) { 276 throw new IOException("Bad encoding in tbsResponseData " + 277 "element of OCSP response: expected ASN.1 SEQUENCE tag."); 278 } 279 DerInputStream seqDerIn = responseData.data; 280 DerValue seq = seqDerIn.getDerValue(); 281 282 // version 283 if (seq.isContextSpecific((byte)0)) { 284 // seq[0] is version 285 if (seq.isConstructed() && seq.isContextSpecific()) { 286 //System.out.println ("version is available"); 287 seq = seq.data.getDerValue(); 288 int version = seq.getInteger(); 289 if (seq.data.available() != 0) { 290 throw new IOException("Bad encoding in version " + 291 " element of OCSP response: bad format"); 292 } 293 seq = seqDerIn.getDerValue(); 294 } 295 } 296 297 // responderID 298 respId = new ResponderId(seq.toByteArray()); 299 if (debug != null) { 300 debug.println("Responder ID: " + respId); 301 } 302 303 // producedAt 304 seq = seqDerIn.getDerValue(); 305 producedAtDate = seq.getGeneralizedTime(); 306 if (debug != null) { 307 debug.println("OCSP response produced at: " + producedAtDate); 308 } 309 310 // responses 311 DerValue[] singleResponseDer = seqDerIn.getSequence(1); 312 singleResponseMap = new HashMap<>(singleResponseDer.length); 313 if (debug != null) { 314 debug.println("OCSP number of SingleResponses: " 315 + singleResponseDer.length); 316 } 317 for (DerValue srDer : singleResponseDer) { 318 SingleResponse singleResponse = new SingleResponse(srDer); 319 singleResponseMap.put(singleResponse.getCertId(), singleResponse); 320 } 321 322 // responseExtensions 323 Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>(); 324 if (seqDerIn.available() > 0) { 325 seq = seqDerIn.getDerValue(); 326 if (seq.isContextSpecific((byte)1)) { 327 tmpExtMap = parseExtensions(seq); 328 } 329 } 330 responseExtensions = tmpExtMap; 331 332 // Attach the nonce value if found in the extension map 333 Extension nonceExt = (Extension)tmpExtMap.get( 334 PKIXExtensions.OCSPNonce_Id.toString()); 335 responseNonce = (nonceExt != null) ? 336 nonceExt.getExtensionValue() : null; 337 if (debug != null && responseNonce != null) { 338 debug.println("Response nonce: " + Arrays.toString(responseNonce)); 339 } 340 341 // signatureAlgorithmId 342 sigAlgId = AlgorithmId.parse(seqTmp[1]); 343 344 // signature 345 signature = seqTmp[2].getBitString(); 346 347 // if seq[3] is available , then it is a sequence of certificates 348 if (seqTmp.length > 3) { 349 // certs are available 350 DerValue seqCert = seqTmp[3]; 351 if (!seqCert.isContextSpecific((byte)0)) { 352 throw new IOException("Bad encoding in certs element of " + 353 "OCSP response: expected ASN.1 context specific tag 0."); 354 } 355 DerValue[] derCerts = seqCert.getData().getSequence(3); 356 certs = new ArrayList<X509CertImpl>(derCerts.length); 357 try { 358 for (int i = 0; i < derCerts.length; i++) { 359 X509CertImpl cert = 360 new X509CertImpl(derCerts[i].toByteArray()); 361 certs.add(cert); 362 363 if (debug != null) { 364 debug.println("OCSP response cert #" + (i + 1) + ": " + 365 cert.getSubjectX500Principal()); 366 } 367 } 368 } catch (CertificateException ce) { 369 throw new IOException("Bad encoding in X509 Certificate", ce); 370 } 371 } else { 372 certs = new ArrayList<X509CertImpl>(); 373 } 374 } 375 376 void verify(List<CertId> certIds, IssuerInfo issuerInfo, 377 X509Certificate responderCert, Date date, byte[] nonce, 378 String variant) 379 throws CertPathValidatorException 380 { 381 switch (responseStatus) { 382 case SUCCESSFUL: 383 break; 384 case TRY_LATER: 385 case INTERNAL_ERROR: 386 throw new CertPathValidatorException( 387 "OCSP response error: " + responseStatus, null, null, -1, 388 BasicReason.UNDETERMINED_REVOCATION_STATUS); 389 case UNAUTHORIZED: 390 default: 391 throw new CertPathValidatorException("OCSP response error: " + 392 responseStatus); 393 } 394 395 // Check that the response includes a response for all of the 396 // certs that were supplied in the request 397 for (CertId certId : certIds) { 398 SingleResponse sr = getSingleResponse(certId); 399 if (sr == null) { 400 if (debug != null) { 401 debug.println("No response found for CertId: " + certId); 402 } 403 throw new CertPathValidatorException( 404 "OCSP response does not include a response for a " + 405 "certificate supplied in the OCSP request"); 406 } 407 if (debug != null) { 408 debug.println("Status of certificate (with serial number " + 409 certId.getSerialNumber() + ") is: " + sr.getCertStatus()); 410 } 411 } 412 413 // Locate the signer cert 414 if (signerCert == null) { 415 // Add the Issuing CA cert and/or Trusted Responder cert to the list 416 // of certs from the OCSP response 417 try { 418 if (issuerInfo.getCertificate() != null) { 419 certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate())); 420 } 421 if (responderCert != null) { 422 certs.add(X509CertImpl.toImpl(responderCert)); 423 } 424 } catch (CertificateException ce) { 425 throw new CertPathValidatorException( 426 "Invalid issuer or trusted responder certificate", ce); 427 } 428 429 if (respId.getType() == ResponderId.Type.BY_NAME) { 430 X500Principal rName = respId.getResponderName(); 431 for (X509CertImpl cert : certs) { 432 if (cert.getSubjectX500Principal().equals(rName)) { 433 signerCert = cert; 434 break; 435 } 436 } 437 } else if (respId.getType() == ResponderId.Type.BY_KEY) { 438 KeyIdentifier ridKeyId = respId.getKeyIdentifier(); 439 for (X509CertImpl cert : certs) { 440 // Match responder's key identifier against the cert's SKID 441 // This will match if the SKID is encoded using the 160-bit 442 // SHA-1 hash method as defined in RFC 5280. 443 KeyIdentifier certKeyId = cert.getSubjectKeyId(); 444 if (certKeyId != null && ridKeyId.equals(certKeyId)) { 445 signerCert = cert; 446 break; 447 } else { 448 // The certificate does not have a SKID or may have 449 // been using a different algorithm (ex: see RFC 7093). 450 // Check if the responder's key identifier matches 451 // against a newly generated key identifier of the 452 // cert's public key using the 160-bit SHA-1 method. 453 try { 454 certKeyId = new KeyIdentifier(cert.getPublicKey()); 455 } catch (IOException e) { 456 // ignore 457 } 458 if (ridKeyId.equals(certKeyId)) { 459 signerCert = cert; 460 break; 461 } 462 } 463 } 464 } 465 } 466 467 // Check whether the signer cert returned by the responder is trusted 468 if (signerCert != null) { 469 // Check if the response is signed by the issuing CA 470 if (signerCert.getSubjectX500Principal().equals( 471 issuerInfo.getName()) && 472 signerCert.getPublicKey().equals( 473 issuerInfo.getPublicKey())) { 474 if (debug != null) { 475 debug.println("OCSP response is signed by the target's " + 476 "Issuing CA"); 477 } 478 // cert is trusted, now verify the signed response 479 480 // Check if the response is signed by a trusted responder 481 } else if (signerCert.equals(responderCert)) { 482 if (debug != null) { 483 debug.println("OCSP response is signed by a Trusted " + 484 "Responder"); 485 } 486 // cert is trusted, now verify the signed response 487 488 // Check if the response is signed by an authorized responder 489 } else if (signerCert.getIssuerX500Principal().equals( 490 issuerInfo.getName())) { 491 492 // Check for the OCSPSigning key purpose 493 try { 494 List<String> keyPurposes = signerCert.getExtendedKeyUsage(); 495 if (keyPurposes == null || 496 !keyPurposes.contains(KP_OCSP_SIGNING_OID)) { 497 throw new CertPathValidatorException( 498 "Responder's certificate not valid for signing " + 499 "OCSP responses"); 500 } 501 } catch (CertificateParsingException cpe) { 502 // assume cert is not valid for signing 503 throw new CertPathValidatorException( 504 "Responder's certificate not valid for signing " + 505 "OCSP responses", cpe); 506 } 507 508 // Check algorithm constraints specified in security property 509 // "jdk.certpath.disabledAlgorithms". 510 AlgorithmChecker algChecker = 511 new AlgorithmChecker(issuerInfo.getAnchor(), date, 512 variant); 513 algChecker.init(false); 514 algChecker.check(signerCert, Collections.<String>emptySet()); 515 516 // check the validity 517 try { 518 if (date == null) { 519 signerCert.checkValidity(); 520 } else { 521 signerCert.checkValidity(date); 522 } 523 } catch (CertificateException e) { 524 throw new CertPathValidatorException( 525 "Responder's certificate not within the " + 526 "validity period", e); 527 } 528 529 // check for revocation 530 // 531 // A CA may specify that an OCSP client can trust a 532 // responder for the lifetime of the responder's 533 // certificate. The CA does so by including the 534 // extension id-pkix-ocsp-nocheck. 535 // 536 Extension noCheck = 537 signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id); 538 if (noCheck != null) { 539 if (debug != null) { 540 debug.println("Responder's certificate includes " + 541 "the extension id-pkix-ocsp-nocheck."); 542 } 543 } else { 544 // we should do the revocation checking of the 545 // authorized responder in a future update. 546 } 547 548 // verify the signature 549 try { 550 signerCert.verify(issuerInfo.getPublicKey()); 551 if (debug != null) { 552 debug.println("OCSP response is signed by an " + 553 "Authorized Responder"); 554 } 555 // cert is trusted, now verify the signed response 556 557 } catch (GeneralSecurityException e) { 558 signerCert = null; 559 } 560 } else { 561 throw new CertPathValidatorException( 562 "Responder's certificate is not authorized to sign " + 563 "OCSP responses"); 564 } 565 } 566 567 // Confirm that the signed response was generated using the public 568 // key from the trusted responder cert 569 if (signerCert != null) { 570 // Check algorithm constraints specified in security property 571 // "jdk.certpath.disabledAlgorithms". 572 AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant); 573 574 if (!verifySignature(signerCert)) { 575 throw new CertPathValidatorException( 576 "Error verifying OCSP Response's signature"); 577 } 578 } else { 579 // Need responder's cert in order to verify the signature 580 throw new CertPathValidatorException( 581 "Unable to verify OCSP Response's signature"); 582 } 583 584 if (nonce != null) { 585 if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) { 586 throw new CertPathValidatorException("Nonces don't match"); 587 } 588 } 589 590 // Check freshness of OCSPResponse 591 long now = (date == null) ? System.currentTimeMillis() : date.getTime(); 592 Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW); 593 Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW); 594 for (SingleResponse sr : singleResponseMap.values()) { 595 if (debug != null) { 596 String until = ""; 597 if (sr.nextUpdate != null) { 598 until = " until " + sr.nextUpdate; 599 } 600 debug.println("OCSP response validity interval is from " + 601 sr.thisUpdate + until); 602 debug.println("Checking validity of OCSP response on: " + 603 new Date(now)); 604 } 605 606 // Check that the test date is within the validity interval: 607 // [ thisUpdate - MAX_CLOCK_SKEW, 608 // MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ] 609 if (nowPlusSkew.before(sr.thisUpdate) || 610 nowMinusSkew.after( 611 sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate)) 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 * @return the {@code ResponseStatus} for this OCSP response 624 */ 625 public ResponseStatus getResponseStatus() { 626 return responseStatus; 627 } 628 629 /* 630 * Verify the signature of the OCSP response. 631 */ 632 private boolean verifySignature(X509Certificate cert) 633 throws CertPathValidatorException { 634 635 try { 636 Signature respSignature = Signature.getInstance(sigAlgId.getName()); 637 respSignature.initVerify(cert.getPublicKey()); 638 respSignature.update(tbsResponseData); 639 640 if (respSignature.verify(signature)) { 641 if (debug != null) { 642 debug.println("Verified signature of OCSP Response"); 643 } 644 return true; 645 646 } else { 647 if (debug != null) { 648 debug.println( 649 "Error verifying signature of OCSP Response"); 650 } 651 return false; 652 } 653 } catch (InvalidKeyException | NoSuchAlgorithmException | 654 SignatureException e) 655 { 656 throw new CertPathValidatorException(e); 657 } 658 } 659 660 /** 661 * Returns the SingleResponse of the specified CertId, or null if 662 * there is no response for that CertId. 663 * 664 * @param certId the {@code CertId} for a {@code SingleResponse} to be 665 * searched for in the OCSP response. 666 * 667 * @return the {@code SingleResponse} for the provided {@code CertId}, 668 * or {@code null} if it is not found. 669 */ 670 public SingleResponse getSingleResponse(CertId certId) { 671 return singleResponseMap.get(certId); 672 } 673 674 /** 675 * Return a set of all CertIds in this {@code OCSPResponse} 676 * 677 * @return an unmodifiable set containing every {@code CertId} in this 678 * response. 679 */ 680 public Set<CertId> getCertIds() { 681 return Collections.unmodifiableSet(singleResponseMap.keySet()); 682 } 683 684 /* 685 * Returns the certificate for the authority that signed the OCSP response. 686 */ 687 X509Certificate getSignerCertificate() { 688 return signerCert; // set in verify() 689 } 690 691 /** 692 * Get the {@code ResponderId} from this {@code OCSPResponse} 693 * 694 * @return the {@code ResponderId} from this response or {@code null} 695 * if no responder ID is in the body of the response (e.g. a 696 * response with a status other than SUCCESS. 697 */ 698 public ResponderId getResponderId() { 699 return respId; 700 } 701 702 /** 703 * Provide a String representation of an OCSPResponse 704 * 705 * @return a human-readable representation of the OCSPResponse 706 */ 707 @Override 708 public String toString() { 709 StringBuilder sb = new StringBuilder(); 710 sb.append("OCSP Response:\n"); 711 sb.append("Response Status: ").append(responseStatus).append("\n"); 712 sb.append("Responder ID: ").append(respId).append("\n"); 713 sb.append("Produced at: ").append(producedAtDate).append("\n"); 714 int count = singleResponseMap.size(); 715 sb.append(count).append(count == 1 ? 716 " response:\n" : " responses:\n"); 717 for (SingleResponse sr : singleResponseMap.values()) { 718 sb.append(sr).append("\n"); 719 } 720 if (responseExtensions != null && responseExtensions.size() > 0) { 721 count = responseExtensions.size(); 722 sb.append(count).append(count == 1 ? 723 " extension:\n" : " extensions:\n"); 724 for (String extId : responseExtensions.keySet()) { 725 sb.append(responseExtensions.get(extId)).append("\n"); 726 } 727 } 728 729 return sb.toString(); 730 } 731 732 /** 733 * Build a String-Extension map from DER encoded data. 734 * @param derVal A {@code DerValue} object built from a SEQUENCE of 735 * extensions 736 * 737 * @return a {@code Map} using the OID in string form as the keys. If no 738 * extensions are found or an empty SEQUENCE is passed in, then 739 * an empty {@code Map} will be returned. 740 * 741 * @throws IOException if any decoding errors occur. 742 */ 743 private static Map<String, java.security.cert.Extension> 744 parseExtensions(DerValue derVal) throws IOException { 745 DerValue[] extDer = derVal.data.getSequence(3); 746 Map<String, java.security.cert.Extension> extMap = 747 new HashMap<>(extDer.length); 748 749 for (DerValue extDerVal : extDer) { 750 Extension ext = new Extension(extDerVal); 751 if (debug != null) { 752 debug.println("Extension: " + ext); 753 } 754 // We don't support any extensions yet. Therefore, if it 755 // is critical we must throw an exception because we 756 // don't know how to process it. 757 if (ext.isCritical()) { 758 throw new IOException("Unsupported OCSP critical extension: " + 759 ext.getExtensionId()); 760 } 761 extMap.put(ext.getId(), ext); 762 } 763 764 return extMap; 765 } 766 767 /* 768 * A class representing a single OCSP response. 769 */ 770 public static final class SingleResponse implements OCSP.RevocationStatus { 771 private final CertId certId; 772 private final CertStatus certStatus; 773 private final Date thisUpdate; 774 private final Date nextUpdate; 775 private final Date revocationTime; 776 private final CRLReason revocationReason; 777 private final Map<String, java.security.cert.Extension> singleExtensions; 778 779 private SingleResponse(DerValue der) throws IOException { 780 if (der.tag != DerValue.tag_Sequence) { 781 throw new IOException("Bad ASN.1 encoding in SingleResponse"); 782 } 783 DerInputStream tmp = der.data; 784 785 certId = new CertId(tmp.getDerValue().data); 786 DerValue derVal = tmp.getDerValue(); 787 short tag = (byte)(derVal.tag & 0x1f); 788 if (tag == CERT_STATUS_REVOKED) { 789 certStatus = CertStatus.REVOKED; 790 revocationTime = derVal.data.getGeneralizedTime(); 791 if (derVal.data.available() != 0) { 792 DerValue dv = derVal.data.getDerValue(); 793 tag = (byte)(dv.tag & 0x1f); 794 if (tag == 0) { 795 int reason = dv.data.getEnumerated(); 796 // if reason out-of-range just leave as UNSPECIFIED 797 if (reason >= 0 && reason < values.length) { 798 revocationReason = values[reason]; 799 } else { 800 revocationReason = CRLReason.UNSPECIFIED; 801 } 802 } else { 803 revocationReason = CRLReason.UNSPECIFIED; 804 } 805 } else { 806 revocationReason = CRLReason.UNSPECIFIED; 807 } 808 // RevokedInfo 809 if (debug != null) { 810 debug.println("Revocation time: " + revocationTime); 811 debug.println("Revocation reason: " + revocationReason); 812 } 813 } else { 814 revocationTime = null; 815 revocationReason = null; 816 if (tag == CERT_STATUS_GOOD) { 817 certStatus = CertStatus.GOOD; 818 } else if (tag == CERT_STATUS_UNKNOWN) { 819 certStatus = CertStatus.UNKNOWN; 820 } else { 821 throw new IOException("Invalid certificate status"); 822 } 823 } 824 825 thisUpdate = tmp.getGeneralizedTime(); 826 if (debug != null) { 827 debug.println("thisUpdate: " + thisUpdate); 828 } 829 830 // Parse optional fields like nextUpdate and singleExtensions 831 Date tmpNextUpdate = null; 832 Map<String, java.security.cert.Extension> tmpMap = null; 833 834 // Check for the first optional item, it could be nextUpdate 835 // [CONTEXT 0] or singleExtensions [CONTEXT 1] 836 if (tmp.available() > 0) { 837 derVal = tmp.getDerValue(); 838 839 // nextUpdate processing 840 if (derVal.isContextSpecific((byte)0)) { 841 tmpNextUpdate = derVal.data.getGeneralizedTime(); 842 if (debug != null) { 843 debug.println("nextUpdate: " + tmpNextUpdate); 844 } 845 846 // If more data exists in the singleResponse, it 847 // can only be singleExtensions. Get this DER value 848 // for processing in the next block 849 derVal = tmp.available() > 0 ? tmp.getDerValue() : null; 850 } 851 852 // singleExtensions processing 853 if (derVal != null) { 854 if (derVal.isContextSpecific((byte)1)) { 855 tmpMap = parseExtensions(derVal); 856 857 // There should not be any other items in the 858 // singleResponse at this point. 859 if (tmp.available() > 0) { 860 throw new IOException(tmp.available() + 861 " bytes of additional data in singleResponse"); 862 } 863 } else { 864 // Unknown item in the singleResponse 865 throw new IOException("Unsupported singleResponse " + 866 "item, tag = " + String.format("%02X", derVal.tag)); 867 } 868 } 869 } 870 871 nextUpdate = tmpNextUpdate; 872 singleExtensions = (tmpMap != null) ? tmpMap : 873 Collections.emptyMap(); 874 if (debug != null) { 875 for (java.security.cert.Extension ext : 876 singleExtensions.values()) { 877 debug.println("singleExtension: " + ext); 878 } 879 } 880 } 881 882 /* 883 * Return the certificate's revocation status code 884 */ 885 @Override 886 public CertStatus getCertStatus() { 887 return certStatus; 888 } 889 890 /** 891 * Get the Cert ID that this SingleResponse is for. 892 * 893 * @return the {@code CertId} for this {@code SingleResponse} 894 */ 895 public CertId getCertId() { 896 return certId; 897 } 898 899 /** 900 * Get the {@code thisUpdate} field from this {@code SingleResponse}. 901 * 902 * @return a {@link Date} object containing the thisUpdate date 903 */ 904 public Date getThisUpdate() { 905 return (thisUpdate != null ? (Date) thisUpdate.clone() : null); 906 } 907 908 /** 909 * Get the {@code nextUpdate} field from this {@code SingleResponse}. 910 * 911 * @return a {@link Date} object containing the nexUpdate date or 912 * {@code null} if a nextUpdate field is not present in the response. 913 */ 914 public Date getNextUpdate() { 915 return (nextUpdate != null ? (Date) nextUpdate.clone() : null); 916 } 917 918 /** 919 * Get the {@code revocationTime} field from this 920 * {@code SingleResponse}. 921 * 922 * @return a {@link Date} object containing the revocationTime date or 923 * {@code null} if the {@code SingleResponse} does not have a status 924 * of {@code REVOKED}. 925 */ 926 @Override 927 public Date getRevocationTime() { 928 return (revocationTime != null ? (Date) revocationTime.clone() : 929 null); 930 } 931 932 /** 933 * Get the {@code revocationReason} field for the 934 * {@code SingleResponse}. 935 * 936 * @return a {@link CRLReason} containing the revocation reason, or 937 * {@code null} if a revocation reason was not provided or the 938 * response status is not {@code REVOKED}. 939 */ 940 @Override 941 public CRLReason getRevocationReason() { 942 return revocationReason; 943 } 944 945 /** 946 * Get the {@code singleExtensions} for this {@code SingleResponse}. 947 * 948 * @return a {@link Map} of {@link Extension} objects, keyed by 949 * their OID value in string form. 950 */ 951 @Override 952 public Map<String, java.security.cert.Extension> getSingleExtensions() { 953 return Collections.unmodifiableMap(singleExtensions); 954 } 955 956 /** 957 * Construct a string representation of a single OCSP response. 958 */ 959 @Override public String toString() { 960 StringBuilder sb = new StringBuilder(); 961 sb.append("SingleResponse:\n"); 962 sb.append(certId); 963 sb.append("\nCertStatus: ").append(certStatus).append("\n"); 964 if (certStatus == CertStatus.REVOKED) { 965 sb.append("revocationTime is "); 966 sb.append(revocationTime).append("\n"); 967 sb.append("revocationReason is "); 968 sb.append(revocationReason).append("\n"); 969 } 970 sb.append("thisUpdate is ").append(thisUpdate).append("\n"); 971 if (nextUpdate != null) { 972 sb.append("nextUpdate is ").append(nextUpdate).append("\n"); 973 } 974 for (java.security.cert.Extension ext : singleExtensions.values()) { 975 sb.append("singleExtension: "); 976 sb.append(ext.toString()).append("\n"); 977 } 978 return sb.toString(); 979 } 980 } 981 982 /** 983 * Helper class that allows consumers to pass in issuer information. This 984 * will always consist of the issuer's name and public key, but may also 985 * contain a certificate if the originating data is in that form. The 986 * trust anchor for the certificate chain will be included for certpath 987 * disabled algorithm checking. 988 */ 989 static final class IssuerInfo { 990 private final TrustAnchor anchor; 991 private final X509Certificate certificate; 992 private final X500Principal name; 993 private final PublicKey pubKey; 994 995 IssuerInfo(TrustAnchor anchor) { 996 this(anchor, (anchor != null) ? anchor.getTrustedCert() : null); 997 } 998 999 IssuerInfo(X509Certificate issuerCert) { 1000 this(null, issuerCert); 1001 } 1002 1003 IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) { 1004 if (anchor == null && issuerCert == null) { 1005 throw new NullPointerException("TrustAnchor and issuerCert " + 1006 "cannot be null"); 1007 } 1008 this.anchor = anchor; 1009 if (issuerCert != null) { 1010 name = issuerCert.getSubjectX500Principal(); 1011 pubKey = issuerCert.getPublicKey(); 1012 certificate = issuerCert; 1013 } else { 1014 name = anchor.getCA(); 1015 pubKey = anchor.getCAPublicKey(); 1016 certificate = anchor.getTrustedCert(); 1017 } 1018 } 1019 1020 /** 1021 * Get the certificate in this IssuerInfo if present. 1022 * 1023 * @return the {@code X509Certificate} used to create this IssuerInfo 1024 * object, or {@code null} if a certificate was not used in its 1025 * creation. 1026 */ 1027 X509Certificate getCertificate() { 1028 return certificate; 1029 } 1030 1031 /** 1032 * Get the name of this issuer. 1033 * 1034 * @return an {@code X500Principal} corresponding to this issuer's 1035 * name. If derived from an issuer's {@code X509Certificate} this 1036 * would be equivalent to the certificate subject name. 1037 */ 1038 X500Principal getName() { 1039 return name; 1040 } 1041 1042 /** 1043 * Get the public key for this issuer. 1044 * 1045 * @return a {@code PublicKey} for this issuer. 1046 */ 1047 PublicKey getPublicKey() { 1048 return pubKey; 1049 } 1050 1051 /** 1052 * Get the TrustAnchor for the certificate chain. 1053 * 1054 * @return a {@code TrustAnchor}. 1055 */ 1056 TrustAnchor getAnchor() { 1057 return anchor; 1058 } 1059 1060 /** 1061 * Create a string representation of this IssuerInfo. 1062 * 1063 * @return a {@code String} form of this IssuerInfo object. 1064 */ 1065 @Override 1066 public String toString() { 1067 StringBuilder sb = new StringBuilder(); 1068 sb.append("Issuer Info:\n"); 1069 sb.append("Name: ").append(name.toString()).append("\n"); 1070 sb.append("Public Key:\n").append(pubKey.toString()).append("\n"); 1071 return sb.toString(); 1072 } 1073 } 1074} 1075