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