changeset 5417:52ab0f489dab

7168191: Signature validation can fail under certain circumstances Reviewed-by: mullan
author vinnie
date Wed, 20 Jun 2012 19:35:47 +0100
parents d0922321932a
children 7fdc1af4bf3f
files src/share/classes/sun/security/provider/certpath/OCSP.java src/share/classes/sun/security/provider/certpath/OCSPChecker.java src/share/classes/sun/security/provider/certpath/OCSPRequest.java src/share/classes/sun/security/provider/certpath/OCSPResponse.java src/share/classes/sun/security/x509/X509CertImpl.java
diffstat 5 files changed, 340 insertions(+), 162 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/provider/certpath/OCSP.java	Thu Jun 14 14:29:49 2012 +0400
+++ b/src/share/classes/sun/security/provider/certpath/OCSP.java	Wed Jun 20 19:35:47 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -84,7 +84,7 @@
      */
     public static RevocationStatus check(X509Certificate cert,
         X509Certificate issuerCert)
-        throws IOException, CertPathValidatorException {
+            throws IOException, CertPathValidatorException {
         CertId certId = null;
         URI responderURI = null;
         try {
@@ -103,7 +103,7 @@
                 ("Exception while encoding OCSPRequest", ioe);
         }
         OCSPResponse ocspResponse = check(Collections.singletonList(certId),
-            responderURI, issuerCert, null);
+            responderURI, Collections.singletonList(issuerCert), null);
         return (RevocationStatus) ocspResponse.getSingleResponse(certId);
     }
 
@@ -123,9 +123,34 @@
      *    encoding the OCSP Request or validating the OCSP Response
      */
     public static RevocationStatus check(X509Certificate cert,
-        X509Certificate issuerCert, URI responderURI, X509Certificate
-        responderCert, Date date)
-        throws IOException, CertPathValidatorException {
+        X509Certificate issuerCert, URI responderURI,
+        X509Certificate responderCert, Date date)
+            throws IOException, CertPathValidatorException {
+
+        return check(cert, issuerCert, responderURI,
+            Collections.singletonList(responderCert), date);
+    }
+
+    /**
+     * Obtains the revocation status of a certificate using OCSP.
+     *
+     * @param cert the certificate to be checked
+     * @param issuerCert the issuer certificate
+     * @param responderURI the URI of the OCSP responder
+     * @param responderCerts the OCSP responder's certificates
+     * @param date the time the validity of the OCSP responder's certificate
+     *    should be checked against. If null, the current time is used.
+     * @return the RevocationStatus
+     * @throws IOException if there is an exception connecting to or
+     *    communicating with the OCSP responder
+     * @throws CertPathValidatorException if an exception occurs while
+     *    encoding the OCSP Request or validating the OCSP Response
+     */
+    public static RevocationStatus check(X509Certificate cert,
+        X509Certificate issuerCert, URI responderURI,
+        List<X509Certificate> responderCerts, Date date)
+            throws IOException, CertPathValidatorException {
+
         CertId certId = null;
         try {
             X509CertImpl certImpl = X509CertImpl.toImpl(cert);
@@ -138,7 +163,7 @@
                 ("Exception while encoding OCSPRequest", ioe);
         }
         OCSPResponse ocspResponse = check(Collections.singletonList(certId),
-            responderURI, responderCert, date);
+            responderURI, responderCerts, date);
         return (RevocationStatus) ocspResponse.getSingleResponse(certId);
     }
 
@@ -147,7 +172,7 @@
      *
      * @param certs the CertIds to be checked
      * @param responderURI the URI of the OCSP responder
-     * @param responderCert the OCSP responder's certificate
+     * @param responderCerts the OCSP responder's certificates
      * @param date the time the validity of the OCSP responder's certificate
      *    should be checked against. If null, the current time is used.
      * @return the OCSPResponse
