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 = new AlgorithmChecker( 511 new TrustAnchor(issuerInfo.getName(), 512 issuerInfo.getPublicKey(), null)); 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); 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. 986 */ 987 static final class IssuerInfo { 988 private final X509Certificate certificate; 989 private final X500Principal name; 990 private final PublicKey pubKey; 991 992 IssuerInfo(X509Certificate issuerCert) { 993 certificate = Objects.requireNonNull(issuerCert, 994 "Constructor requires non-null certificate"); 995 name = certificate.getSubjectX500Principal(); 996 pubKey = certificate.getPublicKey(); 997 } 998 999 IssuerInfo(X500Principal subjectName, PublicKey key) { 1000 certificate = null; 1001 name = Objects.requireNonNull(subjectName, 1002 "Constructor requires non-null subject"); 1003 pubKey = Objects.requireNonNull(key, 1004 "Constructor requires non-null public key"); 1005 } 1006 1007 IssuerInfo(TrustAnchor anchor) { 1008 certificate = anchor.getTrustedCert(); 1009 if (certificate != null) { 1010 name = certificate.getSubjectX500Principal(); 1011 pubKey = certificate.getPublicKey(); 1012 } else { 1013 name = anchor.getCA(); 1014 pubKey = anchor.getCAPublicKey(); 1015 } 1016 } 1017 1018 /** 1019 * Get the certificate in this IssuerInfo if present. 1020 * 1021 * @return the {@code X509Certificate} used to create this IssuerInfo 1022 * object, or {@code null} if a certificate was not used in its 1023 * creation. 1024 */ 1025 X509Certificate getCertificate() { 1026 return certificate; 1027 } 1028 1029 /** 1030 * Get the name of this issuer. 1031 * 1032 * @return an {@code X500Principal} corresponding to this issuer's 1033 * name. If derived from an issuer's {@code X509Certificate} this 1034 * would be equivalent to the certificate subject name. 1035 */ 1036 X500Principal getName() { 1037 return name; 1038 } 1039 1040 /** 1041 * Get the public key for this issuer. 1042 * 1043 * @return a {@code PublicKey} for this issuer. 1044 */ 1045 PublicKey getPublicKey() { 1046 return pubKey; 1047 } 1048 1049 /** 1050 * Create a string representation of this IssuerInfo. 1051 * 1052 * @return a {@code String} form of this IssuerInfo object. 1053 */ 1054 @Override 1055 public String toString() { 1056 StringBuilder sb = new StringBuilder(); 1057 sb.append("Issuer Info:\n"); 1058 sb.append("Name: ").append(name.toString()).append("\n"); 1059 sb.append("Public Key:\n").append(pubKey.toString()).append("\n"); 1060 return sb.toString(); 1061 } 1062 } 1063 }