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