@@ -157,8 +182,8 @@
      *    encoding the OCSP Request or validating the OCSP Response
      */
     static OCSPResponse check(List<CertId> certIds, URI responderURI,
-        X509Certificate responderCert, Date date)
-        throws IOException, CertPathValidatorException {
+        List<X509Certificate> responderCerts, Date date)
+            throws IOException, CertPathValidatorException {
 
         byte[] bytes = null;
         try {
@@ -233,7 +258,7 @@
 
         OCSPResponse ocspResponse = null;
         try {
-            ocspResponse = new OCSPResponse(response, date, responderCert);
+            ocspResponse = new OCSPResponse(response, date, responderCerts);
         } catch (IOException ioe) {
             // response decoding exception
             throw new CertPathValidatorException(ioe);
@@ -291,7 +316,7 @@
 
         List<AccessDescription> descriptions = aia.getAccessDescriptions();
         for (AccessDescription description : descriptions) {
-            if (description.getAccessMethod().equals(
+            if (description.getAccessMethod().equals((Object)
                 AccessDescription.Ad_OCSP_Id)) {
 
                 GeneralName generalName = description.getAccessLocation();
--- a/src/share/classes/sun/security/provider/certpath/OCSPChecker.java	Thu Jun 14 14:29:49 2012 +0400
+++ b/src/share/classes/sun/security/provider/certpath/OCSPChecker.java	Wed Jun 20 19:35:47 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package sun.security.provider.certpath;
 
+import java.io.IOException;
 import java.math.BigInteger;
 import java.util.*;
 import java.security.AccessController;
@@ -190,7 +191,8 @@
         // (unless we're processing the final cert).
         X509Certificate issuerCert = null;
         boolean seekIssuerCert = true;
-        X509Certificate responderCert = null;
+        List<X509Certificate> responderCerts = new ArrayList<X509Certificate>();
+
         if (remainingCerts < certs.length) {
             issuerCert = certs[remainingCerts];
             seekIssuerCert = false; // done
@@ -198,7 +200,7 @@
             // By default, the OCSP responder's cert is the same as the
             // issuer of the cert being validated.
             if (!seekResponderCert) {
-                responderCert = issuerCert;
+                responderCerts.add(issuerCert);
                 if (DEBUG != null) {
                     DEBUG.println("Responder's certificate is the same " +
                         "as the issuer of the certificate being validated");
@@ -212,8 +214,8 @@
         if (seekIssuerCert || seekResponderCert) {
 
             if (DEBUG != null && seekResponderCert) {
-                DEBUG.println("Searching trust anchors for responder's " +
-                    "certificate");
+                DEBUG.println("Searching trust anchors for issuer or " +
+                    "responder certificate");
             }
 
             // Extract the anchor certs
@@ -226,6 +228,8 @@
 
             X500Principal certIssuerName =
                 currCertImpl.getIssuerX500Principal();
+            byte[] certIssuerKeyId = null;
+
             while (anchors.hasNext() && (seekIssuerCert || seekResponderCert)) {
 
                 TrustAnchor anchor = anchors.next();
@@ -242,13 +246,38 @@
                 if (seekIssuerCert &&
                     certIssuerName.equals(anchorSubjectName)) {
 
+                    // Retrieve the issuer's key identifier
+                    if (certIssuerKeyId == null) {
+                        certIssuerKeyId = currCertImpl.getIssuerKeyIdentifier();
+                        if (certIssuerKeyId == null) {
+                            if (DEBUG != null) {
+                                DEBUG.println("No issuer key identifier (AKID) "
+                                    + "in the certificate being validated");
+                            }
+                        }
+                    }
+
+                    // Check that the key identifiers match
+                    if (certIssuerKeyId != null &&
+                        !Arrays.equals(certIssuerKeyId, getKeyId(anchorCert))) {
+
+                        continue; // try next cert
+                    }
+
+                    if (DEBUG != null && certIssuerKeyId != null) {
+                        DEBUG.println("Issuer certificate key ID: " +
+                            String.format("0x%0" +
+                                (certIssuerKeyId.length * 2) + "x",
+                                    new BigInteger(1, certIssuerKeyId)));
+                    }
+
                     issuerCert = anchorCert;
                     seekIssuerCert = false; // done
 
                     // By default, the OCSP responder's cert is the same as
                     // the issuer of the cert being validated.
-                    if (!seekResponderCert && responderCert == null) {
-                        responderCert = anchorCert;
+                    if (!seekResponderCert && responderCerts.isEmpty()) {
+                        responderCerts.add(anchorCert);
                         if (DEBUG != null) {
                             DEBUG.println("Responder's certificate is the" +
                                 " same as the issuer of the certificate " +
@@ -271,8 +300,7 @@
                          responderSerialNumber.equals(
                          anchorCert.getSerialNumber()))) {
 
-                        responderCert = anchorCert;
-                        seekResponderCert = false; // done
+                        responderCerts.add(anchorCert);
                     }
                 }
             }
@@ -300,9 +328,10 @@
                 if (filter != null) {
                     List<CertStore> certStores = pkixParams.getCertStores();
                     for (CertStore certStore : certStores) {
-                        Iterator i = null;
                         try {
-                            i = certStore.getCertificates(filter).iterator();
+                            responderCerts.addAll(
+                                (Collection<X509Certificate>)
+                                    certStore.getCertificates(filter));
                         } catch (CertStoreException cse) {
                             // ignore and try next certStore
                             if (DEBUG != null) {
@@ -310,23 +339,23 @@
                             }
                             continue;
                         }
-                        if (i.hasNext()) {
-                            responderCert = (X509Certificate) i.next();
-                            seekResponderCert = false; // done
-                            break;
-                        }
                     }
                 }
             }
         }
 
         // Could not find the certificate identified in the OCSP properties
-        if (seekResponderCert) {
+        if (seekResponderCert && responderCerts.isEmpty()) {
             throw new CertPathValidatorException(
                 "Cannot find the responder's certificate " +
                 "(set using the OCSP security properties).");
         }
 
+        if (DEBUG != null) {
+            DEBUG.println("Located " + responderCerts.size() +
+                " trusted responder certificate(s)");
+        }
+
         // The algorithm constraints of the OCSP trusted responder certificate
         // does not need to be checked in this code. The constraints will be
         // checked when the responder's certificate is validated.
@@ -337,7 +366,7 @@
             certId = new CertId
                 (issuerCert, currCertImpl.getSerialNumberObject());
             response = OCSP.check(Collections.singletonList(certId), uri,
-                responderCert, pkixParams.getDate());
+                responderCerts, pkixParams.getDate());
         } catch (Exception e) {
             if (e instanceof CertPathValidatorException) {
                 throw (CertPathValidatorException) e;
@@ -353,14 +382,15 @@
         if (certStatus == RevocationStatus.CertStatus.REVOKED) {
             Throwable t = new CertificateRevokedException(
                 rs.getRevocationTime(), rs.getRevocationReason(),
-                responderCert.getSubjectX500Principal(),
+                responderCerts.get(0).getSubjectX500Principal(),
                 rs.getSingleExtensions());
             throw new CertPathValidatorException(t.getMessage(), t,
                 null, -1, BasicReason.REVOKED);
         } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {
             throw new CertPathValidatorException(
                 "Certificate's revocation status is unknown", null, cp,
-                remainingCerts, BasicReason.UNDETERMINED_REVOCATION_STATUS);
+                (remainingCerts - 1),
+                BasicReason.UNDETERMINED_REVOCATION_STATUS);
         }
     }
 
@@ -392,7 +422,7 @@
 
         List<AccessDescription> descriptions = aia.getAccessDescriptions();
         for (AccessDescription description : descriptions) {
-            if (description.getAccessMethod().equals(
+            if (description.getAccessMethod().equals((Object)
                 AccessDescription.Ad_OCSP_Id)) {
 
                 GeneralName generalName = description.getAccessLocation();
@@ -443,4 +473,34 @@
         }
         return hexNumber.toString();
     }
+
+    /*
+     * Returns the subject key identifier for the supplied certificate, or null
+     */
+    static byte[] getKeyId(X509Certificate cert) {
+        X509CertImpl certImpl = null;
+        byte[] certSubjectKeyId = null;
+
+        try {
+            certImpl = X509CertImpl.toImpl(cert);
+            certSubjectKeyId = certImpl.getSubjectKeyIdentifier();
+
+            if (certSubjectKeyId == null) {
+                if (DEBUG != null) {
+                    DEBUG.println("No subject key identifier (SKID) in the " +
+                        "certificate (Subject: " +
+                        cert.getSubjectX500Principal() + ")");
+                }
+            }
+
+        } catch (CertificateException e) {
+            // Ignore certificate
+            if (DEBUG != null) {
+                DEBUG.println("Error parsing X.509 certificate (Subject: " +
+                    cert.getSubjectX500Principal() + ") " + e);
+            }
+        }
+
+        return certSubjectKeyId;
+    }
 }
--- a/src/share/classes/sun/security/provider/certpath/OCSPRequest.java	Thu Jun 14 14:29:49 2012 +0400
+++ b/src/share/classes/sun/security/provider/certpath/OCSPRequest.java	Wed Jun 20 19:35:47 2012 +0100
@@ -75,7 +75,7 @@
 class OCSPRequest {
 
     private static final Debug debug = Debug.getInstance("certpath");
-    private static final boolean dump = false;
+    private static final boolean dump = debug.isOn("ocsp");
 
     // List of request CertIds
     private final List<CertId> certIds;
@@ -116,8 +116,8 @@
 
         if (dump) {
             HexDumpEncoder hexEnc = new HexDumpEncoder();
-            System.out.println("OCSPRequest bytes are... ");
-            System.out.println(hexEnc.encode(bytes));
+            debug.println("\nOCSPRequest bytes... ");
+            debug.println(hexEnc.encode(bytes) + "\n");
         }
 
         return bytes;
--- a/src/share/classes/sun/security/provider/certpath/OCSPResponse.java	Thu Jun 14 14:29:49 2012 +0400
+++ b/src/share/classes/sun/security/provider/certpath/OCSPResponse.java	Wed Jun 20 19:35:47 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,7 @@
 import java.security.cert.CRLReason;
 import java.security.cert.TrustAnchor;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -127,7 +128,7 @@
     private static ResponseStatus[] rsvalues = ResponseStatus.values();
 
     private static final Debug DEBUG = Debug.getInstance("certpath");
-    private static final boolean dump = false;
+    private static final boolean dump = DEBUG.isOn("ocsp");
     private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
         ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
     private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID =
@@ -158,14 +159,14 @@
      * Create an OCSP response from its ASN.1 DER encoding.
      */
     OCSPResponse(byte[] bytes, Date dateCheckedAgainst,
-        X509Certificate responderCert)
+        List<X509Certificate> responderCerts)
         throws IOException, CertPathValidatorException {
 
         // OCSPResponse
         if (dump) {
             HexDumpEncoder hexEnc = new HexDumpEncoder();
-            System.out.println("OCSPResponse bytes are...");
-            System.out.println(hexEnc.encode(bytes));
+            DEBUG.println("\nOCSPResponse bytes...");
+            DEBUG.println(hexEnc.encode(bytes) + "\n");
         }
         DerValue der = new DerValue(bytes);
         if (der.tag != DerValue.tag_Sequence) {
@@ -206,7 +207,7 @@
         // responseType
         derIn = tmp.data;
         ObjectIdentifier responseType = derIn.getOID();
-        if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
+        if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) {
             if (DEBUG != null) {
                 DEBUG.println("OCSP response type: basic");
             }
@@ -263,10 +264,16 @@
                 DEBUG.println("OCSP Responder name: " + responderName);
             }
         } else if (tag == KEY_TAG) {
-            // Ignore, for now
+            if (DEBUG != null) {
+                byte[] responderKeyId = seq.getOctetString();
+                DEBUG.println("OCSP Responder key ID: " +
+                    String.format("0x%0" +
+                        (responderKeyId.length * 2) + "x",
+                            new BigInteger(1, responderKeyId)));
+            }
         } else {
             throw new IOException("Bad encoding in responderID element of " +
-                "OCSP response: expected ASN.1 context specific tag 0 or 1");
+                "OCSP response: expected ASN.1 context specific tag 1 or 2");
         }
 
         // producedAt
@@ -301,7 +308,7 @@
                     if (DEBUG != null) {
                         DEBUG.println("OCSP extension: " + responseExtension);
                     }
-                    if (responseExtension.getExtensionId().equals(
+                    if (responseExtension.getExtensionId().equals((Object)
                         OCSP_NONCE_EXTENSION_OID)) {
                         /*
                         ocspNonce =
@@ -342,99 +349,147 @@
             }
         }
 
-        // Check whether the cert returned by the responder is trusted
+        X509Certificate trustedResponderCert = null;
+
+        // Check whether the signer cert returned by the responder is trusted
         if (x509Certs != null && x509Certs[0] != null) {
-            X509CertImpl cert = x509Certs[0];
+            X509CertImpl signerCert = x509Certs[0];
+
+            if (DEBUG != null) {
+                DEBUG.println("Signer certificate name: " +
+                    signerCert.getSubjectX500Principal());
 
-            // First check if the cert matches the responder cert which
-            // was set locally.
-            if (cert.equals(responderCert)) {
-                // cert is trusted, now verify the signed response
+                byte[] signerKeyId = signerCert.getSubjectKeyIdentifier();
+                if (signerKeyId != null) {
+                    DEBUG.println("Signer certificate key ID: " +
+                        String.format("0x%0" + (signerKeyId.length * 2) + "x",
+                                new BigInteger(1, signerKeyId)));
+                }
+            }
 
-            // Next check if the cert was issued by the responder cert
-            // which was set locally.
-            } else if (cert.getIssuerX500Principal().equals(
-                responderCert.getSubjectX500Principal())) {
+            byte[] certIssuerKeyId = null;
+
+            for (X509Certificate responderCert : responderCerts) {
+
+                // First check if signer cert matches a trusted responder cert
+                if (signerCert.equals(responderCert)) {
 
-                // Check for the OCSPSigning key purpose
-                try {
-                    List<String> keyPurposes = cert.getExtendedKeyUsage();
-                    if (keyPurposes == null ||
-                        !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
-                        throw new CertPathValidatorException(
-                            "Responder's certificate not valid for signing " +
-                            "OCSP responses");
+                    // signer cert is trusted, now verify the signed response
+                    trustedResponderCert = responderCert;
+                    if (DEBUG != null) {
+                        DEBUG.println("Signer certificate is a trusted " +
+                            "responder");
+                    }
+                    break;
+
+                // Next check if signer cert was issued by a trusted responder
+                // cert
+                } else if (signerCert.getIssuerX500Principal().equals(
+                    responderCert.getSubjectX500Principal())) {
+
+                    // Retrieve the issuer's key identifier
+                    if (certIssuerKeyId == null) {
+                        certIssuerKeyId = signerCert.getIssuerKeyIdentifier();
                     }
-                } catch (CertificateParsingException cpe) {
-                    // assume cert is not valid for signing
-                    throw new CertPathValidatorException(
-                        "Responder's certificate not valid for signing " +
-                        "OCSP responses", cpe);
-                }
+
+                    // Check that the key identifiers match
+                    if (certIssuerKeyId == null ||
+                        !Arrays.equals(certIssuerKeyId,
+                            OCSPChecker.getKeyId(responderCert))) {
+
+                        continue; // try next cert
+                    }
 
-                // Check algorithm constraints specified in security property
-                // "jdk.certpath.disabledAlgorithms".
-                AlgorithmChecker algChecker = new AlgorithmChecker(
-                                    new TrustAnchor(responderCert, null));
-                algChecker.init(false);
-                algChecker.check(cert, Collections.<String>emptySet());
+                    if (DEBUG != null) {
+                        DEBUG.println("Issuer certificate key ID: " +
+                            String.format("0x%0" +
+                                (certIssuerKeyId.length * 2) + "x",
+                                    new BigInteger(1, certIssuerKeyId)));
+                    }
+
+                    // Check for the OCSPSigning key purpose
+                    try {
+                        List<String> keyPurposes =
+                            signerCert.getExtendedKeyUsage();
+                        if (keyPurposes == null ||
+                            !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
 
-                // check the validity
-                try {
-                    if (dateCheckedAgainst == null) {
-                        cert.checkValidity();
-                    } else {
-                        cert.checkValidity(dateCheckedAgainst);
+                            continue; // try next cert
+                        }
+                    } catch (CertificateParsingException cpe) {
+
+                        continue; // try next cert
                     }
-                } catch (GeneralSecurityException e) {
-                    throw new CertPathValidatorException(
-                        "Responder's certificate not within the " +
-                        "validity period", e);
-                }
+
+                    // Check algorithm constraints specified in security
+                    // property "jdk.certpath.disabledAlgorithms".
+                    AlgorithmChecker algChecker = new AlgorithmChecker(
+                                        new TrustAnchor(responderCert, null));
+                    algChecker.init(false);
+                    algChecker.check(signerCert,
+                        Collections.<String>emptySet());
+
+                    // Check the date validity
+                    try {
+                        if (dateCheckedAgainst == null) {
+                            signerCert.checkValidity();
+                        } else {
+                            signerCert.checkValidity(dateCheckedAgainst);
+                        }
+                    } catch (GeneralSecurityException e) {
+                        if (DEBUG != null) {
+                            DEBUG.println("Responder's certificate not within" +
+                            " the validity period" + e);
+                        }
+                        continue; // try next cert
+                    }
 
-                // check for revocation
-                //
-                // A CA may specify that an OCSP client can trust a
-                // responder for the lifetime of the responder's
-                // certificate. The CA does so by including the
-                // extension id-pkix-ocsp-nocheck.
-                //
-                Extension noCheck =
-                    cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
-                if (noCheck != null) {
-                    if (DEBUG != null) {
-                        DEBUG.println("Responder's certificate includes " +
-                            "the extension id-pkix-ocsp-nocheck.");
+                    // Check for revocation
+                    //
+                    // A CA may specify that an OCSP client can trust a
+                    // responder for the lifetime of the responder's
+                    // certificate. The CA does so by including the
+                    // extension id-pkix-ocsp-nocheck.
+                    //
+                    Extension noCheck =
+                        signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
+                    if (noCheck != null) {
+                        if (DEBUG != null) {
+                            DEBUG.println("Responder's certificate includes " +
+                                "the extension id-pkix-ocsp-nocheck.");
+                        }
+                    } else {
+                        // we should do the revocation checking of the
+                        // authorized responder in a future update.
                     }
-                } else {
-                    // we should do the revocation checking of the
-                    // authorized responder in a future update.
-                }
 
-                // verify the signature
-                try {
-                    cert.verify(responderCert.getPublicKey());
-                    responderCert = cert;
-                    // cert is trusted, now verify the signed response
+                    // Verify the signature
+                    try {
+                        signerCert.verify(responderCert.getPublicKey());
+                        trustedResponderCert = signerCert;
+                        // cert is trusted, now verify the signed response
+                        if (DEBUG != null) {
+                            DEBUG.println("Signer certificate was issued by " +
+                                "a trusted responder");
+                        }
+                        break;
 
-                } catch (GeneralSecurityException e) {
-                    responderCert = null;
+                    } catch (GeneralSecurityException e) {
+                        trustedResponderCert = null;
+                    }
                 }
-            } else {
-                throw new CertPathValidatorException(
-                    "Responder's certificate is not authorized to sign " +
-                    "OCSP responses");
             }
         }
 
         // Confirm that the signed response was generated using the public
         // key from the trusted responder cert
-        if (responderCert != null) {
+        if (trustedResponderCert != null) {
             // Check algorithm constraints specified in security property
             // "jdk.certpath.disabledAlgorithms".
-            AlgorithmChecker.check(responderCert.getPublicKey(), sigAlgId);
+            AlgorithmChecker.check(trustedResponderCert.getPublicKey(),
+                sigAlgId);
 
-            if (!verifyResponse(responseDataDer, responderCert,
+            if (!verifyResponse(responseDataDer, trustedResponderCert,
                 sigAlgId, signature)) {
                 throw new CertPathValidatorException(
                     "Error verifying OCSP Responder's signature");
@@ -442,7 +497,8 @@
         } else {
             // Need responder's cert in order to verify the signature
             throw new CertPathValidatorException(
-                "Unable to verify OCSP Responder's signature");
+                "Responder's certificate is not trusted for signing " +
+                "OCSP responses");
         }
     }
 
--- a/src/share/classes/sun/security/x509/X509CertImpl.java	Thu Jun 14 14:29:49 2012 +0400
+++ b/src/share/classes/sun/security/x509/X509CertImpl.java	Wed Jun 20 19:35:47 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -174,6 +174,12 @@
      */
     private boolean verificationResult;
 
+    // Cached SKID
+    private byte[] subjectKeyId = null;
+
+    // Cached AKID
+    private byte[] issuerKeyId = null;
+
     /**
      * Default constructor.
      */
@@ -196,10 +202,7 @@
             parse(new DerValue(certData));
         } catch (IOException e) {
             signedCert = null;
-            CertificateException ce = new
-                CertificateException("Unable to initialize, " + e);
-            ce.initCause(e);
-            throw ce;
+            throw new CertificateException("Unable to initialize, " + e, e);
         }
     }
 
@@ -231,25 +234,19 @@
                 inBuffered.reset();
                 der = new DerValue(inBuffered);
             } catch (IOException ioe1) {
-                CertificateException ce = new
-                    CertificateException("Input stream must be " +
-                                         "either DER-encoded bytes " +
-                                         "or RFC1421 hex-encoded " +
-                                         "DER-encoded bytes: " +
-                                         ioe1.getMessage());
-                ce.initCause(ioe1);
-                throw ce;
+                throw new CertificateException("Input stream must be " +
+                                               "either DER-encoded bytes " +
+                                               "or RFC1421 hex-encoded " +
+                                               "DER-encoded bytes: " +
+                                               ioe1.getMessage(), ioe1);
             }
         }
         try {
             parse(der);
         } catch (IOException ioe) {
             signedCert = null;
-            CertificateException ce = new
-                CertificateException("Unable to parse DER value of " +
-                                     "certificate, " + ioe);
-            ce.initCause(ioe);
-            throw ce;
+            throw new CertificateException("Unable to parse DER value of " +
+                                           "certificate, " + ioe, ioe);
         }
     }
 
@@ -320,10 +317,7 @@
             parse(derVal);
         } catch (IOException e) {
             signedCert = null;
-            CertificateException ce = new
-                CertificateException("Unable to initialize, " + e);
-            ce.initCause(e);
-            throw ce;
+            throw new CertificateException("Unable to initialize, " + e, e);
         }
     }
 
@@ -1070,6 +1064,32 @@
     }
 
     /**
+     * Return the issuing authority's key identifier bytes, or null
+     */
+    public byte[] getIssuerKeyIdentifier()
+    {
+        if (issuerKeyId == null) {
+            AuthorityKeyIdentifierExtension aki =
+                getAuthorityKeyIdentifierExtension();
+            if (aki != null) {
+
+                try {
+                    issuerKeyId = ((KeyIdentifier)
+                        aki.get(AuthorityKeyIdentifierExtension.KEY_ID))
+                            .getIdentifier();
+                } catch (IOException e) {
+                    // should never happen (because KEY_ID attr is supported)
+                }
+
+            } else {
+                issuerKeyId = new byte[0]; // no AKID present
+            }
+        }
+
+        return issuerKeyId.length != 0 ? issuerKeyId : null;
+    }
+
+    /**
      * Get BasicConstraints extension
      * @return BasicConstraints object or null (if no such object in
      * certificate)
@@ -1169,6 +1189,32 @@
     }
 
     /**
+     * Returns the subject's key identifier bytes, or null
+     */
+    public byte[] getSubjectKeyIdentifier()
+    {
+        if (subjectKeyId == null) {
+            SubjectKeyIdentifierExtension ski =
+                getSubjectKeyIdentifierExtension();
+            if (ski != null) {
+
+                try {
+                    subjectKeyId = ((KeyIdentifier)
+                        ski.get(SubjectKeyIdentifierExtension.KEY_ID))
+                            .getIdentifier();
+                } catch (IOException e) {
+                    // should never happen (because KEY_ID attr is supported)
+                }
+
+            } else {
+                subjectKeyId = new byte[0]; // no SKID present
+            }
+        }
+
+        return subjectKeyId.length != 0 ? subjectKeyId : null;
+    }
+
+    /**
      * Get CRLDistributionPoints extension
      * @return CRLDistributionPoints object or null (if no such object in
      * certificate)
@@ -1279,7 +1325,7 @@
                 return null;
             } else {
                 for (Extension ex : extensions.getAllExtensions()) {
-                    if (ex.getExtensionId().equals(oid)) {
+                    if (ex.getExtensionId().equals((Object)oid)) {
                         //XXXX May want to consider cloning this
                         return ex;
                     }
@@ -1335,7 +1381,7 @@
 
                 for (Extension ex : exts.getAllExtensions()) {
                     ObjectIdentifier inCertOID = ex.getExtensionId();
-                    if (inCertOID.equals(findOID)) {
+                    if (inCertOID.equals((Object)findOID)) {
                         certExt = ex;
                         break;
                     }
@@ -1434,10 +1480,7 @@
                 new ExtendedKeyUsageExtension(Boolean.FALSE, data);
             return Collections.unmodifiableList(ekuExt.getExtendedKeyUsage());
         } catch (IOException ioe) {
-            CertificateParsingException cpe =
-                new CertificateParsingException();
-            cpe.initCause(ioe);
-            throw cpe;
+            throw new CertificateParsingException(ioe);
         }
     }
 
@@ -1578,8 +1621,8 @@
         }
         GeneralNames names;
         try {
-            names = (GeneralNames) subjectAltNameExt.get
-                (SubjectAlternativeNameExtension.SUBJECT_NAME);
+            names = (GeneralNames) subjectAltNameExt.get(
+                    SubjectAlternativeNameExtension.SUBJECT_NAME);
         } catch (IOException ioe) {
             // should not occur
             return Collections.<List<?>>emptySet();
@@ -1610,18 +1653,15 @@
 
             GeneralNames names;
             try {
-                names = (GeneralNames) subjectAltNameExt.get
-                    (SubjectAlternativeNameExtension.SUBJECT_NAME);
+                names = (GeneralNames) subjectAltNameExt.get(
+                        SubjectAlternativeNameExtension.SUBJECT_NAME);
             }  catch (IOException ioe) {
                 // should not occur
                 return Collections.<List<?>>emptySet();
             }
             return makeAltNames(names);
         } catch (IOException ioe) {
-            CertificateParsingException cpe =
-                new CertificateParsingException();
-            cpe.initCause(ioe);
-            throw cpe;
+            throw new CertificateParsingException(ioe);
         }
     }
 
@@ -1644,8 +1684,8 @@
         }
         GeneralNames names;
         try {
-            names = (GeneralNames) issuerAltNameExt.get
-                (IssuerAlternativeNameExtension.ISSUER_NAME);
+            names = (GeneralNames) issuerAltNameExt.get(
+                    IssuerAlternativeNameExtension.ISSUER_NAME);
         } catch (IOException ioe) {
             // should not occur
             return Collections.<List<?>>emptySet();
@@ -1676,18 +1716,15 @@
                                                     data);
             GeneralNames names;
             try {
-                names = (GeneralNames) issuerAltNameExt.get
-                    (IssuerAlternativeNameExtension.ISSUER_NAME);
+                names = (GeneralNames) issuerAltNameExt.get(
+                        IssuerAlternativeNameExtension.ISSUER_NAME);
             }  catch (IOException ioe) {
                 // should not occur
                 return Collections.<List<?>>emptySet();
             }
             return makeAltNames(names);
         } catch (IOException ioe) {
-            CertificateParsingException cpe =
-                new CertificateParsingException();
-            cpe.initCause(ioe);
-            throw cpe;
+            throw new CertificateParsingException(ioe);
         }
     }