jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java
changeset 3841 6738c111d48f
parent 3314 1e9d33caef08
child 4209 e2e5a973b879
equal deleted inserted replaced
3714:6a4eb8f53f91 3841:6738c111d48f
    26 package sun.security.provider.certpath;
    26 package sun.security.provider.certpath;
    27 
    27 
    28 import java.io.*;
    28 import java.io.*;
    29 import java.math.BigInteger;
    29 import java.math.BigInteger;
    30 import java.security.*;
    30 import java.security.*;
       
    31 import java.security.cert.CertificateException;
       
    32 import java.security.cert.CertificateParsingException;
    31 import java.security.cert.CertPathValidatorException;
    33 import java.security.cert.CertPathValidatorException;
    32 import java.security.cert.CRLReason;
    34 import java.security.cert.CRLReason;
    33 import java.security.cert.X509Certificate;
    35 import java.security.cert.X509Certificate;
    34 import java.security.cert.PKIXParameters;
    36 import java.util.Collections;
    35 import javax.security.auth.x500.X500Principal;
       
    36 import java.util.Date;
    37 import java.util.Date;
    37 import java.util.HashMap;
    38 import java.util.HashMap;
    38 import java.util.List;
    39 import java.util.List;
    39 import java.util.Map;
    40 import java.util.Map;
    40 import java.util.Set;
       
    41 import java.util.Iterator;
       
    42 import sun.misc.HexDumpEncoder;
    41 import sun.misc.HexDumpEncoder;
    43 import sun.security.x509.*;
    42 import sun.security.x509.*;
    44 import sun.security.util.*;
    43 import sun.security.util.*;
    45 
    44 
    46 /**
    45 /**
   111  * </pre>
   110  * </pre>
   112  *
   111  *
   113  * @author      Ram Marti
   112  * @author      Ram Marti
   114  */
   113  */
   115 
   114 
   116 class OCSPResponse {
   115 public final class OCSPResponse {
   117 
   116 
   118     // Certificate status CHOICE
   117     public enum ResponseStatus {
   119     public static final int CERT_STATUS_GOOD = 0;
   118         SUCCESSFUL,            // Response has valid confirmations
   120     public static final int CERT_STATUS_REVOKED = 1;
   119         MALFORMED_REQUEST,     // Illegal confirmation request
   121     public static final int CERT_STATUS_UNKNOWN = 2;
   120         INTERNAL_ERROR,        // Internal error in issuer
       
   121         TRY_LATER,             // Try again later
       
   122         UNUSED,                // is not used
       
   123         SIG_REQUIRED,          // Must sign the request
       
   124         UNAUTHORIZED           // Request unauthorized
       
   125     };
       
   126     private static ResponseStatus[] rsvalues = ResponseStatus.values();
   122 
   127 
   123     private static final Debug DEBUG = Debug.getInstance("certpath");
   128     private static final Debug DEBUG = Debug.getInstance("certpath");
   124     private static final boolean dump = false;
   129     private static final boolean dump = false;
   125     private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID;
   130     private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
   126     private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID;
   131         ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
   127     static {
   132     private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID =
   128         ObjectIdentifier tmp1 = null;
   133         ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});
   129         ObjectIdentifier tmp2 = null;
   134 
   130         try {
   135     private static final int CERT_STATUS_GOOD = 0;
   131             tmp1 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.1");
   136     private static final int CERT_STATUS_REVOKED = 1;
   132             tmp2 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.2");
   137     private static final int CERT_STATUS_UNKNOWN = 2;
   133         } catch (Exception e) {
       
   134             // should not happen; log and exit
       
   135         }
       
   136         OCSP_BASIC_RESPONSE_OID = tmp1;
       
   137         OCSP_NONCE_EXTENSION_OID = tmp2;
       
   138     }
       
   139 
       
   140     // OCSP response status code
       
   141     private static final int OCSP_RESPONSE_OK = 0;
       
   142 
   138 
   143     // ResponderID CHOICE tags
   139     // ResponderID CHOICE tags
   144     private static final int NAME_TAG = 1;
   140     private static final int NAME_TAG = 1;
   145     private static final int KEY_TAG = 2;
   141     private static final int KEY_TAG = 2;
   146 
   142 
   147     // Object identifier for the OCSPSigning key purpose
   143     // 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";
   144     private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
   149 
   145 
   150     private SingleResponse singleResponse;
   146     private final ResponseStatus responseStatus;
       
   147     private final Map<CertId, SingleResponse> singleResponseMap;
   151 
   148 
   152     // Maximum clock skew in milliseconds (15 minutes) allowed when checking
   149     // Maximum clock skew in milliseconds (15 minutes) allowed when checking
   153     // validity of OCSP responses
   150     // validity of OCSP responses
   154     private static final long MAX_CLOCK_SKEW = 900000;
   151     private static final long MAX_CLOCK_SKEW = 900000;
   155 
   152 
   157     private static CRLReason[] values = CRLReason.values();
   154     private static CRLReason[] values = CRLReason.values();
   158 
   155 
   159     /*
   156     /*
   160      * Create an OCSP response from its ASN.1 DER encoding.
   157      * Create an OCSP response from its ASN.1 DER encoding.
   161      */
   158      */
   162     // used by OCSPChecker
   159     OCSPResponse(byte[] bytes, Date dateCheckedAgainst,
   163     OCSPResponse(byte[] bytes, PKIXParameters params,
       
   164         X509Certificate responderCert)
   160         X509Certificate responderCert)
   165         throws IOException, CertPathValidatorException {
   161         throws IOException, CertPathValidatorException {
   166 
   162 
   167         try {
   163         // OCSPResponse
   168             int responseStatus;
   164         if (dump) {
   169             ObjectIdentifier  responseType;
   165             HexDumpEncoder hexEnc = new HexDumpEncoder();
   170             int version;
   166             System.out.println("OCSPResponse bytes are...");
   171             CertificateIssuerName responderName = null;
   167             System.out.println(hexEnc.encode(bytes));
   172             Date producedAtDate;
   168         }
   173             AlgorithmId sigAlgId;
   169         DerValue der = new DerValue(bytes);
   174             byte[] ocspNonce;
   170         if (der.tag != DerValue.tag_Sequence) {
   175 
   171             throw new IOException("Bad encoding in OCSP response: " +
   176             // OCSPResponse
   172                 "expected ASN.1 SEQUENCE tag.");
   177             if (dump) {
   173         }
   178                 HexDumpEncoder hexEnc = new HexDumpEncoder();
   174         DerInputStream derIn = der.getData();
   179                 System.out.println("OCSPResponse bytes are...");
   175 
   180                 System.out.println(hexEnc.encode(bytes));
   176         // responseStatus
   181             }
   177         int status = derIn.getEnumerated();
   182             DerValue der = new DerValue(bytes);
   178         if (status >= 0 && status < rsvalues.length) {
   183             if (der.tag != DerValue.tag_Sequence) {
   179             responseStatus = rsvalues[status];
   184                 throw new IOException("Bad encoding in OCSP response: " +
   180         } else {
   185                     "expected ASN.1 SEQUENCE tag.");
   181             // unspecified responseStatus
   186             }
   182             throw new IOException("Unknown OCSPResponse status: " + status);
   187             DerInputStream derIn = der.getData();
   183         }
   188 
   184         if (DEBUG != null) {
   189             // responseStatus
   185             DEBUG.println("OCSP response status: " + responseStatus);
   190             responseStatus = derIn.getEnumerated();
   186         }
       
   187         if (responseStatus != ResponseStatus.SUCCESSFUL) {
       
   188             // no need to continue, responseBytes are not set.
       
   189             singleResponseMap = Collections.emptyMap();
       
   190             return;
       
   191         }
       
   192 
       
   193         // responseBytes
       
   194         der = derIn.getDerValue();
       
   195         if (!der.isContextSpecific((byte)0)) {
       
   196             throw new IOException("Bad encoding in responseBytes element " +
       
   197                 "of OCSP response: expected ASN.1 context specific tag 0.");
       
   198         }
       
   199         DerValue tmp = der.data.getDerValue();
       
   200         if (tmp.tag != DerValue.tag_Sequence) {
       
   201             throw new IOException("Bad encoding in responseBytes element " +
       
   202                 "of OCSP response: expected ASN.1 SEQUENCE tag.");
       
   203         }
       
   204 
       
   205         // responseType
       
   206         derIn = tmp.data;
       
   207         ObjectIdentifier responseType = derIn.getOID();
       
   208         if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
   191             if (DEBUG != null) {
   209             if (DEBUG != null) {
   192                 DEBUG.println("OCSP response: " +
   210                 DEBUG.println("OCSP response type: basic");
   193                     responseToText(responseStatus));
   211             }
   194             }
   212         } else {
   195             if (responseStatus != OCSP_RESPONSE_OK) {
   213             if (DEBUG != null) {
   196                 throw new CertPathValidatorException(
   214                 DEBUG.println("OCSP response type: " + responseType);
   197                     "OCSP Response Failure: " +
   215             }
   198                         responseToText(responseStatus));
   216             throw new IOException("Unsupported OCSP response type: " +
   199             }
   217                 responseType);
   200 
   218         }
   201             // responseBytes
   219 
   202             der = derIn.getDerValue();
   220         // BasicOCSPResponse
   203             if (! der.isContextSpecific((byte)0)) {
   221         DerInputStream basicOCSPResponse =
   204                 throw new IOException("Bad encoding in responseBytes element " +
   222             new DerInputStream(derIn.getOctetString());
   205                     "of OCSP response: expected ASN.1 context specific tag 0.");
   223 
   206             };
   224         DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
   207             DerValue tmp = der.data.getDerValue();
   225         if (seqTmp.length < 3) {
   208             if (tmp.tag != DerValue.tag_Sequence) {
   226             throw new IOException("Unexpected BasicOCSPResponse value");
   209                 throw new IOException("Bad encoding in responseBytes element " +
   227         }
   210                     "of OCSP response: expected ASN.1 SEQUENCE tag.");
   228 
   211             }
   229         DerValue responseData = seqTmp[0];
   212 
   230 
   213             // responseType
   231         // Need the DER encoded ResponseData to verify the signature later
   214             derIn = tmp.data;
   232         byte[] responseDataDer = seqTmp[0].toByteArray();
   215             responseType = derIn.getOID();
   233 
   216             if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
   234         // tbsResponseData
   217                 if (DEBUG != null) {
   235         if (responseData.tag != DerValue.tag_Sequence) {
   218                     DEBUG.println("OCSP response type: basic");
   236             throw new IOException("Bad encoding in tbsResponseData " +
   219                 }
   237                 "element of OCSP response: expected ASN.1 SEQUENCE tag.");
   220             } else {
   238         }
   221                 if (DEBUG != null) {
   239         DerInputStream seqDerIn = responseData.data;
   222                     DEBUG.println("OCSP response type: " + responseType);
   240         DerValue seq = seqDerIn.getDerValue();
   223                 }
   241 
   224                 throw new IOException("Unsupported OCSP response type: " +
   242         // version
   225                     responseType);
   243         if (seq.isContextSpecific((byte)0)) {
   226             }
   244             // seq[0] is version
   227 
   245             if (seq.isConstructed() && seq.isContextSpecific()) {
   228             // BasicOCSPResponse
   246                 //System.out.println ("version is available");
   229             DerInputStream basicOCSPResponse =
   247                 seq = seq.data.getDerValue();
   230                 new DerInputStream(derIn.getOctetString());
   248                 int version = seq.getInteger();
   231 
   249                 if (seq.data.available() != 0) {
   232             DerValue[]  seqTmp = basicOCSPResponse.getSequence(2);
   250                     throw new IOException("Bad encoding in version " +
   233             DerValue responseData = seqTmp[0];
   251                         " element of OCSP response: bad format");
   234 
   252                 }
   235             // Need the DER encoded ResponseData to verify the signature later
   253                 seq = seqDerIn.getDerValue();
   236             byte[] responseDataDer = seqTmp[0].toByteArray();
   254             }
   237 
   255         }
   238             // tbsResponseData
   256 
   239             if (responseData.tag != DerValue.tag_Sequence) {
   257         // responderID
   240                 throw new IOException("Bad encoding in tbsResponseData " +
   258         short tag = (byte)(seq.tag & 0x1f);
   241                     " element of OCSP response: expected ASN.1 SEQUENCE tag.");
   259         if (tag == NAME_TAG) {
   242             }
   260             if (DEBUG != null) {
   243             DerInputStream seqDerIn = responseData.data;
   261                 X500Name responderName = new X500Name(seq.getData());
   244             DerValue seq = seqDerIn.getDerValue();
   262                 DEBUG.println("OCSP Responder name: " + responderName);
   245 
   263             }
   246             // version
   264         } else if (tag == KEY_TAG) {
   247             if (seq.isContextSpecific((byte)0)) {
   265             // Ignore, for now
   248                 // seq[0] is version
   266         } else {
   249                 if (seq.isConstructed() && seq.isContextSpecific()) {
   267             throw new IOException("Bad encoding in responderID element of " +
   250                     //System.out.println ("version is available");
   268                 "OCSP response: expected ASN.1 context specific tag 0 or 1");
   251                     seq = seq.data.getDerValue();
   269         }
   252                     version = seq.getInteger();
   270 
   253                     if (seq.data.available() != 0) {
   271         // producedAt
   254                         throw new IOException("Bad encoding in version " +
   272         seq = seqDerIn.getDerValue();
   255                             " element of OCSP response: bad format");
   273         if (DEBUG != null) {
   256                     }
   274             Date producedAtDate = seq.getGeneralizedTime();
   257                     seq = seqDerIn.getDerValue();
   275             DEBUG.println("OCSP response produced at: " + producedAtDate);
   258                 }
   276         }
   259             }
   277 
   260 
   278         // responses
   261             // responderID
   279         DerValue[] singleResponseDer = seqDerIn.getSequence(1);
   262             short tag = (byte)(seq.tag & 0x1f);
   280         singleResponseMap
   263             if (tag == NAME_TAG) {
   281             = new HashMap<CertId, SingleResponse>(singleResponseDer.length);
   264                 responderName = new CertificateIssuerName(seq.getData());
   282         if (DEBUG != null) {
   265                 if (DEBUG != null) {
   283             DEBUG.println("OCSP number of SingleResponses: "
   266                     DEBUG.println("OCSP Responder name: " + responderName);
   284                 + singleResponseDer.length);
   267                 }
   285         }
   268             } else if (tag == KEY_TAG) {
   286         for (int i = 0; i < singleResponseDer.length; i++) {
   269                 // Ignore, for now
   287             SingleResponse singleResponse
   270             } else {
   288                 = new SingleResponse(singleResponseDer[i]);
   271                 throw new IOException("Bad encoding in responderID element " +
   289             singleResponseMap.put(singleResponse.getCertId(), singleResponse);
   272                     "of OCSP response: expected ASN.1 context specific tag 0 " +
   290         }
   273                     "or 1");
   291 
   274             }
   292         // responseExtensions
   275 
   293         if (seqDerIn.available() > 0) {
   276             // producedAt
       
   277             seq = seqDerIn.getDerValue();
   294             seq = seqDerIn.getDerValue();
   278             producedAtDate = seq.getGeneralizedTime();
   295             if (seq.isContextSpecific((byte)1)) {
   279 
   296                 DerValue[] responseExtDer = seq.data.getSequence(3);
   280             // responses
   297                 for (int i = 0; i < responseExtDer.length; i++) {
   281             DerValue[] singleResponseDer = seqDerIn.getSequence(1);
   298                     Extension responseExtension
   282             // Examine only the first response
   299                         = new Extension(responseExtDer[i]);
   283             singleResponse = new SingleResponse(singleResponseDer[0]);
   300                     if (DEBUG != null) {
   284 
   301                         DEBUG.println("OCSP extension: " + responseExtension);
   285             // responseExtensions
   302                     }
   286             if (seqDerIn.available() > 0) {
   303                     if (responseExtension.getExtensionId().equals(
   287                 seq = seqDerIn.getDerValue();
   304                         OCSP_NONCE_EXTENSION_OID)) {
   288                 if (seq.isContextSpecific((byte)1)) {
   305                         /*
   289                     DerValue[]  responseExtDer = seq.data.getSequence(3);
   306                         ocspNonce =
   290                     Extension[] responseExtension =
   307                             responseExtension[i].getExtensionValue();
   291                         new Extension[responseExtDer.length];
   308                          */
   292                     for (int i = 0; i < responseExtDer.length; i++) {
   309                     } else if (responseExtension.isCritical())  {
   293                         responseExtension[i] = new Extension(responseExtDer[i]);
   310                         throw new IOException(
   294                         if (DEBUG != null) {
   311                             "Unsupported OCSP critical extension: " +
   295                             DEBUG.println("OCSP extension: " +
   312                             responseExtension.getExtensionId());
   296                                 responseExtension[i]);
   313                     }
   297                         }
   314                 }
   298                         if ((responseExtension[i].getExtensionId()).equals(
   315             }
   299                             OCSP_NONCE_EXTENSION_OID)) {
   316         }
   300                             ocspNonce =
   317 
   301                                 responseExtension[i].getExtensionValue();
   318         // signatureAlgorithmId
   302 
   319         AlgorithmId sigAlgId = AlgorithmId.parse(seqTmp[1]);
   303                         } else if (responseExtension[i].isCritical())  {
   320 
   304                             throw new IOException(
   321         // signature
   305                                 "Unsupported OCSP critical extension: " +
   322         byte[] signature = seqTmp[2].getBitString();
   306                                 responseExtension[i].getExtensionId());
   323         X509CertImpl[] x509Certs = null;
   307                         }
   324 
   308                     }
   325         // if seq[3] is available , then it is a sequence of certificates
   309                 }
   326         if (seqTmp.length > 3) {
   310             }
   327             // certs are available
   311 
   328             DerValue seqCert = seqTmp[3];
   312             // signatureAlgorithmId
   329             if (!seqCert.isContextSpecific((byte)0)) {
   313             sigAlgId = AlgorithmId.parse(seqTmp[1]);
   330                 throw new IOException("Bad encoding in certs element of " +
   314 
   331                     "OCSP response: expected ASN.1 context specific tag 0.");
   315             // signature
   332             }
   316             byte[] signature = seqTmp[2].getBitString();
   333             DerValue[] certs = seqCert.getData().getSequence(3);
   317             X509CertImpl[] x509Certs = null;
   334             x509Certs = new X509CertImpl[certs.length];
   318 
   335             try {
   319             // if seq[3] is available , then it is a sequence of certificates
       
   320             if (seqTmp.length > 3) {
       
   321                 // certs are available
       
   322                 DerValue seqCert = seqTmp[3];
       
   323                 if (! seqCert.isContextSpecific((byte)0)) {
       
   324                     throw new IOException("Bad encoding in certs element " +
       
   325                     "of OCSP response: expected ASN.1 context specific tag 0.");
       
   326                 }
       
   327                 DerValue[] certs = (seqCert.getData()).getSequence(3);
       
   328                 x509Certs = new X509CertImpl[certs.length];
       
   329                 for (int i = 0; i < certs.length; i++) {
   336                 for (int i = 0; i < certs.length; i++) {
   330                     x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
   337                     x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
   331                 }
   338                 }
   332             }
   339             } catch (CertificateException ce) {
   333 
   340                 throw new IOException("Bad encoding in X509 Certificate", ce);
   334             // Check whether the cert returned by the responder is trusted
   341             }
   335             if (x509Certs != null && x509Certs[0] != null) {
   342         }
   336                 X509CertImpl cert = x509Certs[0];
   343 
   337 
   344         // Check whether the cert returned by the responder is trusted
   338                 // First check if the cert matches the responder cert which
   345         if (x509Certs != null && x509Certs[0] != null) {
   339                 // was set locally.
   346             X509CertImpl cert = x509Certs[0];
   340                 if (cert.equals(responderCert)) {
   347 
   341                     // cert is trusted, now verify the signed response
   348             // First check if the cert matches the responder cert which
   342 
   349             // was set locally.
   343                 // Next check if the cert was issued by the responder cert
   350             if (cert.equals(responderCert)) {
   344                 // which was set locally.
   351                 // cert is trusted, now verify the signed response
   345                 } else if (cert.getIssuerX500Principal().equals(
   352 
   346                     responderCert.getSubjectX500Principal())) {
   353             // Next check if the cert was issued by the responder cert
   347 
   354             // which was set locally.
   348                     // Check for the OCSPSigning key purpose
   355             } else if (cert.getIssuerX500Principal().equals(
       
   356                 responderCert.getSubjectX500Principal())) {
       
   357 
       
   358                 // Check for the OCSPSigning key purpose
       
   359                 try {
   349                     List<String> keyPurposes = cert.getExtendedKeyUsage();
   360                     List<String> keyPurposes = cert.getExtendedKeyUsage();
   350                     if (keyPurposes == null ||
   361                     if (keyPurposes == null ||
   351                         !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
   362                         !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
   352                         if (DEBUG != null) {
       
   353                             DEBUG.println("Responder's certificate is not " +
       
   354                                 "valid for signing OCSP responses.");
       
   355                         }
       
   356                         throw new CertPathValidatorException(
   363                         throw new CertPathValidatorException(
   357                             "Responder's certificate not valid for signing " +
   364                             "Responder's certificate not valid for signing " +
   358                             "OCSP responses");
   365                             "OCSP responses");
   359                     }
   366                     }
   360 
   367                 } catch (CertificateParsingException cpe) {
   361                     // check the validity
   368                     // assume cert is not valid for signing
   362                     try {
   369                     throw new CertPathValidatorException(
   363                         Date dateCheckedAgainst = params.getDate();
   370                         "Responder's certificate not valid for signing " +
   364                         if (dateCheckedAgainst == null) {
   371                         "OCSP responses", cpe);
   365                             cert.checkValidity();
   372                 }
   366                         } else {
   373 
   367                             cert.checkValidity(dateCheckedAgainst);
   374                 // check the validity
   368                         }
   375                 try {
   369                     } catch (GeneralSecurityException e) {
   376                     if (dateCheckedAgainst == null) {
   370                         if (DEBUG != null) {
   377                         cert.checkValidity();
   371                             DEBUG.println("Responder's certificate is not " +
       
   372                                 "within the validity period.");
       
   373                         }
       
   374                         throw new CertPathValidatorException(
       
   375                             "Responder's certificate not within the " +
       
   376                             "validity period");
       
   377                     }
       
   378 
       
   379                     // check for revocation
       
   380                     //
       
   381                     // A CA may specify that an OCSP client can trust a
       
   382                     // responder for the lifetime of the responder's
       
   383                     // certificate. The CA does so by including the
       
   384                     // extension id-pkix-ocsp-nocheck.
       
   385                     //
       
   386                     Extension noCheck =
       
   387                             cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
       
   388                     if (noCheck != null) {
       
   389                         if (DEBUG != null) {
       
   390                             DEBUG.println("Responder's certificate includes " +
       
   391                                 "the extension id-pkix-ocsp-nocheck.");
       
   392                         }
       
   393                     } else {
   378                     } else {
   394                         // we should do the revocating checking of the
   379                         cert.checkValidity(dateCheckedAgainst);
   395                         // authorized responder in a future update.
   380                     }
   396                     }
   381                 } catch (GeneralSecurityException e) {
   397 
   382                     throw new CertPathValidatorException(
   398                     // verify the signature
   383                         "Responder's certificate not within the " +
   399                     try {
   384                         "validity period", e);
   400                         cert.verify(responderCert.getPublicKey());
   385                 }
   401                         responderCert = cert;
   386 
   402                         // cert is trusted, now verify the signed response
   387                 // check for revocation
   403 
   388                 //
   404                     } catch (GeneralSecurityException e) {
   389                 // A CA may specify that an OCSP client can trust a
   405                         responderCert = null;
   390                 // responder for the lifetime of the responder's
       
   391                 // certificate. The CA does so by including the
       
   392                 // extension id-pkix-ocsp-nocheck.
       
   393                 //
       
   394                 Extension noCheck =
       
   395                     cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
       
   396                 if (noCheck != null) {
       
   397                     if (DEBUG != null) {
       
   398                         DEBUG.println("Responder's certificate includes " +
       
   399                             "the extension id-pkix-ocsp-nocheck.");
   406                     }
   400                     }
   407                 } else {
   401                 } else {
   408                     if (DEBUG != null) {
   402                     // we should do the revocation checking of the
   409                         DEBUG.println("Responder's certificate is not " +
   403                     // authorized responder in a future update.
   410                             "authorized to sign OCSP responses.");
   404                 }
   411                     }
   405 
   412                     throw new CertPathValidatorException(
   406                 // verify the signature
   413                         "Responder's certificate not authorized to sign " +
   407                 try {
   414                         "OCSP responses");
   408                     cert.verify(responderCert.getPublicKey());
   415                 }
   409                     responderCert = cert;
   416             }
   410                     // cert is trusted, now verify the signed response
   417 
   411 
   418             // Confirm that the signed response was generated using the public
   412                 } catch (GeneralSecurityException e) {
   419             // key from the trusted responder cert
   413                     responderCert = null;
   420             if (responderCert != null) {
       
   421 
       
   422                 if (! verifyResponse(responseDataDer, responderCert,
       
   423                     sigAlgId, signature, params)) {
       
   424                     if (DEBUG != null) {
       
   425                         DEBUG.println("Error verifying OCSP Responder's " +
       
   426                             "signature");
       
   427                     }
       
   428                     throw new CertPathValidatorException(
       
   429                         "Error verifying OCSP Responder's signature");
       
   430                 }
   414                 }
   431             } else {
   415             } else {
   432                 // Need responder's cert in order to verify the signature
       
   433                 if (DEBUG != null) {
       
   434                     DEBUG.println("Unable to verify OCSP Responder's " +
       
   435                         "signature");
       
   436                 }
       
   437                 throw new CertPathValidatorException(
   416                 throw new CertPathValidatorException(
   438                     "Unable to verify OCSP Responder's signature");
   417                     "Responder's certificate is not authorized to sign " +
   439             }
   418                     "OCSP responses");
   440         } catch (CertPathValidatorException cpve) {
   419             }
   441             throw cpve;
   420         }
   442         } catch (Exception e) {
   421 
   443             throw new CertPathValidatorException(e);
   422         // Confirm that the signed response was generated using the public
   444         }
   423         // key from the trusted responder cert
       
   424         if (responderCert != null) {
       
   425             if (!verifyResponse(responseDataDer, responderCert,
       
   426                 sigAlgId, signature)) {
       
   427                 throw new CertPathValidatorException(
       
   428                     "Error verifying OCSP Responder's signature");
       
   429             }
       
   430         } else {
       
   431             // Need responder's cert in order to verify the signature
       
   432             throw new CertPathValidatorException(
       
   433                 "Unable to verify OCSP Responder's signature");
       
   434         }
       
   435     }
       
   436 
       
   437     /**
       
   438      * Returns the OCSP ResponseStatus.
       
   439      */
       
   440     ResponseStatus getResponseStatus() {
       
   441         return responseStatus;
   445     }
   442     }
   446 
   443 
   447     /*
   444     /*
   448      * Verify the signature of the OCSP response.
   445      * Verify the signature of the OCSP response.
   449      * The responder's cert is implicitly trusted.
   446      * The responder's cert is implicitly trusted.
   450      */
   447      */
   451     private boolean verifyResponse(byte[] responseData, X509Certificate cert,
   448     private boolean verifyResponse(byte[] responseData, X509Certificate cert,
   452         AlgorithmId sigAlgId, byte[] signBytes, PKIXParameters params)
   449         AlgorithmId sigAlgId, byte[] signBytes)
   453         throws SignatureException {
   450         throws CertPathValidatorException {
   454 
   451 
   455         try {
   452         try {
   456 
       
   457             Signature respSignature = Signature.getInstance(sigAlgId.getName());
   453             Signature respSignature = Signature.getInstance(sigAlgId.getName());
   458             respSignature.initVerify(cert);
   454             respSignature.initVerify(cert);
   459             respSignature.update(responseData);
   455             respSignature.update(responseData);
   460 
   456 
   461             if (respSignature.verify(signBytes)) {
   457             if (respSignature.verify(signBytes)) {
   470                         "Error verifying signature of OCSP Responder");
   466                         "Error verifying signature of OCSP Responder");
   471                 }
   467                 }
   472                 return false;
   468                 return false;
   473             }
   469             }
   474         } catch (InvalidKeyException ike) {
   470         } catch (InvalidKeyException ike) {
   475             throw new SignatureException(ike);
   471             throw new CertPathValidatorException(ike);
   476 
       
   477         } catch (NoSuchAlgorithmException nsae) {
   472         } catch (NoSuchAlgorithmException nsae) {
   478             throw new SignatureException(nsae);
   473             throw new CertPathValidatorException(nsae);
       
   474         } catch (SignatureException se) {
       
   475             throw new CertPathValidatorException(se);
   479         }
   476         }
   480     }
   477     }
   481 
   478 
   482     /*
   479     /**
   483      * Return the revocation status code for a given certificate.
   480      * Returns the SingleResponse of the specified CertId, or null if
       
   481      * there is no response for that CertId.
   484      */
   482      */
   485     // used by OCSPChecker
   483     SingleResponse getSingleResponse(CertId certId) {
   486     int getCertStatus(SerialNumber sn) {
   484         return singleResponseMap.get(certId);
   487         // ignore serial number for now; if we support multiple
       
   488         // requests/responses then it will be used
       
   489         return singleResponse.getStatus();
       
   490     }
       
   491 
       
   492     // used by OCSPChecker
       
   493     CertId getCertId() {
       
   494         return singleResponse.getCertId();
       
   495     }
       
   496 
       
   497     Date getRevocationTime() {
       
   498         return singleResponse.getRevocationTime();
       
   499     }
       
   500 
       
   501     CRLReason getRevocationReason() {
       
   502         return singleResponse.getRevocationReason();
       
   503     }
       
   504 
       
   505     Map<String, java.security.cert.Extension> getSingleExtensions() {
       
   506         return singleResponse.getSingleExtensions();
       
   507     }
       
   508 
       
   509     /*
       
   510      * Map an OCSP response status code to a string.
       
   511      */
       
   512     static private String responseToText(int status) {
       
   513         switch (status)  {
       
   514         case 0:
       
   515             return "Successful";
       
   516         case 1:
       
   517             return "Malformed request";
       
   518         case 2:
       
   519             return "Internal error";
       
   520         case 3:
       
   521             return "Try again later";
       
   522         case 4:
       
   523             return "Unused status code";
       
   524         case 5:
       
   525             return "Request must be signed";
       
   526         case 6:
       
   527             return "Request is unauthorized";
       
   528         default:
       
   529             return ("Unknown status code: " + status);
       
   530         }
       
   531     }
       
   532 
       
   533     /*
       
   534      * Map a certificate's revocation status code to a string.
       
   535      */
       
   536     // used by OCSPChecker
       
   537     static String certStatusToText(int certStatus) {
       
   538         switch (certStatus)  {
       
   539         case 0:
       
   540             return "Good";
       
   541         case 1:
       
   542             return "Revoked";
       
   543         case 2:
       
   544             return "Unknown";
       
   545         default:
       
   546             return ("Unknown certificate status code: " + certStatus);
       
   547         }
       
   548     }
   485     }
   549 
   486 
   550     /*
   487     /*
   551      * A class representing a single OCSP response.
   488      * A class representing a single OCSP response.
   552      */
   489      */
   553     private class SingleResponse {
   490     final static class SingleResponse implements OCSP.RevocationStatus {
   554         private CertId certId;
   491         private final CertId certId;
   555         private int certStatus;
   492         private final CertStatus certStatus;
   556         private Date thisUpdate;
   493         private final Date thisUpdate;
   557         private Date nextUpdate;
   494         private final Date nextUpdate;
   558         private Date revocationTime;
   495         private final Date revocationTime;
   559         private CRLReason revocationReason = CRLReason.UNSPECIFIED;
   496         private final CRLReason revocationReason;
   560         private HashMap<String, java.security.cert.Extension> singleExtensions;
   497         private final Map<String, java.security.cert.Extension> singleExtensions;
   561 
   498 
   562         private SingleResponse(DerValue der) throws IOException {
   499         private SingleResponse(DerValue der) throws IOException {
   563             if (der.tag != DerValue.tag_Sequence) {
   500             if (der.tag != DerValue.tag_Sequence) {
   564                 throw new IOException("Bad ASN.1 encoding in SingleResponse");
   501                 throw new IOException("Bad ASN.1 encoding in SingleResponse");
   565             }
   502             }
   566             DerInputStream tmp = der.data;
   503             DerInputStream tmp = der.data;
   567 
   504 
   568             certId = new CertId(tmp.getDerValue().data);
   505             certId = new CertId(tmp.getDerValue().data);
   569             DerValue derVal = tmp.getDerValue();
   506             DerValue derVal = tmp.getDerValue();
   570             short tag = (byte)(derVal.tag & 0x1f);
   507             short tag = (byte)(derVal.tag & 0x1f);
   571             if (tag ==  CERT_STATUS_GOOD) {
   508             if (tag ==  CERT_STATUS_REVOKED) {
   572                 certStatus = CERT_STATUS_GOOD;
   509                 certStatus = CertStatus.REVOKED;
   573             } else if (tag == CERT_STATUS_REVOKED) {
       
   574                 certStatus = CERT_STATUS_REVOKED;
       
   575                 revocationTime = derVal.data.getGeneralizedTime();
   510                 revocationTime = derVal.data.getGeneralizedTime();
   576                 if (derVal.data.available() != 0) {
   511                 if (derVal.data.available() != 0) {
   577                     int reason = derVal.getEnumerated();
   512                     DerValue dv = derVal.data.getDerValue();
   578                     // if reason out-of-range just leave as UNSPECIFIED
   513                     tag = (byte)(dv.tag & 0x1f);
   579                     if (reason >= 0 && reason < values.length) {
   514                     if (tag == 0) {
   580                         revocationReason = values[reason];
   515                         int reason = dv.data.getEnumerated();
   581                     }
   516                         // if reason out-of-range just leave as UNSPECIFIED
       
   517                         if (reason >= 0 && reason < values.length) {
       
   518                             revocationReason = values[reason];
       
   519                         } else {
       
   520                             revocationReason = CRLReason.UNSPECIFIED;
       
   521                         }
       
   522                     } else {
       
   523                         revocationReason = CRLReason.UNSPECIFIED;
       
   524                     }
       
   525                 } else {
       
   526                     revocationReason = CRLReason.UNSPECIFIED;
   582                 }
   527                 }
   583                 // RevokedInfo
   528                 // RevokedInfo
   584                 if (DEBUG != null) {
   529                 if (DEBUG != null) {
   585                     DEBUG.println("Revocation time: " + revocationTime);
   530                     DEBUG.println("Revocation time: " + revocationTime);
   586                     DEBUG.println("Revocation reason: " + revocationReason);
   531                     DEBUG.println("Revocation reason: " + revocationReason);
   587                 }
   532                 }
   588 
       
   589             } else if (tag == CERT_STATUS_UNKNOWN) {
       
   590                 certStatus = CERT_STATUS_UNKNOWN;
       
   591 
       
   592             } else {
   533             } else {
   593                 throw new IOException("Invalid certificate status");
   534                 revocationTime = null;
       
   535                 revocationReason = CRLReason.UNSPECIFIED;
       
   536                 if (tag == CERT_STATUS_GOOD) {
       
   537                     certStatus = CertStatus.GOOD;
       
   538                 } else if (tag == CERT_STATUS_UNKNOWN) {
       
   539                     certStatus = CertStatus.UNKNOWN;
       
   540                 } else {
       
   541                     throw new IOException("Invalid certificate status");
       
   542                 }
   594             }
   543             }
   595 
   544 
   596             thisUpdate = tmp.getGeneralizedTime();
   545             thisUpdate = tmp.getGeneralizedTime();
   597 
   546 
   598             if (tmp.available() == 0)  {
   547             if (tmp.available() == 0)  {
   599                 // we are done
   548                 // we are done
       
   549                 nextUpdate = null;
   600             } else {
   550             } else {
   601                 derVal = tmp.getDerValue();
   551                 derVal = tmp.getDerValue();
   602                 tag = (byte)(derVal.tag & 0x1f);
   552                 tag = (byte)(derVal.tag & 0x1f);
   603                 if (tag == 0) {
   553                 if (tag == 0) {
   604                     // next update
   554                     // next update
   608                         // we are done
   558                         // we are done
   609                     } else {
   559                     } else {
   610                         derVal = tmp.getDerValue();
   560                         derVal = tmp.getDerValue();
   611                         tag = (byte)(derVal.tag & 0x1f);
   561                         tag = (byte)(derVal.tag & 0x1f);
   612                     }
   562                     }
       
   563                 } else {
       
   564                     nextUpdate = null;
   613                 }
   565                 }
   614             }
   566             }
   615             // singleExtensions
   567             // singleExtensions
   616             if (tmp.available() > 0) {
   568             if (tmp.available() > 0) {
   617                 derVal = tmp.getDerValue();
   569                 derVal = tmp.getDerValue();
   625                         singleExtensions.put(ext.getId(), ext);
   577                         singleExtensions.put(ext.getId(), ext);
   626                         if (DEBUG != null) {
   578                         if (DEBUG != null) {
   627                             DEBUG.println("OCSP single extension: " + ext);
   579                             DEBUG.println("OCSP single extension: " + ext);
   628                         }
   580                         }
   629                     }
   581                     }
   630                 }
   582                 } else {
       
   583                     singleExtensions = Collections.emptyMap();
       
   584                 }
       
   585             } else {
       
   586                 singleExtensions = Collections.emptyMap();
   631             }
   587             }
   632 
   588 
   633             long now = System.currentTimeMillis();
   589             long now = System.currentTimeMillis();
   634             Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
   590             Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
   635             Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
   591             Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
   655         }
   611         }
   656 
   612 
   657         /*
   613         /*
   658          * Return the certificate's revocation status code
   614          * Return the certificate's revocation status code
   659          */
   615          */
   660         private int getStatus() {
   616         @Override public CertStatus getCertStatus() {
   661             return certStatus;
   617             return certStatus;
   662         }
   618         }
   663 
   619 
   664         private CertId getCertId() {
   620         private CertId getCertId() {
   665             return certId;
   621             return certId;
   666         }
   622         }
   667 
   623 
   668         private Date getRevocationTime() {
   624         @Override public Date getRevocationTime() {
   669             return revocationTime;
   625             return (Date) revocationTime.clone();
   670         }
   626         }
   671 
   627 
   672         private CRLReason getRevocationReason() {
   628         @Override public CRLReason getRevocationReason() {
   673             return revocationReason;
   629             return revocationReason;
   674         }
   630         }
   675 
   631 
   676         private Map<String, java.security.cert.Extension> getSingleExtensions() {
   632         @Override
   677             return singleExtensions;
   633         public Map<String, java.security.cert.Extension> getSingleExtensions() {
       
   634             return Collections.unmodifiableMap(singleExtensions);
   678         }
   635         }
   679 
   636 
   680         /**
   637         /**
   681          * Construct a string representation of a single OCSP response.
   638          * Construct a string representation of a single OCSP response.
   682          */
   639          */
   683         public String toString() {
   640         @Override public String toString() {
   684             StringBuilder sb = new StringBuilder();
   641             StringBuilder sb = new StringBuilder();
   685             sb.append("SingleResponse:  \n");
   642             sb.append("SingleResponse:  \n");
   686             sb.append(certId);
   643             sb.append(certId);
   687             sb.append("\nCertStatus: "+ certStatusToText(getCertStatus(null)) +
   644             sb.append("\nCertStatus: "+ certStatus + "\n");
   688                 "\n");
   645             if (certStatus == CertStatus.REVOKED) {
   689             if (certStatus == CERT_STATUS_REVOKED) {
       
   690                 sb.append("revocationTime is " + revocationTime + "\n");
   646                 sb.append("revocationTime is " + revocationTime + "\n");
   691                 sb.append("revocationReason is " + revocationReason + "\n");
   647                 sb.append("revocationReason is " + revocationReason + "\n");
   692             }
   648             }
   693             sb.append("thisUpdate is " + thisUpdate + "\n");
   649             sb.append("thisUpdate is " + thisUpdate + "\n");
   694             if (nextUpdate != null) {
   650             if (nextUpdate != null) {