jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java
changeset 2 90ce3da70b43
child 1567 58ef474069d7
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2003-2006 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.security.provider.certpath;
       
    27 
       
    28 import java.io.*;
       
    29 import java.math.BigInteger;
       
    30 import java.security.*;
       
    31 import java.security.cert.Certificate;
       
    32 import java.security.cert.CertificateFactory;
       
    33 import java.security.cert.CertPathValidatorException;
       
    34 import java.security.cert.CRLReason;
       
    35 import java.security.cert.X509Certificate;
       
    36 import java.security.cert.PKIXParameters;
       
    37 import javax.security.auth.x500.X500Principal;
       
    38 import java.util.Date;
       
    39 import java.util.HashMap;
       
    40 import java.util.List;
       
    41 import java.util.Map;
       
    42 import java.util.Set;
       
    43 import java.util.Iterator;
       
    44 import sun.misc.HexDumpEncoder;
       
    45 import sun.security.x509.*;
       
    46 import sun.security.util.*;
       
    47 
       
    48 /**
       
    49  * This class is used to process an OCSP response.
       
    50  * The OCSP Response is defined
       
    51  * in RFC 2560 and the ASN.1 encoding is as follows:
       
    52  * <pre>
       
    53  *
       
    54  *  OCSPResponse ::= SEQUENCE {
       
    55  *      responseStatus         OCSPResponseStatus,
       
    56  *      responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
       
    57  *
       
    58  *   OCSPResponseStatus ::= ENUMERATED {
       
    59  *       successful            (0),  --Response has valid confirmations
       
    60  *       malformedRequest      (1),  --Illegal confirmation request
       
    61  *       internalError         (2),  --Internal error in issuer
       
    62  *       tryLater              (3),  --Try again later
       
    63  *                                   --(4) is not used
       
    64  *       sigRequired           (5),  --Must sign the request
       
    65  *       unauthorized          (6)   --Request unauthorized
       
    66  *   }
       
    67  *
       
    68  *   ResponseBytes ::=       SEQUENCE {
       
    69  *       responseType   OBJECT IDENTIFIER,
       
    70  *       response       OCTET STRING }
       
    71  *
       
    72  *   BasicOCSPResponse       ::= SEQUENCE {
       
    73  *      tbsResponseData      ResponseData,
       
    74  *      signatureAlgorithm   AlgorithmIdentifier,
       
    75  *      signature            BIT STRING,
       
    76  *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
       
    77  *
       
    78  *   The value for signature SHALL be computed on the hash of the DER
       
    79  *   encoding ResponseData.
       
    80  *
       
    81  *   ResponseData ::= SEQUENCE {
       
    82  *      version              [0] EXPLICIT Version DEFAULT v1,
       
    83  *      responderID              ResponderID,
       
    84  *      producedAt               GeneralizedTime,
       
    85  *      responses                SEQUENCE OF SingleResponse,
       
    86  *      responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
       
    87  *
       
    88  *   ResponderID ::= CHOICE {
       
    89  *      byName               [1] Name,
       
    90  *      byKey                [2] KeyHash }
       
    91  *
       
    92  *   KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
       
    93  *   (excluding the tag and length fields)
       
    94  *
       
    95  *   SingleResponse ::= SEQUENCE {
       
    96  *      certID                       CertID,
       
    97  *      certStatus                   CertStatus,
       
    98  *      thisUpdate                   GeneralizedTime,
       
    99  *      nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
       
   100  *      singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
       
   101  *
       
   102  *   CertStatus ::= CHOICE {
       
   103  *       good        [0]     IMPLICIT NULL,
       
   104  *       revoked     [1]     IMPLICIT RevokedInfo,
       
   105  *       unknown     [2]     IMPLICIT UnknownInfo }
       
   106  *
       
   107  *   RevokedInfo ::= SEQUENCE {
       
   108  *       revocationTime              GeneralizedTime,
       
   109  *       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
       
   110  *
       
   111  *   UnknownInfo ::= NULL -- this can be replaced with an enumeration
       
   112  *
       
   113  * </pre>
       
   114  *
       
   115  * @author      Ram Marti
       
   116  */
       
   117 
       
   118 class OCSPResponse {
       
   119 
       
   120     // Certificate status CHOICE
       
   121     public static final int CERT_STATUS_GOOD = 0;
       
   122     public static final int CERT_STATUS_REVOKED = 1;
       
   123     public static final int CERT_STATUS_UNKNOWN = 2;
       
   124 
       
   125     private static final Debug DEBUG = Debug.getInstance("certpath");
       
   126     private static final boolean dump = false;
       
   127     private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID;
       
   128     private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID;
       
   129     static {
       
   130         ObjectIdentifier tmp1 = null;
       
   131         ObjectIdentifier tmp2 = null;
       
   132         try {
       
   133             tmp1 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.1");
       
   134             tmp2 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.2");
       
   135         } catch (Exception e) {
       
   136             // should not happen; log and exit
       
   137         }
       
   138         OCSP_BASIC_RESPONSE_OID = tmp1;
       
   139         OCSP_NONCE_EXTENSION_OID = tmp2;
       
   140     }
       
   141 
       
   142     // OCSP response status code
       
   143     private static final int OCSP_RESPONSE_OK = 0;
       
   144 
       
   145     // ResponderID CHOICE tags
       
   146     private static final int NAME_TAG = 1;
       
   147     private static final int KEY_TAG = 2;
       
   148 
       
   149     // Object identifier for the OCSPSigning key purpose
       
   150     private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
       
   151 
       
   152     private SingleResponse singleResponse;
       
   153 
       
   154     // an array of all of the CRLReasons (used in SingleResponse)
       
   155     private static CRLReason[] values = CRLReason.values();
       
   156 
       
   157     /*
       
   158      * Create an OCSP response from its ASN.1 DER encoding.
       
   159      */
       
   160     // used by OCSPChecker
       
   161     OCSPResponse(byte[] bytes, PKIXParameters params,
       
   162         X509Certificate responderCert)
       
   163         throws IOException, CertPathValidatorException {
       
   164 
       
   165         try {
       
   166             int responseStatus;
       
   167             ObjectIdentifier  responseType;
       
   168             int version;
       
   169             CertificateIssuerName responderName = null;
       
   170             Date producedAtDate;
       
   171             AlgorithmId sigAlgId;
       
   172             byte[] ocspNonce;
       
   173 
       
   174             // OCSPResponse
       
   175             if (dump) {
       
   176                 HexDumpEncoder hexEnc = new HexDumpEncoder();
       
   177                 System.out.println("OCSPResponse bytes are...");
       
   178                 System.out.println(hexEnc.encode(bytes));
       
   179             }
       
   180             DerValue der = new DerValue(bytes);
       
   181             if (der.tag != DerValue.tag_Sequence) {
       
   182                 throw new IOException("Bad encoding in OCSP response: " +
       
   183                     "expected ASN.1 SEQUENCE tag.");
       
   184             }
       
   185             DerInputStream derIn = der.getData();
       
   186 
       
   187             // responseStatus
       
   188             responseStatus = derIn.getEnumerated();
       
   189             if (DEBUG != null) {
       
   190                 DEBUG.println("OCSP response: " +
       
   191                     responseToText(responseStatus));
       
   192             }
       
   193             if (responseStatus != OCSP_RESPONSE_OK) {
       
   194                 throw new CertPathValidatorException(
       
   195                     "OCSP Response Failure: " +
       
   196                         responseToText(responseStatus));
       
   197             }
       
   198 
       
   199             // responseBytes
       
   200             der = derIn.getDerValue();
       
   201             if (! der.isContextSpecific((byte)0)) {
       
   202                 throw new IOException("Bad encoding in responseBytes element " +
       
   203                     "of OCSP response: expected ASN.1 context specific tag 0.");
       
   204             };
       
   205             DerValue tmp = der.data.getDerValue();
       
   206             if (tmp.tag != DerValue.tag_Sequence) {
       
   207                 throw new IOException("Bad encoding in responseBytes element " +
       
   208                     "of OCSP response: expected ASN.1 SEQUENCE tag.");
       
   209             }
       
   210 
       
   211             // responseType
       
   212             derIn = tmp.data;
       
   213             responseType = derIn.getOID();
       
   214             if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
       
   215                 if (DEBUG != null) {
       
   216                     DEBUG.println("OCSP response type: basic");
       
   217                 }
       
   218             } else {
       
   219                 if (DEBUG != null) {
       
   220                     DEBUG.println("OCSP response type: " + responseType);
       
   221                 }
       
   222                 throw new IOException("Unsupported OCSP response type: " +
       
   223                     responseType);
       
   224             }
       
   225 
       
   226             // BasicOCSPResponse
       
   227             DerInputStream basicOCSPResponse =
       
   228                 new DerInputStream(derIn.getOctetString());
       
   229 
       
   230             DerValue[]  seqTmp = basicOCSPResponse.getSequence(2);
       
   231             DerValue responseData = seqTmp[0];
       
   232 
       
   233             // Need the DER encoded ResponseData to verify the signature later
       
   234             byte[] responseDataDer = seqTmp[0].toByteArray();
       
   235 
       
   236             // tbsResponseData
       
   237             if (responseData.tag != DerValue.tag_Sequence) {
       
   238                 throw new IOException("Bad encoding in tbsResponseData " +
       
   239                     " element of OCSP response: expected ASN.1 SEQUENCE tag.");
       
   240             }
       
   241             DerInputStream seqDerIn = responseData.data;
       
   242             DerValue seq = seqDerIn.getDerValue();
       
   243 
       
   244             // version
       
   245             if (seq.isContextSpecific((byte)0)) {
       
   246                 // seq[0] is version
       
   247                 if (seq.isConstructed() && seq.isContextSpecific()) {
       
   248                     //System.out.println ("version is available");
       
   249                     seq = seq.data.getDerValue();
       
   250                     version = seq.getInteger();
       
   251                     if (seq.data.available() != 0) {
       
   252                         throw new IOException("Bad encoding in version " +
       
   253                             " element of OCSP response: bad format");
       
   254                     }
       
   255                     seq = seqDerIn.getDerValue();
       
   256                 }
       
   257             }
       
   258 
       
   259             // responderID
       
   260             short tag = (byte)(seq.tag & 0x1f);
       
   261             if (tag == NAME_TAG) {
       
   262                 responderName = new CertificateIssuerName(seq.getData());
       
   263                 if (DEBUG != null) {
       
   264                     DEBUG.println("OCSP Responder name: " + responderName);
       
   265                 }
       
   266             } else if (tag == KEY_TAG) {
       
   267                 // Ignore, for now
       
   268             } else {
       
   269                 throw new IOException("Bad encoding in responderID element " +
       
   270                     "of OCSP response: expected ASN.1 context specific tag 0 " +
       
   271                     "or 1");
       
   272             }
       
   273 
       
   274             // producedAt
       
   275             seq = seqDerIn.getDerValue();
       
   276             producedAtDate = seq.getGeneralizedTime();
       
   277 
       
   278             // responses
       
   279             DerValue[] singleResponseDer = seqDerIn.getSequence(1);
       
   280             // Examine only the first response
       
   281             singleResponse = new SingleResponse(singleResponseDer[0]);
       
   282 
       
   283             // responseExtensions
       
   284             if (seqDerIn.available() > 0) {
       
   285                 seq = seqDerIn.getDerValue();
       
   286                 if (seq.isContextSpecific((byte)1)) {
       
   287                     DerValue[]  responseExtDer = seq.data.getSequence(3);
       
   288                     Extension[] responseExtension =
       
   289                         new Extension[responseExtDer.length];
       
   290                     for (int i = 0; i < responseExtDer.length; i++) {
       
   291                         responseExtension[i] = new Extension(responseExtDer[i]);
       
   292                         if (DEBUG != null) {
       
   293                             DEBUG.println("OCSP extension: " +
       
   294                                 responseExtension[i]);
       
   295                         }
       
   296                         if ((responseExtension[i].getExtensionId()).equals(
       
   297                             OCSP_NONCE_EXTENSION_OID)) {
       
   298                             ocspNonce =
       
   299                                 responseExtension[i].getExtensionValue();
       
   300 
       
   301                         } else if (responseExtension[i].isCritical())  {
       
   302                             throw new IOException(
       
   303                                 "Unsupported OCSP critical extension: " +
       
   304                                 responseExtension[i].getExtensionId());
       
   305                         }
       
   306                     }
       
   307                 }
       
   308             }
       
   309 
       
   310             // signatureAlgorithmId
       
   311             sigAlgId = AlgorithmId.parse(seqTmp[1]);
       
   312 
       
   313             // signature
       
   314             byte[] signature = seqTmp[2].getBitString();
       
   315             X509CertImpl[] x509Certs = null;
       
   316 
       
   317             // if seq[3] is available , then it is a sequence of certificates
       
   318             if (seqTmp.length > 3) {
       
   319                 // certs are available
       
   320                 DerValue seqCert = seqTmp[3];
       
   321                 if (! seqCert.isContextSpecific((byte)0)) {
       
   322                     throw new IOException("Bad encoding in certs element " +
       
   323                     "of OCSP response: expected ASN.1 context specific tag 0.");
       
   324                 }
       
   325                 DerValue[] certs = (seqCert.getData()).getSequence(3);
       
   326                 x509Certs = new X509CertImpl[certs.length];
       
   327                 for (int i = 0; i < certs.length; i++) {
       
   328                     x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
       
   329                 }
       
   330             }
       
   331 
       
   332             // Check whether the cert returned by the responder is trusted
       
   333             if (x509Certs != null && x509Certs[0] != null) {
       
   334                 X509Certificate cert = x509Certs[0];
       
   335 
       
   336                 // First check if the cert matches the responder cert which
       
   337                 // was set locally.
       
   338                 if (cert.equals(responderCert)) {
       
   339                     // cert is trusted, now verify the signed response
       
   340 
       
   341                 // Next check if the cert was issued by the responder cert
       
   342                 // which was set locally.
       
   343                 } else if (cert.getIssuerDN().equals(
       
   344                     responderCert.getSubjectDN())) {
       
   345 
       
   346                     // Check for the OCSPSigning key purpose
       
   347                     List<String> keyPurposes = cert.getExtendedKeyUsage();
       
   348                     if (keyPurposes == null ||
       
   349                         !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
       
   350                         if (DEBUG != null) {
       
   351                             DEBUG.println("Responder's certificate is not " +
       
   352                                 "valid for signing OCSP responses.");
       
   353                         }
       
   354                         throw new CertPathValidatorException(
       
   355                             "Responder's certificate not valid for signing " +
       
   356                             "OCSP responses");
       
   357                     }
       
   358 
       
   359                     // verify the signature
       
   360                     try {
       
   361                         cert.verify(responderCert.getPublicKey());
       
   362                         responderCert = cert;
       
   363                         // cert is trusted, now verify the signed response
       
   364 
       
   365                     } catch (GeneralSecurityException e) {
       
   366                         responderCert = null;
       
   367                     }
       
   368                 }
       
   369             }
       
   370 
       
   371             // Confirm that the signed response was generated using the public
       
   372             // key from the trusted responder cert
       
   373             if (responderCert != null) {
       
   374 
       
   375                 if (! verifyResponse(responseDataDer, responderCert,
       
   376                     sigAlgId, signature, params)) {
       
   377                     if (DEBUG != null) {
       
   378                         DEBUG.println("Error verifying OCSP Responder's " +
       
   379                             "signature");
       
   380                     }
       
   381                     throw new CertPathValidatorException(
       
   382                         "Error verifying OCSP Responder's signature");
       
   383                 }
       
   384             } else {
       
   385                 // Need responder's cert in order to verify the signature
       
   386                 if (DEBUG != null) {
       
   387                     DEBUG.println("Unable to verify OCSP Responder's " +
       
   388                         "signature");
       
   389                 }
       
   390                 throw new CertPathValidatorException(
       
   391                     "Unable to verify OCSP Responder's signature");
       
   392             }
       
   393         } catch (CertPathValidatorException cpve) {
       
   394             throw cpve;
       
   395         } catch (Exception e) {
       
   396             throw new CertPathValidatorException(e);
       
   397         }
       
   398     }
       
   399 
       
   400     /*
       
   401      * Verify the signature of the OCSP response.
       
   402      * The responder's cert is implicitly trusted.
       
   403      */
       
   404     private boolean verifyResponse(byte[] responseData, X509Certificate cert,
       
   405         AlgorithmId sigAlgId, byte[] signBytes, PKIXParameters params)
       
   406         throws SignatureException {
       
   407 
       
   408         try {
       
   409 
       
   410             Signature respSignature = Signature.getInstance(sigAlgId.getName());
       
   411             respSignature.initVerify(cert);
       
   412             respSignature.update(responseData);
       
   413 
       
   414             if (respSignature.verify(signBytes)) {
       
   415                 if (DEBUG != null) {
       
   416                     DEBUG.println("Verified signature of OCSP Responder");
       
   417                 }
       
   418                 return true;
       
   419 
       
   420             } else {
       
   421                 if (DEBUG != null) {
       
   422                     DEBUG.println(
       
   423                         "Error verifying signature of OCSP Responder");
       
   424                 }
       
   425                 return false;
       
   426             }
       
   427         } catch (InvalidKeyException ike) {
       
   428             throw new SignatureException(ike);
       
   429 
       
   430         } catch (NoSuchAlgorithmException nsae) {
       
   431             throw new SignatureException(nsae);
       
   432         }
       
   433     }
       
   434 
       
   435     /*
       
   436      * Return the revocation status code for a given certificate.
       
   437      */
       
   438     // used by OCSPChecker
       
   439     int getCertStatus(SerialNumber sn) {
       
   440         // ignore serial number for now; if we support multiple
       
   441         // requests/responses then it will be used
       
   442         return singleResponse.getStatus();
       
   443     }
       
   444 
       
   445     // used by OCSPChecker
       
   446     CertId getCertId() {
       
   447         return singleResponse.getCertId();
       
   448     }
       
   449 
       
   450     Date getRevocationTime() {
       
   451         return singleResponse.getRevocationTime();
       
   452     }
       
   453 
       
   454     CRLReason getRevocationReason() {
       
   455         return singleResponse.getRevocationReason();
       
   456     }
       
   457 
       
   458     Map<String, java.security.cert.Extension> getSingleExtensions() {
       
   459         return singleResponse.getSingleExtensions();
       
   460     }
       
   461 
       
   462     /*
       
   463      * Map an OCSP response status code to a string.
       
   464      */
       
   465     static private String responseToText(int status) {
       
   466         switch (status)  {
       
   467         case 0:
       
   468             return "Successful";
       
   469         case 1:
       
   470             return "Malformed request";
       
   471         case 2:
       
   472             return "Internal error";
       
   473         case 3:
       
   474             return "Try again later";
       
   475         case 4:
       
   476             return "Unused status code";
       
   477         case 5:
       
   478             return "Request must be signed";
       
   479         case 6:
       
   480             return "Request is unauthorized";
       
   481         default:
       
   482             return ("Unknown status code: " + status);
       
   483         }
       
   484     }
       
   485 
       
   486     /*
       
   487      * Map a certificate's revocation status code to a string.
       
   488      */
       
   489     // used by OCSPChecker
       
   490     static String certStatusToText(int certStatus) {
       
   491         switch (certStatus)  {
       
   492         case 0:
       
   493             return "Good";
       
   494         case 1:
       
   495             return "Revoked";
       
   496         case 2:
       
   497             return "Unknown";
       
   498         default:
       
   499             return ("Unknown certificate status code: " + certStatus);
       
   500         }
       
   501     }
       
   502 
       
   503     /*
       
   504      * A class representing a single OCSP response.
       
   505      */
       
   506     private class SingleResponse {
       
   507         private CertId certId;
       
   508         private int certStatus;
       
   509         private Date thisUpdate;
       
   510         private Date nextUpdate;
       
   511         private Date revocationTime;
       
   512         private CRLReason revocationReason = CRLReason.UNSPECIFIED;
       
   513         private HashMap<String, java.security.cert.Extension> singleExtensions;
       
   514 
       
   515         private SingleResponse(DerValue der) throws IOException {
       
   516             if (der.tag != DerValue.tag_Sequence) {
       
   517                 throw new IOException("Bad ASN.1 encoding in SingleResponse");
       
   518             }
       
   519             DerInputStream tmp = der.data;
       
   520 
       
   521             certId = new CertId(tmp.getDerValue().data);
       
   522             DerValue derVal = tmp.getDerValue();
       
   523             short tag = (byte)(derVal.tag & 0x1f);
       
   524             if (tag ==  CERT_STATUS_GOOD) {
       
   525                 certStatus = CERT_STATUS_GOOD;
       
   526             } else if (tag == CERT_STATUS_REVOKED) {
       
   527                 certStatus = CERT_STATUS_REVOKED;
       
   528                 revocationTime = derVal.data.getGeneralizedTime();
       
   529                 if (derVal.data.available() != 0) {
       
   530                     int reason = derVal.getEnumerated();
       
   531                     // if reason out-of-range just leave as UNSPECIFIED
       
   532                     if (reason >= 0 && reason < values.length) {
       
   533                         revocationReason = values[reason];
       
   534                     }
       
   535                 }
       
   536                 // RevokedInfo
       
   537                 if (DEBUG != null) {
       
   538                     DEBUG.println("Revocation time: " + revocationTime);
       
   539                     DEBUG.println("Revocation reason: " + revocationReason);
       
   540                 }
       
   541 
       
   542             } else if (tag == CERT_STATUS_UNKNOWN) {
       
   543                 certStatus = CERT_STATUS_UNKNOWN;
       
   544 
       
   545             } else {
       
   546                 throw new IOException("Invalid certificate status");
       
   547             }
       
   548 
       
   549             thisUpdate = tmp.getGeneralizedTime();
       
   550 
       
   551             if (tmp.available() == 0)  {
       
   552                 // we are done
       
   553             } else {
       
   554                 derVal = tmp.getDerValue();
       
   555                 tag = (byte)(derVal.tag & 0x1f);
       
   556                 if (tag == 0) {
       
   557                     // next update
       
   558                     nextUpdate = derVal.data.getGeneralizedTime();
       
   559 
       
   560                     if (tmp.available() == 0)  {
       
   561                         // we are done
       
   562                     } else {
       
   563                         derVal = tmp.getDerValue();
       
   564                         tag = (byte)(derVal.tag & 0x1f);
       
   565                     }
       
   566                 }
       
   567             }
       
   568             // singleExtensions
       
   569             if (tmp.available() > 0) {
       
   570                 derVal = tmp.getDerValue();
       
   571                 if (derVal.isContextSpecific((byte)1)) {
       
   572                     DerValue[] singleExtDer = derVal.data.getSequence(3);
       
   573                     singleExtensions =
       
   574                         new HashMap<String, java.security.cert.Extension>
       
   575                             (singleExtDer.length);
       
   576                     for (int i = 0; i < singleExtDer.length; i++) {
       
   577                         Extension ext = new Extension(singleExtDer[i]);
       
   578                         singleExtensions.put(ext.getId(), ext);
       
   579                         if (DEBUG != null) {
       
   580                             DEBUG.println("OCSP single extension: " + ext);
       
   581                         }
       
   582                     }
       
   583                 }
       
   584             }
       
   585 
       
   586             Date now = new Date();
       
   587             if (DEBUG != null) {
       
   588                 String until = "";
       
   589                 if (nextUpdate != null) {
       
   590                     until = " until " + nextUpdate;
       
   591                 }
       
   592                 DEBUG.println("Response's validity interval is from " +
       
   593                     thisUpdate + until);
       
   594             }
       
   595             // Check that the test date is within the validity interval
       
   596             if ((thisUpdate != null && now.before(thisUpdate)) ||
       
   597                 (nextUpdate != null && now.after(nextUpdate))) {
       
   598 
       
   599                 if (DEBUG != null) {
       
   600                     DEBUG.println("Response is unreliable: its validity " +
       
   601                         "interval is out-of-date");
       
   602                 }
       
   603                 throw new IOException("Response is unreliable: its validity " +
       
   604                     "interval is out-of-date");
       
   605             }
       
   606         }
       
   607 
       
   608         /*
       
   609          * Return the certificate's revocation status code
       
   610          */
       
   611         private int getStatus() {
       
   612             return certStatus;
       
   613         }
       
   614 
       
   615         private CertId getCertId() {
       
   616             return certId;
       
   617         }
       
   618 
       
   619         private Date getRevocationTime() {
       
   620             return revocationTime;
       
   621         }
       
   622 
       
   623         private CRLReason getRevocationReason() {
       
   624             return revocationReason;
       
   625         }
       
   626 
       
   627         private Map<String, java.security.cert.Extension> getSingleExtensions() {
       
   628             return singleExtensions;
       
   629         }
       
   630 
       
   631         /**
       
   632          * Construct a string representation of a single OCSP response.
       
   633          */
       
   634         public String toString() {
       
   635             StringBuilder sb = new StringBuilder();
       
   636             sb.append("SingleResponse:  \n");
       
   637             sb.append(certId);
       
   638             sb.append("\nCertStatus: "+ certStatusToText(getCertStatus(null)) +
       
   639                 "\n");
       
   640             if (certStatus == CERT_STATUS_REVOKED) {
       
   641                 sb.append("revocationTime is " + revocationTime + "\n");
       
   642                 sb.append("revocationReason is " + revocationReason + "\n");
       
   643             }
       
   644             sb.append("thisUpdate is " + thisUpdate + "\n");
       
   645             if (nextUpdate != null) {
       
   646                 sb.append("nextUpdate is " + nextUpdate + "\n");
       
   647             }
       
   648             return sb.toString();
       
   649         }
       
   650     }
       
   651 }