8046321: OCSP Stapling for TLS
authorjnimeh
Wed, 05 Aug 2015 12:19:38 -0700 (2015-08-05)
changeset 32032 22badc53802f
parent 32031 687635f86b21
child 32033 bf24e33c7919
8046321: OCSP Stapling for TLS Summary: Initial feature commit for OCSP stapling in JSSE Reviewed-by: xuelei, mullan
jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPNonceExtension.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPRequest.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/ResponderId.java
jdk/src/java.base/share/classes/sun/security/ssl/CertStatusReqExtension.java
jdk/src/java.base/share/classes/sun/security/ssl/CertStatusReqItemV2.java
jdk/src/java.base/share/classes/sun/security/ssl/CertStatusReqListV2Extension.java
jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java
jdk/src/java.base/share/classes/sun/security/ssl/ExtensionType.java
jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java
jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java
jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java
jdk/src/java.base/share/classes/sun/security/ssl/OCSPStatusRequest.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
jdk/src/java.base/share/classes/sun/security/ssl/StatusRequest.java
jdk/src/java.base/share/classes/sun/security/ssl/StatusRequestType.java
jdk/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java
jdk/src/java.base/share/classes/sun/security/ssl/UnknownStatusRequest.java
jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java
jdk/src/java.base/share/classes/sun/security/validator/PKIXValidator.java
jdk/src/java.base/share/classes/sun/security/validator/SimpleValidator.java
jdk/src/java.base/share/classes/sun/security/validator/Validator.java
jdk/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java
jdk/test/java/security/testlibrary/CertificateBuilder.java
jdk/test/java/security/testlibrary/SimpleOCSPServer.java
jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java
jdk/test/javax/net/ssl/Stapling/HttpsUrlConnClient.java
jdk/test/javax/net/ssl/Stapling/SSLEngineWithStapling.java
jdk/test/javax/net/ssl/Stapling/SSLSocketWithStapling.java
jdk/test/sun/security/provider/certpath/Extensions/OCSPNonceExtensionTests.java
jdk/test/sun/security/provider/certpath/ResponderId/ResponderIdTests.java
jdk/test/sun/security/ssl/ExtensionType/OptimalListSize.java
jdk/test/sun/security/ssl/StatusStapling/BogusStatusRequest.java
jdk/test/sun/security/ssl/StatusStapling/CertStatusReqExtensionTests.java
jdk/test/sun/security/ssl/StatusStapling/CertStatusReqItemV2Tests.java
jdk/test/sun/security/ssl/StatusStapling/CertStatusReqListV2ExtensionTests.java
jdk/test/sun/security/ssl/StatusStapling/OCSPStatusRequestTests.java
jdk/test/sun/security/ssl/StatusStapling/StatusResponseManagerTests.java
jdk/test/sun/security/ssl/StatusStapling/TEST.properties
jdk/test/sun/security/ssl/StatusStapling/TestCase.java
jdk/test/sun/security/ssl/StatusStapling/TestUtils.java
--- a/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java	Wed Aug 05 12:19:38 2015 -0700
@@ -115,4 +115,45 @@
     public List<SNIServerName> getRequestedServerNames() {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * Returns a {@link List} containing DER-encoded OCSP responses
+     * (using the ASN.1 type OCSPResponse defined in RFC 6960) for
+     * the client to verify status of the server's certificate during
+     * handshaking.
+     *
+     * <P>
+     * This method only applies to certificate-based server
+     * authentication.  An {@link X509ExtendedTrustManager} will use the
+     * returned value for server certificate validation.
+     *
+     * @implSpec This method throws UnsupportedOperationException by default.
+     *         Classes derived from ExtendedSSLSession must implement
+     *         this method.
+     *
+     * @return a non-null unmodifiable list of byte arrays, each entry
+     *         containing a DER-encoded OCSP response (using the
+     *         ASN.1 type OCSPResponse defined in RFC 6960).  The order
+     *         of the responses must match the order of the certificates
+     *         presented by the server in its Certificate message (See
+     *         {@link SSLSession#getLocalCertificates()} for server mode,
+     *         and {@link SSLSession#getPeerCertificates()} for client mode).
+     *         It is possible that fewer response entries may be returned than
+     *         the number of presented certificates.  If an entry in the list
+     *         is a zero-length byte array, it should be treated by the
+     *         caller as if the OCSP entry for the corresponding certificate
+     *         is missing.  The returned list may be empty if no OCSP responses
+     *         were presented during handshaking or if OCSP stapling is not
+     *         supported by either endpoint for this handshake.
+     *
+     * @throws UnsupportedOperationException if the underlying provider
+     *         does not implement the operation
+     *
+     * @see X509ExtendedTrustManager
+     *
+     * @since 9
+     */
+    public List<byte[]> getStatusResponses() {
+        throw new UnsupportedOperationException();
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java	Wed Aug 05 12:19:38 2015 -0700
@@ -42,14 +42,13 @@
 import java.util.List;
 import java.util.Map;
 
-import static sun.security.provider.certpath.OCSPResponse.*;
 import sun.security.action.GetIntegerAction;
 import sun.security.util.Debug;
-import sun.security.util.ObjectIdentifier;
 import sun.security.x509.AccessDescription;
 import sun.security.x509.AuthorityInfoAccessExtension;
 import sun.security.x509.GeneralName;
 import sun.security.x509.GeneralNameInterface;
+import sun.security.x509.PKIXExtensions;
 import sun.security.x509.URIName;
 import sun.security.x509.X509CertImpl;
 
@@ -65,9 +64,6 @@
  */
 public final class OCSP {
 
-    static final ObjectIdentifier NONCE_EXTENSION_OID =
-        ObjectIdentifier.newInternal(new int[]{ 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});
-
     private static final Debug debug = Debug.getInstance("certpath");
 
     private static final int DEFAULT_CONNECT_TIMEOUT = 15000;
@@ -184,12 +180,15 @@
     /**
      * Checks the revocation status of a list of certificates using OCSP.
      *
-     * @param certs the CertIds to be checked
+     * @param certIds the CertIds to be checked
      * @param responderURI the URI of the OCSP responder
      * @param issuerCert the issuer's certificate
      * @param responderCert the OCSP responder's certificate
      * @param date the time the validity of the OCSP responder's certificate
      *    should be checked against. If null, the current time is used.
+     * @param extensions zero or more OCSP extensions to be included in the
+     *    request.  If no extensions are requested, an empty {@code List} must
+     *    be used.  A {@code null} value is not allowed.
      * @return the OCSPResponse
      * @throws IOException if there is an exception connecting to or
      *    communicating with the OCSP responder
@@ -202,19 +201,54 @@
                               List<Extension> extensions)
         throws IOException, CertPathValidatorException
     {
-        byte[] bytes = null;
-        OCSPRequest request = null;
+        byte[] nonce = null;
+        for (Extension ext : extensions) {
+            if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) {
+                nonce = ext.getValue();
+            }
+        }
+
+        OCSPResponse ocspResponse = null;
         try {
-            request = new OCSPRequest(certIds, extensions);
-            bytes = request.encodeBytes();
+            byte[] response = getOCSPBytes(certIds, responderURI, extensions);
+            ocspResponse = new OCSPResponse(response);
+
+            // verify the response
+            ocspResponse.verify(certIds, issuerCert, responderCert, date,
+                    nonce);
         } catch (IOException ioe) {
-            throw new CertPathValidatorException
-                ("Exception while encoding OCSPRequest", ioe);
+            throw new CertPathValidatorException(
+                "Unable to determine revocation status due to network error",
+                ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
         }
 
+        return ocspResponse;
+    }
+
+
+    /**
+     * Send an OCSP request, then read and return the OCSP response bytes.
+     *
+     * @param certIds the CertIds to be checked
+     * @param responderURI the URI of the OCSP responder
+     * @param extensions zero or more OCSP extensions to be included in the
+     *    request.  If no extensions are requested, an empty {@code List} must
+     *    be used.  A {@code null} value is not allowed.
+     *
+     * @return the OCSP response bytes
+     *
+     * @throws IOException if there is an exception connecting to or
+     *    communicating with the OCSP responder
+     */
+    public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI,
+            List<Extension> extensions) throws IOException {
+        OCSPRequest request = new OCSPRequest(certIds, extensions);
+        byte[] bytes = request.encodeBytes();
+
         InputStream in = null;
         OutputStream out = null;
         byte[] response = null;
+
         try {
             URL url = responderURI.toURL();
             if (debug != null) {
@@ -257,10 +291,6 @@
                 }
             }
             response = Arrays.copyOf(response, total);
-        } catch (IOException ioe) {
-            throw new CertPathValidatorException(
-                "Unable to determine revocation status due to network error",
-                ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
         } finally {
             if (in != null) {
                 try {
@@ -277,20 +307,7 @@
                 }
             }
         }
-
-        OCSPResponse ocspResponse = null;
-        try {
-            ocspResponse = new OCSPResponse(response);
-        } catch (IOException ioe) {
-            // response decoding exception
-            throw new CertPathValidatorException(ioe);
-        }
-
-        // verify the response
-        ocspResponse.verify(certIds, issuerCert, responderCert, date,
-            request.getNonce());
-
-        return ocspResponse;
+        return response;
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPNonceExtension.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.security.SecureRandom;
+
+import sun.security.x509.AttributeNameEnumeration;
+import sun.security.x509.CertAttrSet;
+import sun.security.x509.Extension;
+import sun.security.x509.PKIXExtensions;
+import sun.security.util.*;
+
+/**
+ * Represent the OCSP Nonce Extension.
+ * This extension, if present, provides a nonce value in OCSP requests
+ * and responses.  This will cryptographically bind requests and responses
+ * and help to prevent replay attacks (see RFC 6960, section 4.4.1).
+ *
+ * @see Extension
+ * @see CertAttrSet
+ */
+public class OCSPNonceExtension extends Extension
+implements CertAttrSet<String> {
+
+    /**
+     * Attribute name.
+     */
+    public static final String NAME = "OCSPNonce";
+    public static final String NONCE = "nonce";
+
+    private byte[] nonceData = null;
+    private String extensionName;
+
+    /**
+     * Encode this extension value to DER and assign it to the
+     * {@code extensionName} data member.
+     *
+     * @throws IOException if any errors occur during DER encoding
+     */
+    private void encodeInternal() throws IOException {
+        if (nonceData == null) {
+            this.extensionValue = null;
+            return;
+        }
+        DerOutputStream os = new DerOutputStream();
+        os.putOctetString(this.nonceData);
+        this.extensionValue = os.toByteArray();
+    }
+
+    /**
+     * Create a {@code OCSPNonceExtension} by providing the nonce length.
+     * The criticality is set to false.  The random bytes will be generated
+     * using the SUN provider.
+     *
+     * @param length the number of random bytes composing the nonce
+     *
+     * @throws IOException if any errors happen during encoding of the
+     *      extension.
+     */
+    public OCSPNonceExtension(int length) throws IOException {
+        this(PKIXExtensions.OCSPNonce_Id, false, length, NAME);
+    }
+
+    /**
+     * Creates the extension (also called by the subclass).
+     *
+     * @param extensionId the {@code ObjectIdentifier} for the OCSP Nonce
+     *      extension
+     * @param isCritical a boolean flag indicating if the criticality bit
+     *      is to be set for this extension
+     * @param length the length of the nonce in bytes
+     * @param extensionName the name of the extension
+     *
+     * @throws IOException if any errors happen during encoding of the
+     *      extension.
+     */
+    protected OCSPNonceExtension(ObjectIdentifier extensionId,
+            boolean isCritical, int length, String extensionName)
+            throws IOException {
+        SecureRandom rng = new SecureRandom();
+        this.nonceData = new byte[length];
+        rng.nextBytes(nonceData);
+        this.extensionId = extensionId;
+        this.critical = isCritical;
+        this.extensionName = extensionName;
+        encodeInternal();
+    }
+
+    /**
+     * Create the extension using the provided criticality bit setting and
+     * DER encoding.
+     *
+     * @param critical true if the extension is to be treated as critical.
+     * @param value an array of DER encoded bytes of the extnValue for the
+     *      extension.  It must not include the encapsulating OCTET STRING
+     *      tag and length.  For an {@code OCSPNonceExtension} the data value
+     *      should be a simple OCTET STRING containing random bytes
+     *      (see RFC 6960, section 4.4.1).
+     *
+     * @throws ClassCastException if value is not an array of bytes
+     * @throws IOException if any errors happen during encoding of the
+     *      extension
+     */
+    public OCSPNonceExtension(Boolean critical, Object value)
+            throws IOException {
+        this(PKIXExtensions.OCSPNonce_Id, critical, value, NAME);
+    }
+
+    /**
+     * Creates the extension (also called by the subclass).
+     *
+     * @param extensionId the {@code ObjectIdentifier} for the OCSP Nonce
+     *      extension
+     * @param critical a boolean flag indicating if the criticality bit
+     *      is to be set for this extension
+     * @param value an array of DER encoded bytes of the extnValue for the
+     *      extension.  It must not include the encapsulating OCTET STRING
+     *      tag and length.  For an {@code OCSPNonceExtension} the data value
+     *      should be a simple OCTET STRING containing random bytes
+     *      (see RFC 6960, section 4.4.1).
+     * @param extensionName the name of the extension
+     *
+     * @throws ClassCastException if value is not an array of bytes
+     * @throws IOException if any errors happen during encoding of the
+     *      extension
+     */
+    protected OCSPNonceExtension(ObjectIdentifier extensionId,
+            Boolean critical, Object value, String extensionName)
+            throws IOException {
+        this.extensionId = extensionId;
+        this.critical = critical;
+        this.extensionValue = (byte[]) value;
+        DerValue val = new DerValue(this.extensionValue);
+        this.nonceData = val.getOctetString();
+        this.extensionName = extensionName;
+    }
+
+    /**
+     * Set the attribute value.
+     *
+     * @param name the name of the attribute.
+     * @param obj an array of nonce bytes for the extension.  It must not
+     *      contain any DER tags or length.
+     *
+     * @throws IOException if an unsupported name is provided or the supplied
+     *      {@code obj} is not a byte array
+     */
+    @Override
+    public void set(String name, Object obj) throws IOException {
+        if (name.equalsIgnoreCase(NONCE)) {
+            if (!(obj instanceof byte[])) {
+                throw new IOException("Attribute must be of type byte[].");
+            }
+            nonceData = (byte[])obj;
+        } else {
+            throw new IOException("Attribute name not recognized by"
+                    + " CertAttrSet:" + extensionName + ".");
+        }
+        encodeInternal();
+    }
+
+    /**
+     * Get the attribute value.
+     *
+     * @param name the name of the attribute to retrieve.  Only "OCSPNonce"
+     *      is currently supported.
+     *
+     * @return an array of bytes that are the nonce data.  It will not contain
+     *      any DER tags or length, only the random nonce bytes.
+     *
+     * @throws IOException if an unsupported name is provided.
+     */
+    @Override
+    public Object get(String name) throws IOException {
+        if (name.equalsIgnoreCase(NONCE)) {
+            return nonceData;
+        } else {
+            throw new IOException("Attribute name not recognized by"
+                    + " CertAttrSet:" + extensionName + ".");
+        }
+    }
+
+    /**
+     * Delete the attribute value.
+     *
+     * @param name the name of the attribute to retrieve.  Only "OCSPNonce"
+     *      is currently supported.
+     *
+     * @throws IOException if an unsupported name is provided or an error
+     *      occurs during re-encoding of the extension.
+     */
+    @Override
+    public void delete(String name) throws IOException {
+        if (name.equalsIgnoreCase(NONCE)) {
+            nonceData = null;
+        } else {
+            throw new IOException("Attribute name not recognized by"
+                  + " CertAttrSet:" + extensionName + ".");
+        }
+        encodeInternal();
+    }
+
+    /**
+     * Returns a printable representation of the {@code OCSPNonceExtension}.
+     */
+    @Override
+    public String toString() {
+        String s = super.toString() + extensionName + ": " +
+                ((nonceData == null) ? "" : Debug.toString(nonceData))
+                + "\n";
+        return (s);
+    }
+
+    /**
+     * Write the extension to an {@code OutputStream}
+     *
+     * @param out the {@code OutputStream} to write the extension to.
+     *
+     * @throws IOException on encoding errors.
+     */
+    @Override
+    public void encode(OutputStream out) throws IOException {
+        encode(out, PKIXExtensions.OCSPNonce_Id, this.critical);
+    }
+
+    /**
+     * Write the extension to the DerOutputStream.
+     *
+     * @param out the {@code OutputStream} to write the extension to.
+     * @param extensionId the {@code ObjectIdentifier} used for this extension
+     * @param isCritical a flag indicating if the criticality bit is set for
+     *      this extension.
+     *
+     * @throws IOException on encoding errors.
+     */
+    protected void encode(OutputStream out, ObjectIdentifier extensionId,
+            boolean isCritical) throws IOException {
+
+        DerOutputStream tmp = new DerOutputStream();
+
+        if (this.extensionValue == null) {
+            this.extensionId = extensionId;
+            this.critical = isCritical;
+            encodeInternal();
+        }
+        super.encode(tmp);
+        out.write(tmp.toByteArray());
+    }
+
+    /**
+     * Return an enumeration of names of attributes existing within this
+     * attribute.
+     */
+    @Override
+    public Enumeration<String> getElements() {
+        AttributeNameEnumeration elements = new AttributeNameEnumeration();
+        elements.addElement(NONCE);
+        return (elements.elements());
+    }
+
+    /**
+     * Return the name of this attribute.
+     */
+    @Override
+    public String getName() {
+        return (extensionName);
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPRequest.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPRequest.java	Wed Aug 05 12:19:38 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, 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
@@ -32,10 +32,11 @@
 
 import sun.misc.HexDumpEncoder;
 import sun.security.util.*;
+import sun.security.x509.PKIXExtensions;
 
 /**
  * This class can be used to generate an OCSP request and send it over
- * an outputstream. Currently we do not support signing requests
+ * an output stream. Currently we do not support signing requests.
  * The OCSP Request is specified in RFC 2560 and
  * the ASN.1 definition is as follows:
  * <pre>
@@ -118,7 +119,8 @@
             DerOutputStream extOut = new DerOutputStream();
             for (Extension ext : extensions) {
                 ext.encode(extOut);
-                if (ext.getId().equals(OCSP.NONCE_EXTENSION_OID.toString())) {
+                if (ext.getId().equals(
+                        PKIXExtensions.OCSPNonce_Id.toString())) {
                     nonce = ext.getValue();
                 }
             }
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java	Wed Aug 05 12:19:38 2015 -0700
@@ -41,6 +41,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.security.auth.x500.X500Principal;
 
 import sun.misc.HexDumpEncoder;
@@ -129,7 +130,7 @@
         SIG_REQUIRED,          // Must sign the request
         UNAUTHORIZED           // Request unauthorized
     };
-    private static ResponseStatus[] rsvalues = ResponseStatus.values();
+    private static final ResponseStatus[] rsvalues = ResponseStatus.values();
 
     private static final Debug debug = Debug.getInstance("certpath");
     private static final boolean dump = debug != null && Debug.isOn("ocsp");
@@ -173,7 +174,7 @@
     }
 
     // an array of all of the CRLReasons (used in SingleResponse)
-    private static CRLReason[] values = CRLReason.values();
+    private static final CRLReason[] values = CRLReason.values();
 
     private final ResponseStatus responseStatus;
     private final Map<CertId, SingleResponse> singleResponseMap;
@@ -183,13 +184,16 @@
     private final byte[] responseNonce;
     private List<X509CertImpl> certs;
     private X509CertImpl signerCert = null;
-    private X500Principal responderName = null;
-    private KeyIdentifier responderKeyId = null;
+    private final ResponderId respId;
+    private Date producedAtDate = null;
+    private final Map<String, java.security.cert.Extension> responseExtensions;
 
     /*
      * Create an OCSP response from its ASN.1 DER encoding.
+     *
+     * @param bytes The DER-encoded bytes for an OCSP response
      */
-    OCSPResponse(byte[] bytes) throws IOException {
+    public OCSPResponse(byte[] bytes) throws IOException {
         if (dump) {
             HexDumpEncoder hexEnc = new HexDumpEncoder();
             debug.println("OCSPResponse bytes...\n\n" +
@@ -221,6 +225,8 @@
             signature = null;
             tbsResponseData = null;
             responseNonce = null;
+            responseExtensions = Collections.emptyMap();
+            respId = null;
             return;
         }
 
@@ -239,7 +245,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");
             }
@@ -289,27 +295,15 @@
         }
 
         // responderID
-        short tag = (byte)(seq.tag & 0x1f);
-        if (tag == NAME_TAG) {
-            responderName = new X500Principal(seq.getData().toByteArray());
-            if (debug != null) {
-                debug.println("Responder's name: " + responderName);
-            }
-        } else if (tag == KEY_TAG) {
-            responderKeyId = new KeyIdentifier(seq.getData().getOctetString());
-            if (debug != null) {
-                debug.println("Responder's key ID: " +
-                    Debug.toString(responderKeyId.getIdentifier()));
-            }
-        } else {
-            throw new IOException("Bad encoding in responderID element of " +
-                "OCSP response: expected ASN.1 context specific tag 0 or 1");
+        respId = new ResponderId(seq.toByteArray());
+        if (debug != null) {
+            debug.println("Responder ID: " + respId);
         }
 
         // producedAt
         seq = seqDerIn.getDerValue();
+        producedAtDate = seq.getGeneralizedTime();
         if (debug != null) {
-            Date producedAtDate = seq.getGeneralizedTime();
             debug.println("OCSP response produced at: " + producedAtDate);
         }
 
@@ -320,36 +314,29 @@
             debug.println("OCSP number of SingleResponses: "
                           + singleResponseDer.length);
         }
-        for (int i = 0; i < singleResponseDer.length; i++) {
-            SingleResponse singleResponse =
-                new SingleResponse(singleResponseDer[i]);
+        for (DerValue srDer : singleResponseDer) {
+            SingleResponse singleResponse = new SingleResponse(srDer);
             singleResponseMap.put(singleResponse.getCertId(), singleResponse);
         }
 
         // responseExtensions
-        byte[] nonce = null;
+        Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();
         if (seqDerIn.available() > 0) {
             seq = seqDerIn.getDerValue();
             if (seq.isContextSpecific((byte)1)) {
-                DerValue[] responseExtDer = seq.data.getSequence(3);
-                for (int i = 0; i < responseExtDer.length; i++) {
-                    Extension ext = new Extension(responseExtDer[i]);
-                    if (debug != null) {
-                        debug.println("OCSP extension: " + ext);
-                    }
-                    // Only the NONCE extension is recognized
-                    if (ext.getExtensionId().equals(OCSP.NONCE_EXTENSION_OID))
-                    {
-                        nonce = ext.getExtensionValue();
-                    } else if (ext.isCritical())  {
-                        throw new IOException(
-                            "Unsupported OCSP critical extension: " +
-                            ext.getExtensionId());
-                    }
-                }
+                tmpExtMap = parseExtensions(seq);
             }
         }
-        responseNonce = nonce;
+        responseExtensions = tmpExtMap;
+
+        // Attach the nonce value if found in the extension map
+        Extension nonceExt = (Extension)tmpExtMap.get(
+                PKIXExtensions.OCSPNonce_Id.toString());
+        responseNonce = (nonceExt != null) ?
+                nonceExt.getExtensionValue() : null;
+        if (debug != null && responseNonce != null) {
+            debug.println("Response nonce: " + Arrays.toString(responseNonce));
+        }
 
         // signatureAlgorithmId
         sigAlgId = AlgorithmId.parse(seqTmp[1]);
@@ -436,20 +423,22 @@
                     "Invalid issuer or trusted responder certificate", ce);
             }
 
-            if (responderName != null) {
+            if (respId.getType() == ResponderId.Type.BY_NAME) {
+                X500Principal rName = respId.getResponderName();
                 for (X509CertImpl cert : certs) {
-                    if (cert.getSubjectX500Principal().equals(responderName)) {
+                    if (cert.getSubjectX500Principal().equals(rName)) {
                         signerCert = cert;
                         break;
                     }
                 }
-            } else if (responderKeyId != null) {
+            } else if (respId.getType() == ResponderId.Type.BY_KEY) {
+                KeyIdentifier ridKeyId = respId.getKeyIdentifier();
                 for (X509CertImpl cert : certs) {
                     // Match responder's key identifier against the cert's SKID
                     // This will match if the SKID is encoded using the 160-bit
                     // SHA-1 hash method as defined in RFC 5280.
                     KeyIdentifier certKeyId = cert.getSubjectKeyId();
-                    if (certKeyId != null && responderKeyId.equals(certKeyId)) {
+                    if (certKeyId != null && ridKeyId.equals(certKeyId)) {
                         signerCert = cert;
                         break;
                     } else {
@@ -463,7 +452,7 @@
                         } catch (IOException e) {
                             // ignore
                         }
-                        if (responderKeyId.equals(certKeyId)) {
+                        if (ridKeyId.equals(certKeyId)) {
                             signerCert = cert;
                             break;
                         }
@@ -592,7 +581,6 @@
         }
 
         // Check freshness of OCSPResponse
-
         long now = (date == null) ? System.currentTimeMillis() : date.getTime();
         Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
         Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
@@ -603,16 +591,16 @@
                     until = " until " + sr.nextUpdate;
                 }
                 debug.println("OCSP response validity interval is from " +
-                              sr.thisUpdate + until);
+                        sr.thisUpdate + until);
                 debug.println("Checking validity of OCSP response on: " +
-                    new Date(now));
+                        new Date(now));
             }
 
             // Check that the test date is within the validity interval:
             //   [ thisUpdate - MAX_CLOCK_SKEW,
             //     MAX(thisUpdate, nextUpdate) + MAX_CLOCK_SKEW ]
             if (nowPlusSkew.before(sr.thisUpdate) ||
-                nowMinusSkew.after(
+                    nowMinusSkew.after(
                     sr.nextUpdate != null ? sr.nextUpdate : sr.thisUpdate))
             {
                 throw new CertPathValidatorException(
@@ -624,8 +612,10 @@
 
     /**
      * Returns the OCSP ResponseStatus.
+     *
+     * @return the {@code ResponseStatus} for this OCSP response
      */
-    ResponseStatus getResponseStatus() {
+    public ResponseStatus getResponseStatus() {
         return responseStatus;
     }
 
@@ -663,11 +653,27 @@
     /**
      * Returns the SingleResponse of the specified CertId, or null if
      * there is no response for that CertId.
+     *
+     * @param certId the {@code CertId} for a {@code SingleResponse} to be
+     * searched for in the OCSP response.
+     *
+     * @return the {@code SingleResponse} for the provided {@code CertId},
+     * or {@code null} if it is not found.
      */
-    SingleResponse getSingleResponse(CertId certId) {
+    public SingleResponse getSingleResponse(CertId certId) {
         return singleResponseMap.get(certId);
     }
 
+    /**
+     * Return a set of all CertIds in this {@code OCSPResponse}
+     *
+     * @return an unmodifiable set containing every {@code CertId} in this
+     *      response.
+     */
+    public Set<CertId> getCertIds() {
+        return Collections.unmodifiableSet(singleResponseMap.keySet());
+    }
+
     /*
      * Returns the certificate for the authority that signed the OCSP response.
      */
@@ -676,11 +682,52 @@
     }
 
     /**
+     * Get the {@code ResponderId} from this {@code OCSPResponse}
+     *
+     * @return the {@code ResponderId} from this response or {@code null}
+     *      if no responder ID is in the body of the response (e.g. a
+     *      response with a status other than SUCCESS.
+     */
+    public ResponderId getResponderId() {
+        return respId;
+    }
+
+    /**
+     * Provide a String representation of an OCSPResponse
+     *
+     * @return a human-readable representation of the OCSPResponse
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("OCSP Response:\n");
+        sb.append("Response Status: ").append(responseStatus).append("\n");
+        sb.append("Responder ID: ").append(respId).append("\n");
+        sb.append("Produced at: ").append(producedAtDate).append("\n");
+        int count = singleResponseMap.size();
+        sb.append(count).append(count == 1 ?
+                " response:\n" : " responses:\n");
+        for (SingleResponse sr : singleResponseMap.values()) {
+            sb.append(sr).append("\n");
+        }
+        if (responseExtensions != null && responseExtensions.size() > 0) {
+            count = responseExtensions.size();
+            sb.append(count).append(count == 1 ?
+                    " extension:\n" : " extensions:\n");
+            for (String extId : responseExtensions.keySet()) {
+                sb.append(responseExtensions.get(extId)).append("\n");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
      * Build a String-Extension map from DER encoded data.
      * @param derVal A {@code DerValue} object built from a SEQUENCE of
      *      extensions
      *
-     * @return A {@code Map} using the OID in string form as the keys.  If no
+     * @return a {@code Map} using the OID in string form as the keys.  If no
      *      extensions are found or an empty SEQUENCE is passed in, then
      *      an empty {@code Map} will be returned.
      *
@@ -694,6 +741,9 @@
 
         for (DerValue extDerVal : extDer) {
             Extension ext = new Extension(extDerVal);
+            if (debug != null) {
+                debug.println("Extension: " + ext);
+            }
             // We don't support any extensions yet. Therefore, if it
             // is critical we must throw an exception because we
             // don't know how to process it.
@@ -710,7 +760,7 @@
     /*
      * A class representing a single OCSP response.
      */
-    final static class SingleResponse implements OCSP.RevocationStatus {
+    public final static class SingleResponse implements OCSP.RevocationStatus {
         private final CertId certId;
         private final CertStatus certStatus;
         private final Date thisUpdate;
@@ -825,23 +875,72 @@
         /*
          * Return the certificate's revocation status code
          */
-        @Override public CertStatus getCertStatus() {
+        @Override
+        public CertStatus getCertStatus() {
             return certStatus;
         }
 
-        private CertId getCertId() {
+        /**
+         * Get the Cert ID that this SingleResponse is for.
+         *
+         * @return the {@code CertId} for this {@code SingleResponse}
+         */
+        public CertId getCertId() {
             return certId;
         }
 
-        @Override public Date getRevocationTime() {
+        /**
+         * Get the {@code thisUpdate} field from this {@code SingleResponse}.
+         *
+         * @return a {@link Date} object containing the thisUpdate date
+         */
+        public Date getThisUpdate() {
+            return (thisUpdate != null ? (Date) thisUpdate.clone() : null);
+        }
+
+        /**
+         * Get the {@code nextUpdate} field from this {@code SingleResponse}.
+         *
+         * @return a {@link Date} object containing the nexUpdate date or
+         * {@code null} if a nextUpdate field is not present in the response.
+         */
+        public Date getNextUpdate() {
+            return (nextUpdate != null ? (Date) nextUpdate.clone() : null);
+        }
+
+        /**
+         * Get the {@code revocationTime} field from this
+         * {@code SingleResponse}.
+         *
+         * @return a {@link Date} object containing the revocationTime date or
+         * {@code null} if the {@code SingleResponse} does not have a status
+         * of {@code REVOKED}.
+         */
+        @Override
+        public Date getRevocationTime() {
             return (revocationTime != null ? (Date) revocationTime.clone() :
                     null);
         }
 
-        @Override public CRLReason getRevocationReason() {
+        /**
+         * Get the {@code revocationReason} field for the
+         * {@code SingleResponse}.
+         *
+         * @return a {@link CRLReason} containing the revocation reason, or
+         * {@code null} if a revocation reason was not provided or the
+         * response status is not {@code REVOKED}.
+         */
+        @Override
+        public CRLReason getRevocationReason() {
             return revocationReason;
         }
 
+        /**
+         * Get the {@code singleExtensions} for this {@code SingleResponse}.
+         *
+         * @return a {@link Map} of {@link Extension} objects, keyed by
+         * their OID value in string form.
+         */
         @Override
         public Map<String, java.security.cert.Extension> getSingleExtensions() {
             return Collections.unmodifiableMap(singleExtensions);
@@ -852,19 +951,22 @@
          */
         @Override public String toString() {
             StringBuilder sb = new StringBuilder();
-            sb.append("SingleResponse:  \n");
+            sb.append("SingleResponse:\n");
             sb.append(certId);
-            sb.append("\nCertStatus: "+ certStatus + "\n");
+            sb.append("\nCertStatus: ").append(certStatus).append("\n");
             if (certStatus == CertStatus.REVOKED) {
-                sb.append("revocationTime is " + revocationTime + "\n");
-                sb.append("revocationReason is " + revocationReason + "\n");
+                sb.append("revocationTime is ");
+                sb.append(revocationTime).append("\n");
+                sb.append("revocationReason is ");
+                sb.append(revocationReason).append("\n");
             }
-            sb.append("thisUpdate is " + thisUpdate + "\n");
+            sb.append("thisUpdate is ").append(thisUpdate).append("\n");
             if (nextUpdate != null) {
-                sb.append("nextUpdate is " + nextUpdate + "\n");
+                sb.append("nextUpdate is ").append(nextUpdate).append("\n");
             }
             for (java.security.cert.Extension ext : singleExtensions.values()) {
-                sb.append("singleExtension: " + ext + "\n");
+                sb.append("singleExtension: ");
+                sb.append(ext.toString()).append("\n");
             }
             return sb.toString();
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/ResponderId.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.util.Arrays;
+import java.io.IOException;
+import java.security.PublicKey;
+import javax.security.auth.x500.X500Principal;
+import sun.security.x509.KeyIdentifier;
+import sun.security.util.DerValue;
+
+/**
+ * Class for ResponderId entities as described in RFC6960.  ResponderId objects
+ * are used to uniquely identify OCSP responders.
+ * <p>
+ * The RFC 6960 defines a ResponderID structure as:
+ * <pre>
+ * ResponderID ::= CHOICE {
+ *      byName              [1] Name,
+ *      byKey               [2] KeyHash }
+ *
+ * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
+ * (excluding the tag and length fields)
+ *
+ * Name is defined in RFC 5280.
+ * </pre>
+ *
+ * @see ResponderId.Type
+ * @since 1.9
+ */
+public final class ResponderId {
+
+    /**
+     * A {@code ResponderId} enumeration describing the accepted forms for a
+     * {@code ResponderId}.
+     *
+     * @see ResponderId
+     * @since 1.9
+     */
+    public static enum Type {
+        /**
+         * A BY_NAME {@code ResponderId} will be built from a subject name,
+         * either as an {@code X500Principal} or a DER-encoded byte array.
+         */
+        BY_NAME(1, "byName"),
+
+        /**
+         * A BY_KEY {@code ResponderId} will be built from a public key
+         * identifier, either derived from a {@code PublicKey} or directly
+         * from a DER-encoded byte array containing the key identifier.
+         */
+        BY_KEY(2, "byKey");
+
+        private final int tagNumber;
+        private final String ridTypeName;
+
+        private Type(int value, String name) {
+            this.tagNumber = value;
+            this.ridTypeName = name;
+        }
+
+        public int value() {
+            return tagNumber;
+        }
+
+        @Override
+        public String toString() {
+            return ridTypeName;
+        }
+    }
+
+    private Type type;
+    private X500Principal responderName;
+    private KeyIdentifier responderKeyId;
+    private byte[] encodedRid;
+
+    /**
+     * Constructs a {@code ResponderId} object using an {@code X500Principal}.
+     * When encoded in DER this object will use the BY_NAME option.
+     *
+     * @param subjectName the subject name of the certificate used
+     * to sign OCSP responses.
+     *
+     * @throws IOException if the internal DER-encoding of the
+     *      {@code X500Principal} fails.
+     */
+    public ResponderId(X500Principal subjectName) throws IOException {
+        responderName = subjectName;
+        responderKeyId = null;
+        encodedRid = principalToBytes();
+        type = Type.BY_NAME;
+    }
+
+    /**
+     * Constructs a {@code ResponderId} object using a {@code PublicKey}.
+     * When encoded in DER this object will use the byKey option, a
+     * SHA-1 hash of the responder's public key.
+     *
+     * @param pubKey the the OCSP responder's public key
+     *
+     * @throws IOException if the internal DER-encoding of the
+     *      {@code KeyIdentifier} fails.
+     */
+    public ResponderId(PublicKey pubKey) throws IOException {
+        responderKeyId = new KeyIdentifier(pubKey);
+        responderName = null;
+        encodedRid = keyIdToBytes();
+        type = Type.BY_KEY;
+    }
+
+    /**
+     * Constructs a {@code ResponderId} object from its DER-encoding.
+     *
+     * @param encodedData the DER-encoded bytes
+     *
+     * @throws IOException if the encodedData is not properly DER encoded
+     */
+    public ResponderId(byte[] encodedData) throws IOException {
+        DerValue outer = new DerValue(encodedData);
+
+        if (outer.isContextSpecific((byte)Type.BY_NAME.value())
+                && outer.isConstructed()) {
+            // Use the X500Principal constructor as a way to sanity
+            // check the incoming data.
+            responderName = new X500Principal(outer.getDataBytes());
+            encodedRid = principalToBytes();
+            type = Type.BY_NAME;
+        } else if (outer.isContextSpecific((byte)Type.BY_KEY.value())
+                && outer.isConstructed()) {
+            // Use the KeyIdentifier constructor as a way to sanity
+            // check the incoming data.
+            responderKeyId =
+                new KeyIdentifier(new DerValue(outer.getDataBytes()));
+            encodedRid = keyIdToBytes();
+            type = Type.BY_KEY;
+        } else {
+            throw new IOException("Invalid ResponderId content");
+        }
+    }
+
+    /**
+     * Encode a {@code ResponderId} in DER form
+     *
+     * @return a byte array containing the DER-encoded representation for this
+     *      {@code ResponderId}
+     */
+    public byte[] getEncoded() {
+        return encodedRid.clone();
+    }
+
+    /**
+     * Return the type of {@ResponderId}
+     *
+     * @return a number corresponding to the context-specific tag number
+     *      used in the DER-encoding for a {@code ResponderId}
+     */
+    public ResponderId.Type getType() {
+        return type;
+    }
+
+    /**
+     * Get the length of the encoded {@code ResponderId} (including the tag and
+     * length of the explicit tagging from the outer ASN.1 CHOICE).
+     *
+     * @return the length of the encoded {@code ResponderId}
+     */
+    public int length() {
+        return encodedRid.length;
+    }
+
+    /**
+     * Obtain the underlying {@code X500Principal} from a {@code ResponderId}
+     *
+     * @return the {@code X500Principal} for this {@code ResponderId} if it
+     *      is a BY_NAME variant.  If the {@code ResponderId} is a BY_KEY
+     *      variant, this routine will return {@code null}.
+     */
+    public X500Principal getResponderName() {
+        return responderName;
+    }
+
+    /**
+     * Obtain the underlying key identifier from a {@code ResponderId}
+     *
+     * @return the {@code KeyIdentifier} for this {@code ResponderId} if it
+     *      is a BY_KEY variant.  If the {@code ResponderId} is a BY_NAME
+     *      variant, this routine will return {@code null}.
+     */
+    public KeyIdentifier getKeyIdentifier() {
+        return responderKeyId;
+    }
+
+    /**
+     * Compares the specified object with this {@code ResponderId} for equality.
+     * A ResponderId will only be considered equivalent if both the type and
+     * data value are equal.  Two ResponderIds initialized by name and
+     * key ID, respectively, will not be equal even if the
+     * ResponderId objects are created from the same source certificate.
+     *
+     * @param obj the object to be compared against
+     *
+     * @return true if the specified object is equal to this {@code Responderid}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof ResponderId) {
+            ResponderId respObj = (ResponderId)obj;
+                return Arrays.equals(encodedRid, respObj.getEncoded());
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the hash code value for this {@code ResponderId}
+     *
+     * @return the hash code value for this {@code ResponderId}
+     */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(encodedRid);
+    }
+
+    /**
+     * Create a String representation of this {@code ResponderId}
+     *
+     * @return a String representation of this {@code ResponderId}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        switch (type) {
+            case BY_NAME:
+                sb.append(type).append(": ").append(responderName);
+                break;
+            case BY_KEY:
+                sb.append(type).append(": ");
+                for (byte keyIdByte : responderKeyId.getIdentifier()) {
+                    sb.append(String.format("%02X", keyIdByte));
+                }
+                break;
+            default:
+                sb.append("Unknown ResponderId Type: ").append(type);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Convert the responderName data member into its DER-encoded form
+     *
+     * @return the DER encoding for a responder ID byName option, including
+     *      explicit context-specific tagging.
+     *
+     * @throws IOException if any encoding error occurs
+     */
+    private byte[] principalToBytes() throws IOException {
+        DerValue dv = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
+                true, (byte)Type.BY_NAME.value()),
+                responderName.getEncoded());
+        return dv.toByteArray();
+    }
+
+    /**
+     * Convert the responderKeyId data member into its DER-encoded form
+     *
+     * @return the DER encoding for a responder ID byKey option, including
+     *      explicit context-specific tagging.
+     *
+     * @throws IOException if any encoding error occurs
+     */
+    private byte[] keyIdToBytes() throws IOException {
+        // Place the KeyIdentifier bytes into an OCTET STRING
+        DerValue inner = new DerValue(DerValue.tag_OctetString,
+                responderKeyId.getIdentifier());
+
+        // Mark the OCTET STRING-wrapped KeyIdentifier bytes
+        // as EXPLICIT CONTEXT 2
+        DerValue outer = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
+                true, (byte)Type.BY_KEY.value()), inner.toByteArray());
+
+        return outer.toByteArray();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CertStatusReqExtension.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/*
+ * RFC6066 defines the TLS extension,"status_request" (type 0x5),
+ * which allows the client to request that the server perform OCSP
+ * on the client's behalf.
+ * The "extension data" field of this extension contains a
+ * "CertificateStatusRequest" structure:
+ *
+ *      struct {
+ *          CertificateStatusType status_type;
+ *          select (status_type) {
+ *              case ocsp: OCSPStatusRequest;
+ *          } request;
+ *      } CertificateStatusRequest;
+ *
+ *      enum { ocsp(1), (255) } CertificateStatusType;
+ *
+ *      struct {
+ *          ResponderID responder_id_list<0..2^16-1>;
+ *          Extensions  request_extensions;
+ *      } OCSPStatusRequest;
+ *
+ *      opaque ResponderID<1..2^16-1>;
+ *      opaque Extensions<0..2^16-1>;
+ */
+
+final class CertStatusReqExtension extends HelloExtension {
+
+    private final StatusRequestType statReqType;
+    private final StatusRequest request;
+
+
+    /**
+     * Construct the default status request extension object.  The default
+     * object results in a status_request extension where the extension
+     * data segment is zero-length.  This is used primarily in ServerHello
+     * messages where the server asserts it can do RFC 6066 status stapling.
+     */
+    CertStatusReqExtension() {
+        super(ExtensionType.EXT_STATUS_REQUEST);
+        statReqType = null;
+        request = null;
+    }
+
+    /**
+     * Construct the status request extension object given a request type
+     *      and {@code StatusRequest} object.
+     *
+     * @param reqType a {@code StatusRequestExtType object correspoding
+     *      to the underlying {@code StatusRequest} object.  A value of
+     *      {@code null} is not allowed.
+     * @param statReq the {@code StatusRequest} object used to provide the
+     *      encoding for the TLS extension.  A value of {@code null} is not
+     *      allowed.
+     *
+     * @throws IllegalArgumentException if the provided {@code StatusRequest}
+     *      does not match the type.
+     * @throws NullPointerException if either the {@code reqType} or
+     *      {@code statReq} arguments are {@code null}.
+     */
+    CertStatusReqExtension(StatusRequestType reqType, StatusRequest statReq) {
+        super(ExtensionType.EXT_STATUS_REQUEST);
+
+        statReqType = Objects.requireNonNull(reqType,
+                "Unallowed null value for status_type");
+        request = Objects.requireNonNull(statReq,
+                "Unallowed null value for request");
+
+        // There is currently only one known status type (OCSP)
+        // We can add more clauses to cover other types in the future
+        if (statReqType == StatusRequestType.OCSP) {
+            if (!(statReq instanceof OCSPStatusRequest)) {
+                throw new IllegalArgumentException("StatusRequest not " +
+                        "of type OCSPStatusRequest");
+            }
+        }
+    }
+
+    /**
+     * Construct the {@code CertStatusReqExtension} object from data read from
+     *      a {@code HandshakeInputStream}
+     *
+     * @param s the {@code HandshakeInputStream} providing the encoded data
+     * @param len the length of the extension data
+     *
+     * @throws IOException if any decoding errors happen during object
+     *      construction.
+     */
+    CertStatusReqExtension(HandshakeInStream s, int len) throws IOException {
+        super(ExtensionType.EXT_STATUS_REQUEST);
+
+        if (len > 0) {
+            // Obtain the status type (first byte)
+            statReqType = StatusRequestType.get(s.getInt8());
+            if (statReqType == StatusRequestType.OCSP) {
+                request = new OCSPStatusRequest(s);
+            } else {
+                // This is a status_type we don't understand.  Create
+                // an UnknownStatusRequest in order to preserve the data
+                request = new UnknownStatusRequest(s, len - 1);
+            }
+        } else {
+            // Treat this as a zero-length extension (i.e. from a ServerHello
+            statReqType = null;
+            request = null;
+        }
+    }
+
+    /**
+     * Return the length of the encoded extension, including extension type,
+     *      extension length and status_type fields.
+     *
+     * @return the length in bytes, including the extension type and
+     *      length fields.
+     */
+    @Override
+    int length() {
+        return (statReqType != null ? 5 + request.length() : 4);
+    }
+
+    /**
+     * Send the encoded TLS extension through a {@code HandshakeOutputStream}
+     *
+     * @param s the {@code HandshakeOutputStream} used to send the encoded data
+     *
+     * @throws IOException tf any errors occur during the encoding process
+     */
+    @Override
+    void send(HandshakeOutStream s) throws IOException {
+        s.putInt16(type.id);
+        s.putInt16(this.length() - 4);
+
+        if (statReqType != null) {
+            s.putInt8(statReqType.id);
+            request.send(s);
+        }
+    }
+
+    /**
+     * Create a string representation of this {@code CertStatusReqExtension}
+     *
+     * @return the string representation of this {@code CertStatusReqExtension}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("Extension ").append(type);
+        if (statReqType != null) {
+            sb.append(": ").append(statReqType).append(", ").append(request);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Return the type field for this {@code CertStatusReqExtension}
+     *
+     * @return the {@code StatusRequestType} for this extension.  {@code null}
+     *      will be returned if the default constructor is used to create
+     *      a zero length status_request extension (found in ServerHello
+     *      messages)
+     */
+    StatusRequestType getType() {
+        return statReqType;
+    }
+
+    /**
+     * Get the underlying {@code StatusRequest} for this
+     *      {@code CertStatusReqExtension}
+     *
+     * @return the {@code StatusRequest} or {@code null} if the default
+     * constructor was used to create this extension.
+     */
+    StatusRequest getRequest() {
+        return request;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CertStatusReqItemV2.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import javax.net.ssl.SSLException;
+
+/*
+ * RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),
+ * which allows the client to request that the server perform OCSP
+ * on the client's behalf.
+ *
+ * The RFC defines an CertStatusReqItemV2 structure:
+ *
+ *      struct {
+ *          CertificateStatusType status_type;
+ *          uint16 request_length;
+ *          select (status_type) {
+ *              case ocsp: OCSPStatusRequest;
+ *              case ocsp_multi: OCSPStatusRequest;
+ *          } request;
+ *      } CertificateStatusRequestItemV2;
+ *
+ *      enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+ */
+
+final class CertStatusReqItemV2 implements StatusRequest {
+
+    private final StatusRequestType statReqType;
+    private final StatusRequest request;
+
+    /**
+     * Construct a {@code CertStatusReqItemV2} object using a type value
+     *      and empty ResponderId and Extension lists.
+     *
+     * @param reqType the type of request (e.g. ocsp).  A {@code null} value
+     *      is not allowed.
+     * @param statReq the {@code StatusRequest} object used to provide the
+     *      encoding for this {@code CertStatusReqItemV2}.  A {@code null}
+     *      value is not allowed.
+     *
+     * @throws IllegalArgumentException if the provided {@code StatusRequest}
+     *      does not match the type.
+     * @throws NullPointerException if either the reqType or statReq arguments
+     *      are {@code null}.
+     */
+    CertStatusReqItemV2(StatusRequestType reqType, StatusRequest statReq) {
+        statReqType = Objects.requireNonNull(reqType,
+                "Unallowed null value for status_type");
+        request = Objects.requireNonNull(statReq,
+                "Unallowed null value for request");
+
+        // There is currently only one known status type (OCSP)
+        // We can add more clauses to cover other types in the future
+        if (statReqType.equals(StatusRequestType.OCSP) ||
+                statReqType.equals(StatusRequestType.OCSP_MULTI)) {
+            if (!(statReq instanceof OCSPStatusRequest)) {
+                throw new IllegalArgumentException("StatusRequest not " +
+                        "of type OCSPStatusRequest");
+            }
+        }
+    }
+
+    /**
+     * Construct a {@code CertStatusReqItemV2} object from encoded bytes
+     *
+     * @param requestBytes the encoded bytes for the {@code CertStatusReqItemV2}
+     *
+     * @throws IOException if any decoding errors take place
+     * @throws IllegalArgumentException if the parsed reqType value is not a
+     *      supported status request type.
+     */
+    CertStatusReqItemV2(byte[] reqItemBytes) throws IOException {
+        ByteBuffer reqBuf = ByteBuffer.wrap(reqItemBytes);
+        statReqType = StatusRequestType.get(reqBuf.get());
+        int requestLength = Short.toUnsignedInt(reqBuf.getShort());
+
+        if (requestLength == reqBuf.remaining()) {
+            byte[] statReqBytes = new byte[requestLength];
+            reqBuf.get(statReqBytes);
+            if (statReqType == StatusRequestType.OCSP ||
+                    statReqType == StatusRequestType.OCSP_MULTI) {
+                request = new OCSPStatusRequest(statReqBytes);
+            } else {
+                request = new UnknownStatusRequest(statReqBytes);
+            }
+        } else {
+            throw new SSLException("Incorrect request_length: " +
+                    "Expected " + reqBuf.remaining() + ", got " +
+                    requestLength);
+        }
+    }
+
+    /**
+     * Construct an {@code CertStatusReqItemV2} object from data read from
+     * a {@code HandshakeInputStream}
+     *
+     * @param s the {@code HandshakeInputStream} providing the encoded data
+     *
+     * @throws IOException if any decoding errors happen during object
+     *      construction.
+     * @throws IllegalArgumentException if the parsed reqType value is not a
+     *      supported status request type.
+     */
+    CertStatusReqItemV2(HandshakeInStream in) throws IOException {
+        statReqType = StatusRequestType.get(in.getInt8());
+        int requestLength = in.getInt16();
+
+        if (statReqType == StatusRequestType.OCSP ||
+                statReqType == StatusRequestType.OCSP_MULTI) {
+            request = new OCSPStatusRequest(in);
+        } else {
+            request = new UnknownStatusRequest(in, requestLength);
+        }
+    }
+
+    /**
+     * Return the length of this {@code CertStatusReqItemV2} in its encoded form
+     *
+     * @return the encoded length of this {@code CertStatusReqItemV2}
+     */
+    @Override
+    public int length() {
+        // The length is the the status type (1 byte) + the request length
+        // field (2 bytes) + the StatusRequest data length.
+        return request.length() + 3;
+    }
+
+    /**
+     * Send the encoded {@code CertStatusReqItemV2} through a
+     *      {@code HandshakeOutputStream}
+     *
+     * @param s the {@code HandshakeOutputStream} used to send the encoded data
+     *
+     * @throws IOException if any errors occur during the encoding process
+     */
+    @Override
+    public void send(HandshakeOutStream s) throws IOException {
+        s.putInt8(statReqType.id);
+        s.putInt16(request.length());
+        request.send(s);
+    }
+
+    /**
+     * Create a string representation of this {@code CertStatusReqItemV2}
+     *
+     * @return the string representation of this {@code CertStatusReqItemV2}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("CertStatusReqItemV2: ").append(statReqType).append(", ");
+        sb.append(request.toString());
+
+        return sb.toString();
+    }
+
+    /**
+     * Return the type field for this {@code CertStatusReqItemV2}
+     *
+     * @return the {@code StatusRequestType} for this extension.
+     */
+    StatusRequestType getType() {
+        return statReqType;
+    }
+
+    /**
+     * Get the underlying {@code StatusRequest} for this
+     *      {@code CertStatusReqItemV2}
+     *
+     * @return the {@code StatusRequest}
+     */
+    StatusRequest getRequest() {
+        return request;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CertStatusReqListV2Extension.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Objects;
+import javax.net.ssl.SSLException;
+
+/*
+ * RFC6066 defines the TLS extension,"status_request" (type 0x5),
+ * which allows the client to request that the server perform OCSP
+ * on the client's behalf.
+ * The "extension data" field of this extension contains a
+ * "CertificateStatusRequest" structure:
+ *
+ *      struct {
+ *          CertificateStatusType status_type;
+ *          select (status_type) {
+ *              case ocsp: OCSPStatusRequest;
+ *          } request;
+ *      } CertificateStatusRequest;
+ *
+ *      enum { ocsp(1), (255) } CertificateStatusType;
+ *
+ *      struct {
+ *          ResponderID responder_id_list<0..2^16-1>;
+ *          Extensions  request_extensions;
+ *      } OCSPStatusRequest;
+ *
+ *      opaque ResponderID<1..2^16-1>;
+ *      opaque Extensions<0..2^16-1>;
+ */
+
+final class CertStatusReqListV2Extension extends HelloExtension {
+
+    private final List<CertStatusReqItemV2> itemList;
+    private final int itemListLength;
+
+    /**
+     * Construct a default {@code CertStatusReqListV2Extension}.  The default
+     * object results in a status_request_v2 extension where the extension
+     * data segment is zero-length.  This is used primarily in ServerHello
+     * messages where the server asserts it can do RFC 6961 status stapling.
+     */
+    CertStatusReqListV2Extension() {
+        super(ExtensionType.EXT_STATUS_REQUEST_V2);
+        itemList = Collections.emptyList();
+        itemListLength = 0;
+    }
+
+    /**
+     * Construct a {@code CertStatusReqListV2Extension} from a provided list
+     *      of {@code CertStatusReqItemV2} objects.
+     *
+     * @param reqList a {@code List} containing one or more
+     *      {@code CertStatusReqItemV2} objects to be included in this TLS
+     *      Hello extension.  Passing an empty list will result in the encoded
+     *      extension having a zero-length extension_data segment, and is
+     *      the same as using the default constructor.
+     *
+     * @throws NullPointerException if reqList is {@code null}
+     */
+    CertStatusReqListV2Extension(List<CertStatusReqItemV2> reqList) {
+        super(ExtensionType.EXT_STATUS_REQUEST_V2);
+        Objects.requireNonNull(reqList,
+                "Unallowed null value for certificate_status_req_list");
+        itemList = Collections.unmodifiableList(new ArrayList<>(reqList));
+        itemListLength = calculateListLength();
+    }
+
+    /**
+     *  Construct the {@code CertStatusReqListV2Extension} object from data
+     *      read from a {@code HandshakeInputStream}
+     *
+     * @param s the {@code HandshakeInputStream} providing the encoded data
+     * @param len the length of the extension data
+     *
+     * @throws IOException if any decoding errors happen during object
+     *      construction.
+     */
+    CertStatusReqListV2Extension(HandshakeInStream s, int len)
+            throws IOException {
+        super(ExtensionType.EXT_STATUS_REQUEST_V2);
+
+        if (len <= 0) {
+            // Handle the empty extension data case (from a ServerHello)
+            itemList = Collections.emptyList();
+            itemListLength = 0;
+        } else {
+            List<CertStatusReqItemV2> workingList = new ArrayList<>();
+
+            itemListLength = s.getInt16();
+            if (itemListLength <= 0) {
+                throw new SSLException("certificate_status_req_list length " +
+                        "must be greater than zero (received length: " +
+                        itemListLength + ")");
+            }
+
+            int totalRead = 0;
+            CertStatusReqItemV2 reqItem;
+            do {
+                reqItem = new CertStatusReqItemV2(s);
+                totalRead += reqItem.length();
+            } while (workingList.add(reqItem) && totalRead < itemListLength);
+
+            // If for some reason the add returns false, we may not have read
+            // all the necessary bytes from the stream.  Check this and throw
+            // an exception if we terminated the loop early.
+            if (totalRead != itemListLength) {
+                throw new SSLException("Not all certificate_status_req_list " +
+                        "bytes were read: expected " + itemListLength +
+                        ", read " + totalRead);
+            }
+
+            itemList = Collections.unmodifiableList(workingList);
+        }
+    }
+
+    /**
+     * Get the list of {@code CertStatusReqItemV2} objects for this extension
+     *
+     * @return an unmodifiable list of {@code CertStatusReqItemV2} objects
+     */
+    List<CertStatusReqItemV2> getRequestItems() {
+        return itemList;
+    }
+
+    /**
+     * Return the length of the encoded extension, including extension type
+     *      and extension length fields.
+     *
+     * @return the length in bytes, including the extension type and
+     *      extension_data length.
+     */
+    @Override
+    int length() {
+        return (itemList.isEmpty() ? 4 : itemListLength + 6);
+    }
+
+    /**
+     * Send the encoded {@code CertStatusReqListV2Extension} through a
+     *      {@code HandshakeOutputStream}
+     *
+     * @param s the {@code HandshakeOutputStream} used to send the encoded data
+     *
+     * @throws IOException if any errors occur during the encoding process
+     */
+    @Override
+    void send(HandshakeOutStream s) throws IOException {
+        s.putInt16(type.id);
+        s.putInt16(this.length() - 4);
+        if (itemListLength > 0) {
+            s.putInt16(itemListLength);
+            for (CertStatusReqItemV2 item : itemList) {
+                item.send(s);
+            }
+        }
+    }
+
+    /**
+     * Create a string representation of this
+     *      {@code CertStatusReqListV2Extension}
+     *
+     * @return the string representation of this
+     *      {@code CertStatusReqListV2Extension}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("Extension ").append(type);
+        for (CertStatusReqItemV2 item : itemList) {
+            sb.append("\n").append(item);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Determine the length of the certificate_status_req_list field in
+     * the status_request_v2 extension.
+     *
+     * @return the total encoded length of all items in the list, or 0 if the
+     *      encapsulating extension_data is zero-length (from a ServerHello)
+     */
+    private int calculateListLength() {
+        int listLen = 0;
+
+        for (CertStatusReqItemV2 item : itemList) {
+            listLen += item.length();
+        }
+
+        return listLen;
+    }
+
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java	Wed Aug 05 12:19:38 2015 -0700
@@ -37,10 +37,12 @@
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateParsingException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.Reason;
+import java.security.cert.CertPathValidatorException.BasicReason;
 import javax.security.auth.x500.X500Principal;
 
 import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
 
 import javax.net.ssl.*;
 
@@ -79,6 +81,12 @@
 
     private boolean serverKeyExchangeReceived;
 
+    private final boolean enableStatusRequestExtension =
+            Debug.getBooleanProperty(
+                    "jdk.tls.client.enableStatusRequestExtension", true);
+    private boolean staplingActive = false;
+    private X509Certificate[] deferredCerts;
+
     /*
      * The RSA PreMasterSecret needs to know the version of
      * ClientHello that was used on this handshake.  This represents
@@ -200,7 +208,16 @@
     @Override
     void processMessage(byte type, int messageLen) throws IOException {
         // check the handshake state
-        handshakeState.check(type);
+        List<Byte> ignoredOptStates = handshakeState.check(type);
+
+        // If the state machine has skipped over certificate status
+        // and stapling was enabled, we need to check the chain immediately
+        // because it was deferred, waiting for CertificateStatus.
+        if (staplingActive && ignoredOptStates.contains(
+                HandshakeMessage.ht_certificate_status)) {
+            checkServerCerts(deferredCerts);
+            serverKey = session.getPeerCertificates()[0].getPublicKey();
+        }
 
         switch (type) {
         case HandshakeMessage.ht_hello_request:
@@ -241,8 +258,19 @@
             CertificateMsg certificateMsg = new CertificateMsg(input);
             handshakeState.update(certificateMsg, resumingSession);
             this.serverCertificate(certificateMsg);
-            serverKey =
-                session.getPeerCertificates()[0].getPublicKey();
+            if (!staplingActive) {
+                // If we are not doing stapling, we can set serverKey right
+                // away.  Otherwise, we will wait until verification of the
+                // chain has completed after CertificateStatus;
+                serverKey = session.getPeerCertificates()[0].getPublicKey();
+            }
+            break;
+
+        case HandshakeMessage.ht_certificate_status:
+            CertificateStatus certStatusMsg = new CertificateStatus(input);
+            handshakeState.update(certStatusMsg, resumingSession);
+            this.certificateStatus(certStatusMsg);
+            serverKey = session.getPeerCertificates()[0].getPublicKey();
             break;
 
         case HandshakeMessage.ht_server_key_exchange:
@@ -685,10 +713,22 @@
             ExtensionType type = ext.type;
             if (type == ExtensionType.EXT_SERVER_NAME) {
                 serverNamesAccepted = true;
+            } else if (type == ExtensionType.EXT_STATUS_REQUEST ||
+                    type == ExtensionType.EXT_STATUS_REQUEST_V2) {
+                // Only enable the stapling feature if the client asserted
+                // these extensions.
+                if (enableStatusRequestExtension) {
+                    staplingActive = true;
+                } else {
+                    fatalSE(Alerts.alert_unexpected_message, "Server set " +
+                            type + " extension when not requested by client");
+                }
             } else if ((type != ExtensionType.EXT_ELLIPTIC_CURVES)
                     && (type != ExtensionType.EXT_EC_POINT_FORMATS)
                     && (type != ExtensionType.EXT_SERVER_NAME)
-                    && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) {
+                    && (type != ExtensionType.EXT_RENEGOTIATION_INFO)
+                    && (type != ExtensionType.EXT_STATUS_REQUEST)
+                    && (type != ExtensionType.EXT_STATUS_REQUEST_V2)) {
                 fatalSE(Alerts.alert_unsupported_extension,
                     "Server sent an unsupported extension: " + type);
             }
@@ -1476,6 +1516,12 @@
             }
         }
 
+        // Add status_request and status_request_v2 extensions
+        if (enableStatusRequestExtension) {
+            clientHelloMessage.addCertStatusReqListV2Extension();
+            clientHelloMessage.addCertStatusRequestExtension();
+        }
+
         // reset the client random cookie
         clnt_random = clientHelloMessage.clnt_random;
 
@@ -1545,40 +1591,36 @@
         }
 
         // ask the trust manager to verify the chain
-        X509TrustManager tm = sslContext.getX509TrustManager();
-        try {
-            // find out the key exchange algorithm used
-            // use "RSA" for non-ephemeral "RSA_EXPORT"
-            String keyExchangeString;
-            if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
-                keyExchangeString = K_RSA.name;
-            } else {
-                keyExchangeString = keyExchange.name;
-            }
+        if (staplingActive) {
+            // Defer the certificate check until after we've received the
+            // CertificateStatus message.  If that message doesn't come in
+            // immediately following this message we will execute the check
+            // directly from processMessage before any other SSL/TLS processing.
+            deferredCerts = peerCerts;
+        } else {
+            // We're not doing stapling, so perform the check right now
+            checkServerCerts(peerCerts);
+        }
+    }
 
-            if (tm instanceof X509ExtendedTrustManager) {
-                if (conn != null) {
-                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
-                        peerCerts.clone(),
-                        keyExchangeString,
-                        conn);
-                } else {
-                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
-                        peerCerts.clone(),
-                        keyExchangeString,
-                        engine);
-                }
-            } else {
-                // Unlikely to happen, because we have wrapped the old
-                // X509TrustManager with the new X509ExtendedTrustManager.
-                throw new CertificateException(
-                    "Improper X509TrustManager implementation");
-            }
-        } catch (CertificateException e) {
-            // This will throw an exception, so include the original error.
-            fatalSE(Alerts.alert_certificate_unknown, e);
+    /**
+     * If certificate status stapling has been enabled, the server will send
+     * one or more status messages to the client.
+     *
+     * @param mesg a {@code CertificateStatus} object built from the data
+     *      sent by the server.
+     *
+     * @throws IOException if any parsing errors occur.
+     */
+    private void certificateStatus(CertificateStatus mesg) throws IOException {
+        if (debug != null && Debug.isOn("handshake")) {
+            mesg.print(System.out);
         }
-        session.setPeerCertificates(peerCerts);
+
+        // Perform the certificate check using the deferred certificates
+        // and responses that we have obtained.
+        session.setStatusResponses(mesg.getResponses());
+        checkServerCerts(deferredCerts);
     }
 
     /*
@@ -1700,4 +1742,88 @@
 
         return false;
     }
+
+    /**
+     * Perform client-side checking of server certificates.
+     *
+     * @param certs an array of {@code X509Certificate} objects presented
+     *      by the server in the ServerCertificate message.
+     *
+     * @throws IOException if a failure occurs during validation or
+     *      the trust manager associated with the {@code SSLContext} is not
+     *      an {@code X509ExtendedTrustManager}.
+     */
+    private void checkServerCerts(X509Certificate[] certs)
+            throws IOException {
+        X509TrustManager tm = sslContext.getX509TrustManager();
+
+        // find out the key exchange algorithm used
+        // use "RSA" for non-ephemeral "RSA_EXPORT"
+        String keyExchangeString;
+        if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
+            keyExchangeString = K_RSA.name;
+        } else {
+            keyExchangeString = keyExchange.name;
+        }
+
+        try {
+            if (tm instanceof X509ExtendedTrustManager) {
+                if (conn != null) {
+                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
+                        certs.clone(),
+                        keyExchangeString,
+                        conn);
+                } else {
+                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
+                        certs.clone(),
+                        keyExchangeString,
+                        engine);
+                }
+            } else {
+                // Unlikely to happen, because we have wrapped the old
+                // X509TrustManager with the new X509ExtendedTrustManager.
+                throw new CertificateException(
+                        "Improper X509TrustManager implementation");
+            }
+
+            // Once the server certificate chain has been validated, set
+            // the certificate chain in the TLS session.
+            session.setPeerCertificates(certs);
+        } catch (CertificateException ce) {
+            fatalSE(getCertificateAlert(ce), ce);
+        }
+    }
+
+    /**
+     * When a failure happens during certificate checking from an
+     * {@link X509TrustManager}, determine what TLS alert description to use.
+     *
+     * @param cexc The exception thrown by the {@link X509TrustManager}
+     *
+     * @return A byte value corresponding to a TLS alert description number.
+     */
+    private byte getCertificateAlert(CertificateException cexc) {
+        // The specific reason for the failure will determine how to
+        // set the alert description value
+        byte alertDesc = Alerts.alert_certificate_unknown;
+
+        Throwable baseCause = cexc.getCause();
+        if (baseCause instanceof CertPathValidatorException) {
+            CertPathValidatorException cpve =
+                    (CertPathValidatorException)baseCause;
+            Reason reason = cpve.getReason();
+            if (reason == BasicReason.REVOKED) {
+                alertDesc = staplingActive ?
+                        Alerts.alert_bad_certificate_status_response :
+                        Alerts.alert_certificate_revoked;
+            } else if (reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
+                alertDesc = staplingActive ?
+                        Alerts.alert_bad_certificate_status_response :
+                        Alerts.alert_certificate_unknown;
+            }
+        }
+
+        return alertDesc;
+    }
 }
+
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ExtensionType.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ExtensionType.java	Wed Aug 05 12:19:38 2015 -0700
@@ -44,7 +44,7 @@
     }
 
     static List<ExtensionType> knownExtensions =
-            new ArrayList<ExtensionType>(13);
+            new ArrayList<ExtensionType>(14);
 
     static ExtensionType get(int id) {
         for (ExtensionType ext : knownExtensions) {
@@ -97,6 +97,10 @@
     final static ExtensionType EXT_SIGNATURE_ALGORITHMS =
             e(0x000D, "signature_algorithms");   // IANA registry value: 13
 
+    // extensions defined in RFC 6961
+    final static ExtensionType EXT_STATUS_REQUEST_V2 =
+            e(0x0011, "status_request_v2");      // IANA registry value: 17
+
     // extensions defined in RFC 5746
     final static ExtensionType EXT_RENEGOTIATION_INFO =
             e(0xff01, "renegotiation_info");     // IANA registry value: 65281
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java	Wed Aug 05 12:19:38 2015 -0700
@@ -49,6 +49,7 @@
 import sun.security.ssl.CipherSuite.*;
 import static sun.security.ssl.CipherSuite.PRF.*;
 import sun.security.util.KeyUtil;
+import sun.security.provider.certpath.OCSPResponse;
 
 /**
  * Many data structures are involved in the handshake messages.  These
@@ -393,6 +394,24 @@
         cookieDigest.update(hos.toByteArray());
     }
 
+    // Add status_request extension type
+    void addCertStatusRequestExtension() {
+        extensions.add(new CertStatusReqExtension(StatusRequestType.OCSP,
+                new OCSPStatusRequest()));
+    }
+
+    // Add status_request_v2 extension type
+    void addCertStatusReqListV2Extension() {
+        // Create a default OCSPStatusRequest that we can use for both
+        // OCSP_MULTI and OCSP request list items.
+        OCSPStatusRequest osr = new OCSPStatusRequest();
+        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
+        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
+                osr));
+        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr));
+        extensions.add(new CertStatusReqListV2Extension(itemList));
+    }
+
     @Override
     int messageType() { return ht_client_hello; }
 
@@ -636,6 +655,240 @@
 }
 
 /*
+ * CertificateStatus ... SERVER --> CLIENT
+ *
+ * When a ClientHello asserting the status_request or status_request_v2
+ * extensions is accepted by the server, it will fetch and return one
+ * or more status responses in this handshake message.
+ *
+ * NOTE: Like the Certificate handshake message, this can potentially
+ * be a very large message both due to the size of multiple status
+ * responses and the certificate chains that are often attached to them.
+ * Up to 2^24 bytes of status responses may be sent, possibly fragmented
+ * over multiple TLS records.
+ */
+static final class CertificateStatus extends HandshakeMessage
+{
+    private final StatusRequestType statusType;
+    private int encodedResponsesLen;
+    private int messageLength = -1;
+    private List<byte[]> encodedResponses;
+
+    @Override
+    int messageType() { return ht_certificate_status; }
+
+    /**
+     * Create a CertificateStatus message from the certificates and their
+     * respective OCSP responses
+     *
+     * @param type an indication of the type of response (OCSP or OCSP_MULTI)
+     * @param responses a {@code List} of OCSP responses in DER-encoded form.
+     *      For the OCSP type, only the first entry in the response list is
+     *      used, and must correspond to the end-entity certificate sent to the
+     *      peer.  Zero-length or null values for the response data are not
+     *      allowed for the OCSP type.  For the OCSP_MULTI type, each entry in
+     *      the list should match its corresponding certificate sent in the
+     *      Server Certificate message.  Where an OCSP response does not exist,
+     *      either a zero-length array or a null value should be used.
+     *
+     * @throws SSLException if an unsupported StatusRequestType or invalid
+     *      OCSP response data is provided.
+     */
+    CertificateStatus(StatusRequestType type, X509Certificate[] chain,
+            Map<X509Certificate, byte[]> responses) throws SSLException {
+        statusType = type;
+        encodedResponsesLen = 0;
+        encodedResponses = new ArrayList<>(chain.length);
+
+        Objects.requireNonNull(chain, "Null chain not allowed");
+        Objects.requireNonNull(responses, "Null responses not allowed");
+
+        if (statusType == StatusRequestType.OCSP) {
+            // Just get the response for the end-entity certificate
+            byte[] respDER = responses.get(chain[0]);
+            if (respDER != null && respDER.length > 0) {
+                encodedResponses.add(respDER);
+                encodedResponsesLen = 3 + respDER.length;
+            } else {
+                throw new SSLHandshakeException("Zero-length or null " +
+                        "OCSP Response");
+            }
+        } else if (statusType == StatusRequestType.OCSP_MULTI) {
+            for (X509Certificate cert : chain) {
+                byte[] respDER = responses.get(cert);
+                if (respDER != null) {
+                    encodedResponses.add(respDER);
+                    encodedResponsesLen += (respDER.length + 3);
+                } else {
+                    // If we cannot find a response for a given certificate
+                    // then use a zero-length placeholder.
+                    encodedResponses.add(new byte[0]);
+                    encodedResponsesLen += 3;
+                }
+            }
+        } else {
+            throw new SSLHandshakeException("Unsupported StatusResponseType: " +
+                    statusType);
+        }
+    }
+
+    /**
+     * Decode the CertificateStatus handshake message coming from a
+     * {@code HandshakeInputStream}.
+     *
+     * @param input the {@code HandshakeInputStream} containing the
+     * CertificateStatus message bytes.
+     *
+     * @throws SSLHandshakeException if a zero-length response is found in the
+     * OCSP response type, or an unsupported response type is detected.
+     * @throws IOException if a decoding error occurs.
+     */
+    CertificateStatus(HandshakeInStream input) throws IOException {
+        encodedResponsesLen = 0;
+        encodedResponses = new ArrayList<>();
+
+        statusType = StatusRequestType.get(input.getInt8());
+        if (statusType == StatusRequestType.OCSP) {
+            byte[] respDER = input.getBytes24();
+            // Convert the incoming bytes to a OCSPResponse strucutre
+            if (respDER.length > 0) {
+                encodedResponses.add(respDER);
+                encodedResponsesLen = 3 + respDER.length;
+            } else {
+                throw new SSLHandshakeException("Zero-length OCSP Response");
+            }
+        } else if (statusType == StatusRequestType.OCSP_MULTI) {
+            int respListLen = input.getInt24();
+            encodedResponsesLen = respListLen;
+
+            // Add each OCSP reponse into the array list in the order
+            // we receive them off the wire.  A zero-length array is
+            // allowed for ocsp_multi, and means that a response for
+            // a given certificate is not available.
+            while (respListLen > 0) {
+                byte[] respDER = input.getBytes24();
+                encodedResponses.add(respDER);
+                respListLen -= (respDER.length + 3);
+            }
+
+            if (respListLen != 0) {
+                throw new SSLHandshakeException(
+                        "Bad OCSP response list length");
+            }
+        } else {
+            throw new SSLHandshakeException("Unsupported StatusResponseType: " +
+                    statusType);
+        }
+    }
+
+    /**
+     * Get the length of the CertificateStatus message.
+     *
+     * @return the length of the message in bytes.
+     */
+    @Override
+    int messageLength() {
+        int len = 1;            // Length + Status type
+
+        if (messageLength == -1) {
+            if (statusType == StatusRequestType.OCSP) {
+                len += encodedResponsesLen;
+            } else if (statusType == StatusRequestType.OCSP_MULTI) {
+                len += 3 + encodedResponsesLen;
+            }
+            messageLength = len;
+        }
+
+        return messageLength;
+    }
+
+    /**
+     * Encode the CertificateStatus handshake message and place it on a
+     * {@code HandshakeOutputStream}.
+     *
+     * @param s the HandshakeOutputStream that will the message bytes.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    @Override
+    void send(HandshakeOutStream s) throws IOException {
+        s.putInt8(statusType.id);
+        if (statusType == StatusRequestType.OCSP) {
+            s.putBytes24(encodedResponses.get(0));
+        } else if (statusType == StatusRequestType.OCSP_MULTI) {
+            s.putInt24(encodedResponsesLen);
+            for (byte[] respBytes : encodedResponses) {
+                if (respBytes != null) {
+                    s.putBytes24(respBytes);
+                } else {
+                    s.putBytes24(null);
+                }
+            }
+        } else {
+            // It is highly unlikely that we will fall into this section of
+            // the code.
+            throw new SSLHandshakeException("Unsupported status_type: " +
+                    statusType.id);
+        }
+    }
+
+    /**
+     * Display a human-readable representation of the CertificateStatus message.
+     *
+     * @param s the PrintStream used to display the message data.
+     *
+     * @throws IOException if any errors occur while parsing the OCSP response
+     * bytes into a readable form.
+     */
+    @Override
+    void print(PrintStream s) throws IOException {
+        s.println("*** CertificateStatus");
+        if (debug != null && Debug.isOn("verbose")) {
+            s.println("Type: " + statusType);
+            if (statusType == StatusRequestType.OCSP) {
+                OCSPResponse oResp = new OCSPResponse(encodedResponses.get(0));
+                s.println(oResp);
+            } else if (statusType == StatusRequestType.OCSP_MULTI) {
+                int numResponses = encodedResponses.size();
+                s.println(numResponses +
+                        (numResponses == 1 ? " entry:" : " entries:"));
+                for (byte[] respDER : encodedResponses) {
+                    if (respDER.length > 0) {
+                        OCSPResponse oResp = new OCSPResponse(respDER);
+                        s.println(oResp);
+                    } else {
+                        s.println("<Zero-length entry>");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the type of CertificateStatus message
+     *
+     * @return the {@code StatusRequestType} for this CertificateStatus
+     *      message.
+     */
+    StatusRequestType getType() {
+        return statusType;
+    }
+
+    /**
+     * Get the list of non-zero length OCSP responses.
+     * The responses returned in this list can be used to map to
+     * {@code X509Certificate} objects provided by the peer and
+     * provided to a {@code PKIXRevocationChecker}.
+     *
+     * @return an unmodifiable List of zero or more byte arrays, each one
+     *      consisting of a single status response.
+     */
+    List<byte[]> getResponses() {
+        return Collections.unmodifiableList(encodedResponses);
+    }
+}
+
+/*
  * ServerKeyExchange ... SERVER --> CLIENT
  *
  * The cipher suite selected, when combined with the certificate exchanged,
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java	Wed Aug 05 12:19:38 2015 -0700
@@ -25,12 +25,12 @@
 
 package sun.security.ssl;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.LinkedList;
 import java.util.HashMap;
 import javax.net.ssl.SSLProtocolException;
 
-import sun.security.ssl.HandshakeMessage.*;
-
 import static sun.security.ssl.CipherSuite.KeyExchange;
 import static sun.security.ssl.CipherSuite.KeyExchange.*;
 import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
@@ -311,7 +311,7 @@
         HS_SERVER_CHANGE_CIPHER_SPEC(
                 "server change_cipher_spec",
                 HandshakeMessage.ht_not_applicable),
-        HS_SERVER_FINISHDE(
+        HS_SERVER_FINISHED(
                 "server finished",
                 HandshakeMessage.ht_finished);
 
@@ -343,7 +343,8 @@
         return upcomingStates.isEmpty();
     }
 
-    void check(byte handshakeType) throws SSLProtocolException {
+    List<Byte> check(byte handshakeType) throws SSLProtocolException {
+        List<Byte> ignoredOptional = new LinkedList<>();
         String exceptionMsg =
                  "Handshake message sequence violation, " + handshakeType;
 
@@ -362,27 +363,28 @@
             }
 
             // It is a kickstart message.
-            return;
+            return Collections.emptyList();
         }
 
-        // Ignore the checking for HelloRequest messages as they are
+        // Ignore the checking for HelloRequest messages as they
         // may be sent by the server at any time.
         if (handshakeType == HandshakeMessage.ht_hello_request) {
-            return;
+            return Collections.emptyList();
         }
 
         for (HandshakeState handshakeState : upcomingStates) {
             if (handshakeState.handshakeType == handshakeType) {
                 // It's the expected next handshake type.
-                return;
+                return ignoredOptional;
             }
 
             if (handshakeState.isOptional) {
+                ignoredOptional.add(handshakeState.handshakeType);
                 continue;
             } else {
                 for (HandshakeState alternative : alternatives) {
                     if (alternative.handshakeType == handshakeType) {
-                        return;
+                        return ignoredOptional;
                     }
 
                     if (alternative.isOptional) {
@@ -541,7 +543,7 @@
             //              (Server Finished Flight)
             //              HS_NEW_SESSION_TICKET
             //          --> HS_SERVER_CHANGE_CIPHER_SPEC
-            //          --> HS_SERVER_FINISHDE
+            //          --> HS_SERVER_FINISHED
             //              (Client Finished Flight)
             //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
             //          --> HS_CLEINT_FINISHED
@@ -587,7 +589,7 @@
 
                 // Mandatory server ChangeCipherSpec and Finished messages
                 upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
-                upcomingStates.add(HS_SERVER_FINISHDE);
+                upcomingStates.add(HS_SERVER_FINISHED);
 
                 // Mandatory client ChangeCipherSpec and Finished messages
                 upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
@@ -598,15 +600,11 @@
                 // boolean hasSupplementalDataExt =
                 //     (hes.get(HandshakeMessage.ht_supplemental_data) != null);
 
-                // Not support CertificateStatus extension yet.
-                //
-                // boolean hasCertificateStatusExt =
-                //    (hes.get(HandshakeMessage.ht_certificate_status) != null);
-
                 // Not support CertificateURL extension yet.
                 //
                 // boolean hasCertificateUrlExt =
-                //     (hes.get(HandshakeMessage.ht_certificate_url) != null);
+                //     (hes.get(ExtensionType EXT_CLIENT_CERTIFICATE_URL)
+                //          != null);
 
                 // Not support SupplementalData extension yet.
                 //
@@ -625,12 +623,11 @@
                     upcomingStates.add(HS_SERVER_CERTIFICATE);
                 }
 
-                // Not support CertificateStatus extension yet.
-                //
-                // // Optional CertificateStatus message
-                // if (hasCertificateStatusExt) {
-                //     upcomingStates.add(HS_CERTIFICATE_STATUS);
-                // }
+                // Optional CertificateStatus message
+                if (hes.get(ExtensionType.EXT_STATUS_REQUEST) != null ||
+                        hes.get(ExtensionType.EXT_STATUS_REQUEST_V2) != null) {
+                    upcomingStates.add(HS_CERTIFICATE_STATUS);
+                }
 
                 // Need ServerKeyExchange message or not?
                 if ((keyExchange == K_RSA_EXPORT) ||
@@ -690,7 +687,7 @@
 
                 // Mandatory server ChangeCipherSpec and Finished messages
                 upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
-                upcomingStates.add(HS_SERVER_FINISHDE);
+                upcomingStates.add(HS_SERVER_FINISHED);
             }
 
             break;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java	Wed Aug 05 12:19:38 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015, 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
@@ -87,6 +87,10 @@
                 extension = new RenegotiationInfoExtension(s, extlen);
             } else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
                 extension = new MaxFragmentLengthExtension(s, extlen);
+            } else if (extType == ExtensionType.EXT_STATUS_REQUEST) {
+                extension = new CertStatusReqExtension(s, extlen);
+            } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) {
+                extension = new CertStatusReqListV2Extension(s, extlen);
             } else {
                 extension = new UnknownExtension(s, extlen, extType);
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/OCSPStatusRequest.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.cert.Extension;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+import javax.net.ssl.SSLException;
+import sun.security.util.DerValue;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerOutputStream;
+import sun.security.provider.certpath.ResponderId;
+
+/*
+ * RFC6066 defines the TLS extension,"status_request" (type 0x5),
+ * which allows the client to request that the server perform OCSP
+ * on the client's behalf.
+ *
+ * The RFC defines an OCSPStatusRequest structure:
+ *
+ *      struct {
+ *          ResponderID responder_id_list<0..2^16-1>;
+ *          Extensions  request_extensions;
+ *      } OCSPStatusRequest;
+ */
+final class OCSPStatusRequest implements StatusRequest {
+
+    private final List<ResponderId> responderIds;
+    private final List<Extension> extensions;
+    private int encodedLen;
+    private int ridListLen;
+    private int extListLen;
+
+    /**
+     * Construct a default {@code OCSPStatusRequest} object with empty
+     * responder ID and code extension list fields.
+     */
+    OCSPStatusRequest() {
+        responderIds = new ArrayList<>();
+        extensions = new ArrayList<>();
+        encodedLen = this.length();
+    }
+
+    /**
+     * Construct an {@code OCSPStatusRequest} object using the provided
+     *      {@code ResponderId} and {@code Extension} lists.
+     *
+     * @param respIds the list of {@code ResponderId} objects to be placed
+     *      into the {@code OCSPStatusRequest}.  If the user wishes to place
+     *      no {@code ResponderId} objects in the request, either an empty
+     *      {@code List} or {@code null} is acceptable.
+     * @param exts the list of {@code Extension} objects to be placed into
+     *      the {@code OCSPStatusRequest}  If the user wishes to place
+     *      no {@code Extension} objects in the request, either an empty
+     *      {@code List} or {@code null} is acceptable.
+     */
+    OCSPStatusRequest(List<ResponderId> respIds, List<Extension> exts) {
+        responderIds = new ArrayList<>(respIds != null ? respIds :
+                Collections.emptyList());
+        extensions = new ArrayList<>(exts != null ? exts :
+                Collections.emptyList());
+        encodedLen = this.length();
+    }
+
+    /**
+     * Construct an {@code OCSPStatusRequest} object from data read from
+     * a {@code HandshakeInputStream}
+     *
+     * @param s the {@code HandshakeInputStream} providing the encoded data
+     *
+     * @throws IOException if any decoding errors happen during object
+     *      construction.
+     */
+    OCSPStatusRequest(HandshakeInStream in) throws IOException {
+        responderIds = new ArrayList<>();
+        extensions = new ArrayList<>();
+
+        int ridListBytesRemaining = in.getInt16();
+        while (ridListBytesRemaining != 0) {
+            byte[] ridBytes = in.getBytes16();
+            responderIds.add(new ResponderId(ridBytes));
+            ridListBytesRemaining -= (ridBytes.length + 2);
+            // Make sure that no individual responder ID's length caused an
+            // overrun relative to the outer responder ID list length
+            if (ridListBytesRemaining < 0) {
+                throw new SSLException("Responder ID length overflow: " +
+                        "current rid = " + ridBytes.length + ", remaining = " +
+                        ridListBytesRemaining);
+            }
+        }
+
+        int extensionLength = in.getInt16();
+        if (extensionLength > 0) {
+            byte[] extensionData = new byte[extensionLength];
+            in.read(extensionData);
+            DerInputStream dis = new DerInputStream(extensionData);
+            DerValue[] extSeqContents = dis.getSequence(extensionData.length);
+            for (DerValue extDerVal : extSeqContents) {
+                extensions.add(new sun.security.x509.Extension(extDerVal));
+            }
+        }
+    }
+
+    /**
+     * Construct an {@code OCSPStatusRequest} from its encoded form
+     *
+     * @param requestBytes the status request extension bytes
+     *
+     * @throws IOException if any error occurs during decoding
+     */
+    OCSPStatusRequest(byte[] requestBytes) throws IOException {
+        responderIds = new ArrayList<>();
+        extensions = new ArrayList<>();
+        ByteBuffer reqBuf = ByteBuffer.wrap(requestBytes);
+
+        // Get the ResponderId list length
+        encodedLen = requestBytes.length;
+        ridListLen = Short.toUnsignedInt(reqBuf.getShort());
+        int endOfRidList = reqBuf.position() + ridListLen;
+
+        // The end position of the ResponderId list in the ByteBuffer
+        // should be at least 2 less than the end of the buffer.  This
+        // 2 byte defecit is the minimum length required to encode a
+        // zero-length extensions segment.
+        if (reqBuf.limit() - endOfRidList < 2) {
+            throw new SSLException
+                ("ResponderId List length exceeds provided buffer - Len: "
+                 + ridListLen + ", Buffer: " + reqBuf.remaining());
+        }
+
+        while (reqBuf.position() < endOfRidList) {
+            int ridLength = Short.toUnsignedInt(reqBuf.getShort());
+            // Make sure an individual ResponderId length doesn't
+            // run past the end of the ResponderId list portion of the
+            // provided buffer.
+            if (reqBuf.position() + ridLength > endOfRidList) {
+                throw new SSLException
+                    ("ResponderId length exceeds list length - Off: "
+                     + reqBuf.position() + ", Length: " + ridLength
+                     + ", End offset: " + endOfRidList);
+            }
+
+            // Consume/add the ResponderId
+            if (ridLength > 0) {
+                byte[] ridData = new byte[ridLength];
+                reqBuf.get(ridData);
+                responderIds.add(new ResponderId(ridData));
+            }
+        }
+
+        // Get the Extensions length
+        int extensionsLen = Short.toUnsignedInt(reqBuf.getShort());
+
+        // The end of the extensions should also be the end of the
+        // encoded OCSPStatusRequest
+        if (extensionsLen != reqBuf.remaining()) {
+            throw new SSLException("Incorrect extensions length: Read "
+                    + extensionsLen + ", Data length: " + reqBuf.remaining());
+        }
+
+        // Extensions are a SEQUENCE of Extension
+        if (extensionsLen > 0) {
+            byte[] extensionData = new byte[extensionsLen];
+            reqBuf.get(extensionData);
+            DerInputStream dis = new DerInputStream(extensionData);
+            DerValue[] extSeqContents = dis.getSequence(extensionData.length);
+            for (DerValue extDerVal : extSeqContents) {
+                extensions.add(new sun.security.x509.Extension(extDerVal));
+            }
+        }
+    }
+
+    /**
+     * Obtain the length of the {@code OCSPStatusRequest} object in its
+     *      encoded form
+     *
+     * @return the length of the {@code OCSPStatusRequest} object in its
+     *      encoded form
+     */
+    @Override
+    public int length() {
+        // If we've previously calculated encodedLen simply return it
+        if (encodedLen != 0) {
+            return encodedLen;
+        }
+
+        ridListLen = 0;
+        for (ResponderId rid : responderIds) {
+            ridListLen += rid.length() + 2;
+        }
+
+        extListLen = 0;
+        if (!extensions.isEmpty()) {
+            try {
+                DerOutputStream extSequence = new DerOutputStream();
+                DerOutputStream extEncoding = new DerOutputStream();
+                for (Extension ext : extensions) {
+                    ext.encode(extEncoding);
+                }
+                extSequence.write(DerValue.tag_Sequence, extEncoding);
+                extListLen = extSequence.size();
+            } catch (IOException ioe) {
+                // Not sure what to do here
+            }
+        }
+
+        // Total length is the responder ID list length and extensions length
+        // plus each lists' 2-byte length fields.
+        encodedLen = ridListLen + extListLen + 4;
+
+        return encodedLen;
+    }
+
+    /**
+     * Send the encoded {@code OCSPStatusRequest} out through the provided
+     *      {@code HandshakeOutputStream}
+     *
+     * @param s the {@code HandshakeOutputStream} on which to send the encoded
+     *      data
+     *
+     * @throws IOException if any encoding errors occur
+     */
+    @Override
+    public void send(HandshakeOutStream s) throws IOException {
+        s.putInt16(ridListLen);
+        for (ResponderId rid : responderIds) {
+            s.putBytes16(rid.getEncoded());
+        }
+
+        DerOutputStream seqOut = new DerOutputStream();
+        DerOutputStream extBytes = new DerOutputStream();
+
+        if (extensions.size() > 0) {
+            for (Extension ext : extensions) {
+                ext.encode(extBytes);
+            }
+            seqOut.write(DerValue.tag_Sequence, extBytes);
+        }
+        s.putBytes16(seqOut.toByteArray());
+    }
+
+    /**
+     * Determine if a provided {@code OCSPStatusRequest} objects is equal to
+     *      this one.
+     *
+     * @param obj an {@code OCSPStatusRequest} object to be compared against
+     *
+     * @return {@code true} if the objects are equal, {@code false} otherwise.
+     *      Equivalence is established if the lists of responder IDs and
+     *      extensions between the two objects are also equal.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        } else if (this == obj) {
+            return true;
+        } else if (obj instanceof OCSPStatusRequest) {
+            OCSPStatusRequest respObj = (OCSPStatusRequest)obj;
+            return responderIds.equals(respObj.getResponderIds()) &&
+                extensions.equals(respObj.getExtensions());
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the hash code value for this {@code OCSPStatusRequest}
+     *
+     * @return the hash code value for this {@code OCSPStatusRequest}
+     */
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + responderIds.hashCode();
+        result = 31 * result + extensions.hashCode();
+
+        return result;
+    }
+
+    /**
+     * Create a string representation of this {@code OCSPStatusRequest}
+     *
+     * @return a string representation of this {@code OCSPStatusRequest}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("OCSPStatusRequest\n");
+        sb.append("    ResponderIds:");
+
+        if (responderIds.isEmpty()) {
+            sb.append(" <EMPTY>");
+        } else {
+            for (ResponderId rid : responderIds) {
+                sb.append("\n    ").append(rid.toString());
+            }
+        }
+
+        sb.append("\n").append("    Extensions:");
+        if (extensions.isEmpty()) {
+            sb.append(" <EMPTY>");
+        } else {
+            for (Extension ext : extensions) {
+                sb.append("\n    ").append(ext.toString());
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Get the list of {@code ResponderId} objects for this
+     *      {@code OCSPStatusRequest}
+     *
+     * @return an unmodifiable {@code List} of {@code ResponderId} objects
+     */
+    List<ResponderId> getResponderIds() {
+        return Collections.unmodifiableList(responderIds);
+    }
+
+    /**
+     * Get the list of {@code Extension} objects for this
+     *      {@code OCSPStatusRequest}
+     *
+     * @return an unmodifiable {@code List} of {@code Extension} objects
+     */
+    List<Extension> getExtensions() {
+        return Collections.unmodifiableList(extensions);
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Wed Aug 05 12:19:38 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, 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
@@ -65,6 +65,8 @@
     // DTLS cookie exchange manager
     private HelloCookieManager helloCookieManager;
 
+    private StatusResponseManager statusResponseManager;
+
     SSLContextImpl() {
         ephemeralKeyManager = new EphemeralKeyManager();
         clientCache = new SSLSessionContextImpl();
@@ -88,6 +90,7 @@
             }
         }
         trustManager = chooseTrustManager(tm);
+        statusResponseManager = new StatusResponseManager();
 
         if (sr == null) {
             secureRandom = JsseJce.getSecureRandom();
@@ -256,6 +259,10 @@
                 "Cookie exchange applies to DTLS only");
     }
 
+    StatusResponseManager getStatusResponseManager() {
+        return statusResponseManager;
+    }
+
     abstract SSLParameters getDefaultServerSSLParams();
     abstract SSLParameters getDefaultClientSSLParams();
     abstract SSLParameters getSupportedSSLParams();
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Wed Aug 05 12:19:38 2015 -0700
@@ -108,6 +108,7 @@
     private String[]            localSupportedSignAlgs;
     private String[]            peerSupportedSignAlgs;
     private List<SNIServerName>    requestedServerNames;
+    private List<byte[]>        statusResponses;
 
     private int                 negotiatedMaxFragLen;
     private int                 maximumPacketSize;
@@ -180,6 +181,7 @@
         localSupportedSignAlgs =
             SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
         negotiatedMaxFragLen = -1;
+        statusResponses = null;
 
         if (debug != null && Debug.isOn("session")) {
             System.out.println("%% Initialized:  " + this);
@@ -226,6 +228,19 @@
     }
 
     /**
+     * Provide status response data obtained during the SSL handshake.
+     *
+     * @param responses a {@link List} of responses in binary form.
+     */
+    void setStatusResponses(List<byte[]> responses) {
+        if (responses != null && !responses.isEmpty()) {
+            statusResponses = responses;
+        } else {
+            statusResponses = Collections.emptyList();
+        }
+    }
+
+    /**
      * Set the peer principal.
      */
     void setPeerPrincipal(Principal principal) {
@@ -532,6 +547,30 @@
     }
 
     /**
+     * Return a List of status responses presented by the peer.
+     * Note: This method can be used only when using certificate-based
+     * server authentication; otherwise an empty {@code List} will be returned.
+     *
+     * @return an unmodifiable {@code List} of byte arrays, each consisting
+     * of a DER-encoded OCSP response (see RFC 6960).  If no responses have
+     * been presented by the server or non-certificate based server
+     * authentication is used then an empty {@code List} is returned.
+     */
+    @Override
+    public List<byte[]> getStatusResponses() {
+        if (statusResponses == null || statusResponses.isEmpty()) {
+            return Collections.emptyList();
+        } else {
+            // Clone both the list and the contents
+            List<byte[]> responses = new ArrayList<>(statusResponses.size());
+            for (byte[] respBytes : statusResponses) {
+                responses.add(respBytes.clone());
+            }
+            return Collections.unmodifiableList(responses);
+        }
+    }
+
+    /**
      * Returns the identity of the peer which was established as part of
      * defining the session.
      *
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Wed Aug 05 12:19:38 2015 -0700
@@ -28,6 +28,7 @@
 
 import java.io.*;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.security.*;
 import java.security.cert.*;
 import java.security.interfaces.*;
@@ -36,9 +37,9 @@
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
-
 import javax.net.ssl.*;
 
+import sun.security.action.GetLongAction;
 import sun.security.util.KeyUtil;
 import sun.security.util.LegacyAlgorithmConstraints;
 import sun.security.action.GetPropertyAction;
@@ -57,11 +58,16 @@
  */
 final class ServerHandshaker extends Handshaker {
 
+    // The default number of milliseconds the handshaker will wait for
+    // revocation status responses.
+    private static final long DEFAULT_STATUS_RESP_DELAY = 5000;
+
     // is the server going to require the client to authenticate?
     private ClientAuthType      doClientAuth;
 
     // our authentication info
     private X509Certificate[]   certs;
+    private Map<X509Certificate, byte[]> responseMap;
     private PrivateKey          privateKey;
 
     private Object              serviceCreds;
@@ -112,6 +118,13 @@
                     LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS,
                     new SSLAlgorithmDecomposer());
 
+    // To switch off the status_request[_v2] extensions
+    private final static boolean enableStatusRequestExtension =
+            Debug.getBooleanProperty(
+                    "jdk.tls.server.enableStatusRequestExtension", false);
+    private boolean staplingActive = false;
+    private long statusRespTimeout;
+
     static {
         String property = AccessController.doPrivileged(
                     new GetPropertyAction("jdk.tls.ephemeralDHKeySize"));
@@ -159,6 +172,11 @@
                 activeProtocolVersion, isInitialHandshake, secureRenegotiation,
                 clientVerifyData, serverVerifyData);
         doClientAuth = clientAuth;
+        statusRespTimeout = AccessController.doPrivileged(
+                    new GetLongAction("jdk.tls.stapling.responseTimeout",
+                        DEFAULT_STATUS_RESP_DELAY));
+        statusRespTimeout = statusRespTimeout >= 0 ? statusRespTimeout :
+                DEFAULT_STATUS_RESP_DELAY;
     }
 
     /*
@@ -176,6 +194,11 @@
                 activeProtocolVersion, isInitialHandshake, secureRenegotiation,
                 clientVerifyData, serverVerifyData, isDTLS);
         doClientAuth = clientAuth;
+        statusRespTimeout = AccessController.doPrivileged(
+                    new GetLongAction("jdk.tls.stapling.responseTimeout",
+                        DEFAULT_STATUS_RESP_DELAY));
+        statusRespTimeout = statusRespTimeout >= 0 ? statusRespTimeout :
+                DEFAULT_STATUS_RESP_DELAY;
     }
 
     /*
@@ -529,6 +552,16 @@
             }
         }
 
+        // Check if the client has asserted the status_request[_v2] extension(s)
+        CertStatusReqExtension statReqExt = (CertStatusReqExtension)
+                    mesg.extensions.get(ExtensionType.EXT_STATUS_REQUEST);
+        CertStatusReqListV2Extension statReqExtV2 =
+                (CertStatusReqListV2Extension)mesg.extensions.get(
+                        ExtensionType.EXT_STATUS_REQUEST_V2);
+        // Keep stapling active if at least one of the extensions has been set
+        staplingActive = enableStatusRequestExtension &&
+                (statReqExt != null || statReqExtV2 != null);
+
         /*
          * FIRST, construct the ServerHello using the options and priorities
          * from the ClientHello.  Update the (pending) cipher spec as we do
@@ -825,6 +858,69 @@
             m1.extensions.add(maxFragLenExt);
         }
 
+        StatusRequestType statReqType = null;
+        StatusRequest statReqData = null;
+        if (staplingActive && !resumingSession) {
+            ExtensionType statusRespExt = ExtensionType.EXT_STATUS_REQUEST;
+
+            // Determine which type of stapling we are doing and assert the
+            // proper extension in the server hello.
+            // Favor status_request_v2 over status_request and ocsp_multi
+            // over ocsp.
+            // If multiple ocsp or ocsp_multi types exist, select the first
+            // instance of a given type
+            if (statReqExtV2 != null) {             // RFC 6961 stapling
+                statusRespExt = ExtensionType.EXT_STATUS_REQUEST_V2;
+                List<CertStatusReqItemV2> reqItems =
+                        statReqExtV2.getRequestItems();
+                int ocspIdx = -1;
+                int ocspMultiIdx = -1;
+                for (int pos = 0; pos < reqItems.size(); pos++) {
+                    CertStatusReqItemV2 item = reqItems.get(pos);
+                    if (ocspIdx < 0 && item.getType() ==
+                            StatusRequestType.OCSP) {
+                        ocspIdx = pos;
+                    } else if (ocspMultiIdx < 0 && item.getType() ==
+                            StatusRequestType.OCSP_MULTI) {
+                        ocspMultiIdx = pos;
+                    }
+                }
+                if (ocspMultiIdx >= 0) {
+                    statReqType = reqItems.get(ocspMultiIdx).getType();
+                    statReqData = reqItems.get(ocspMultiIdx).getRequest();
+                } else if (ocspIdx >= 0) {
+                    statReqType = reqItems.get(ocspIdx).getType();
+                    statReqData = reqItems.get(ocspIdx).getRequest();
+                } else {
+                    // Some unknown type.  We will not do stapling for
+                    // this connection since we cannot understand the
+                    // requested type.
+                    staplingActive = false;
+                }
+            } else {                                // RFC 6066 stapling
+                statReqType = StatusRequestType.OCSP;
+                statReqData = statReqExt.getRequest();
+            }
+
+            if (statReqType != null && statReqData != null) {
+                // Next, attempt to obtain status responses
+                StatusResponseManager statRespMgr =
+                        sslContext.getStatusResponseManager();
+                responseMap = statRespMgr.get(statReqType, statReqData, certs,
+                        statusRespTimeout, TimeUnit.MILLISECONDS);
+                if (!responseMap.isEmpty()) {
+                    // We now can safely assert status_request[_v2] in our
+                    // ServerHello, and know for certain that we can provide
+                    // responses back to this client for this connection.
+                    if (statusRespExt == ExtensionType.EXT_STATUS_REQUEST) {
+                        m1.extensions.add(new CertStatusReqExtension());
+                    } else if (statusRespExt == ExtensionType.EXT_STATUS_REQUEST_V2) {
+                        m1.extensions.add(new CertStatusReqListV2Extension());
+                    }
+                }
+            }
+        }
+
         if (debug != null && Debug.isOn("handshake")) {
             m1.print(System.out);
             System.out.println("Cipher suite:  " + session.getSuite());
@@ -886,6 +982,32 @@
             }
         }
 
+        /**
+         * The CertificateStatus message ... only if it is needed.
+         * This would only be needed if we've established that this handshake
+         * supports status stapling and there is at least one response to
+         * return to the client.
+         */
+        if (staplingActive && !responseMap.isEmpty()) {
+            try {
+                CertificateStatus csMsg = new CertificateStatus(statReqType,
+                        certs, responseMap);
+                if (debug != null && Debug.isOn("handshake")) {
+                    csMsg.print(System.out);
+                }
+                csMsg.write(output);
+                handshakeState.update(csMsg, resumingSession);
+                responseMap = null;
+            } catch (SSLException ssle) {
+                // We don't want the exception to be fatal, we just won't
+                // send the message if we fail on construction.
+                if (debug != null && Debug.isOn("handshake")) {
+                    System.out.println("Failed during CertificateStatus " +
+                            "construction: " + ssle);
+                }
+            }
+        }
+
         /*
          * THIRD, the ServerKeyExchange message ... iff it's needed.
          *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/StatusRequest.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+
+/*
+ * RFC 6066 defines the TLS extension,"status_request" (type 0x5),
+ * which allows the client to request that the server perform OCSP
+ * on the client's behalf.
+ *
+ * This class is an interface for multiple types of StatusRequests
+ * (e.g. OCSPStatusRequest).
+ */
+interface StatusRequest {
+
+    /**
+     * Obtain the length of the {@code StatusRequest} object in encoded form
+     *
+     * @return the length of the {@code StatusRequest} object in encoded form
+     */
+    int length();
+
+    /**
+     * Place the encoded {@code StatusRequest} bytes into the
+     *      {@code HandshakeOutputStream}
+     *
+     * @param s the target {@code HandshakeOutputStream}
+     *
+     * @throws IOException if any encoding error occurs
+     */
+    void send(HandshakeOutStream s) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/StatusRequestType.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+final class StatusRequestType {
+
+    final int id;
+    final String name;
+    static List<StatusRequestType> knownTypes = new ArrayList<>(4);
+
+    private StatusRequestType(int id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    static StatusRequestType get(int id) {
+        for (StatusRequestType ext : knownTypes) {
+            if (ext.id == id) {
+                return ext;
+            }
+        }
+        return new StatusRequestType(id, "type_" + id);
+    }
+
+    private static StatusRequestType e(int id, String name) {
+        StatusRequestType ext = new StatusRequestType(id, name);
+        knownTypes.add(ext);
+        return ext;
+    }
+
+    @Override
+    public String toString() {
+        return (name == null || name.isEmpty()) ?
+                String.format("Unknown (0x%04X", id) : name;
+    }
+
+    // Status request types defined in RFC 6066 and 6961
+    final static StatusRequestType OCSP = e(0x01, "ocsp");
+    final static StatusRequestType OCSP_MULTI = e(0x02, "ocsp_multi");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.AccessController;
+import java.security.cert.X509Certificate;
+import java.security.cert.Extension;
+import java.util.*;
+import java.util.concurrent.*;
+
+import sun.security.provider.certpath.CertId;
+import sun.security.provider.certpath.OCSP;
+import sun.security.provider.certpath.OCSPResponse;
+import sun.security.provider.certpath.ResponderId;
+import sun.security.util.Cache;
+import sun.security.x509.PKIXExtensions;
+import sun.security.x509.SerialNumber;
+import sun.security.action.GetBooleanAction;
+import sun.security.action.GetIntegerAction;
+import sun.security.action.GetPropertyAction;
+
+final class StatusResponseManager {
+    private static final int DEFAULT_CORE_THREADS = 8;
+    private static final int DEFAULT_CACHE_SIZE = 256;
+    private static final int DEFAULT_CACHE_LIFETIME = 3600;         // seconds
+    private static final Debug debug = Debug.getInstance("ssl");
+
+    private final ScheduledThreadPoolExecutor threadMgr;
+    private final Cache<CertId, ResponseCacheEntry> responseCache;
+    private final URI defaultResponder;
+    private final boolean respOverride;
+    private final int cacheCapacity;
+    private final int cacheLifetime;
+    private final boolean ignoreExtensions;
+
+    /**
+     * Create a StatusResponseManager with default parameters.
+     */
+    StatusResponseManager() {
+        int cap = AccessController.doPrivileged(
+                new GetIntegerAction("jdk.tls.stapling.cacheSize",
+                    DEFAULT_CACHE_SIZE));
+        cacheCapacity = cap > 0 ? cap : 0;
+
+        int life = AccessController.doPrivileged(
+                new GetIntegerAction("jdk.tls.stapling.cacheLifetime",
+                    DEFAULT_CACHE_LIFETIME));
+        cacheLifetime = life > 0 ? life : 0;
+
+        String uriStr = AccessController.doPrivileged(
+                new GetPropertyAction("jdk.tls.stapling.responderURI"));
+        URI tmpURI;
+        try {
+            tmpURI = ((uriStr != null && !uriStr.isEmpty()) ?
+                    new URI(uriStr) : null);
+        } catch (URISyntaxException urise) {
+            tmpURI = null;
+        }
+        defaultResponder = tmpURI;
+
+        respOverride = AccessController.doPrivileged(
+                new GetBooleanAction("jdk.tls.stapling.responderOverride"));
+        ignoreExtensions = AccessController.doPrivileged(
+                new GetBooleanAction("jdk.tls.stapling.ignoreExtensions"));
+
+        threadMgr = new ScheduledThreadPoolExecutor(DEFAULT_CORE_THREADS,
+                new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = Executors.defaultThreadFactory().newThread(r);
+                t.setDaemon(true);
+                return t;
+            }
+        });
+        threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+        threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+        threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS);
+        responseCache = Cache.newSoftMemoryCache(cacheCapacity, cacheLifetime);
+    }
+
+    /**
+     * Get the current cache lifetime setting
+     *
+     * @return the current cache lifetime value
+     */
+    int getCacheLifetime() {
+        return cacheLifetime;
+    }
+
+    /**
+     * Get the current maximum cache size.
+     *
+     * @return the current maximum cache size
+     */
+    int getCacheCapacity() {
+        return cacheCapacity;
+    }
+
+    /**
+     * Get the default OCSP responder URI, if previously set.
+     *
+     * @return the current default OCSP responder URI, or {@code null} if
+     *      it has not been set.
+     */
+    URI getDefaultResponder() {
+        return defaultResponder;
+    }
+
+    /**
+     * Get the URI override setting
+     *
+     * @return {@code true} if URI override has been set, {@code false}
+     * otherwise.
+     */
+    boolean getURIOverride() {
+        return respOverride;
+    }
+
+    /**
+     * Get the ignore extensions setting.
+     *
+     * @return {@code true} if the {@code StatusResponseManager} will not
+     * pass OCSP Extensions in the TLS {@code status_request[_v2]} extensions,
+     * {@code false} if extensions will be passed (the default).
+     */
+    boolean getIgnoreExtensions() {
+        return ignoreExtensions;
+    }
+
+    /**
+     * Clear the status response cache
+     */
+    void clear() {
+        debugLog("Clearing response cache");
+        responseCache.clear();
+    }
+
+    /**
+     * Returns the number of currently valid objects in the response cache.
+     *
+     * @return the number of valid objects in the response cache.
+     */
+    int size() {
+        return responseCache.size();
+    }
+
+    /**
+     * Obtain the URI use by the {@code StatusResponseManager} during lookups.
+     * This method takes into account not only the AIA extension from a
+     * certificate to be checked, but also any default URI and possible
+     * override settings for the response manager.
+     *
+     * @param cert the subject to get the responder URI from
+     *
+     * @return a {@code URI} containing the address to the OCSP responder, or
+     *      {@code null} if no AIA extension exists in the certificate and no
+     *      default responder has been configured.
+     *
+     * @throws NullPointerException if {@code cert} is {@code null}.
+     */
+    URI getURI(X509Certificate cert) {
+        Objects.requireNonNull(cert);
+
+        if (cert.getExtensionValue(
+                PKIXExtensions.OCSPNoCheck_Id.toString()) != null) {
+            debugLog("OCSP NoCheck extension found.  OCSP will be skipped");
+            return null;
+        } else if (defaultResponder != null && respOverride) {
+            debugLog("Responder override: URI is " + defaultResponder);
+            return defaultResponder;
+        } else {
+            URI certURI = OCSP.getResponderURI(cert);
+            return (certURI != null ? certURI : defaultResponder);
+        }
+    }
+
+    /**
+     * Shutdown the thread pool
+     */
+    void shutdown() {
+        debugLog("Shutting down " + threadMgr.getActiveCount() +
+                " active threads");
+        threadMgr.shutdown();
+    }
+
+    /**
+     * Get a list of responses for a chain of certificates.
+     * This will find OCSP responses from the cache, or failing that, directly
+     * contact the OCSP responder.  It is assumed that the certificates in
+     * the provided chain are in their proper order (from end-entity to
+     * trust anchor).
+     *
+     * @param type the type of request being made of the
+     *      {@code StatusResponseManager}
+     * @param request the {@code StatusRequest} from the status_request or
+     *      status_request_v2 ClientHello extension.  A value of {@code null}
+     *      is interpreted as providing no responder IDs or extensions.
+     * @param chain an array of 2 or more certificates.  Each certificate must
+     *      be issued by the next certificate in the chain.
+     * @param delay the number of time units to delay before returning
+     *      responses.
+     * @param unit the unit of time applied to the {@code delay} parameter
+     *
+     * @return an unmodifiable {@code Map} containing the certificate and
+     *      its usually
+     *
+     * @throws SSLHandshakeException if an unsupported {@code StatusRequest}
+     *      is provided.
+     */
+    Map<X509Certificate, byte[]> get(StatusRequestType type,
+            StatusRequest request, X509Certificate[] chain, long delay,
+            TimeUnit unit) {
+        Map<X509Certificate, byte[]> responseMap = new HashMap<>();
+        List<OCSPFetchCall> requestList = new ArrayList<>();
+
+        debugLog("Beginning check: Type = " + type + ", Chain length = " +
+                chain.length);
+
+        // It is assumed that the caller has ordered the certs in the chain
+        // in the proper order (each certificate is issued by the next entry
+        // in the provided chain).
+        if (chain.length < 2) {
+            return Collections.emptyMap();
+        }
+
+        if (type == StatusRequestType.OCSP) {
+            try {
+                // For type OCSP, we only check the end-entity certificate
+                OCSPStatusRequest ocspReq = (OCSPStatusRequest)request;
+                CertId cid = new CertId(chain[1],
+                        new SerialNumber(chain[0].getSerialNumber()));
+                ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq);
+                if (cacheEntry != null) {
+                    responseMap.put(chain[0], cacheEntry.ocspBytes);
+                } else {
+                    StatusInfo sInfo = new StatusInfo(chain[0], cid);
+                    requestList.add(new OCSPFetchCall(sInfo, ocspReq));
+                }
+            } catch (IOException exc) {
+                debugLog("Exception during CertId creation: " + exc);
+            }
+        } else if (type == StatusRequestType.OCSP_MULTI) {
+            // For type OCSP_MULTI, we check every cert in the chain that
+            // has a direct issuer at the next index.  We won't have an issuer
+            // certificate for the last certificate in the chain and will
+            // not be able to create a CertId because of that.
+            OCSPStatusRequest ocspReq = (OCSPStatusRequest)request;
+            int ctr;
+            for (ctr = 0; ctr < chain.length - 1; ctr++) {
+                try {
+                    // The cert at "ctr" is the subject cert, "ctr + 1" is the
+                    // issuer certificate.
+                    CertId cid = new CertId(chain[ctr + 1],
+                            new SerialNumber(chain[ctr].getSerialNumber()));
+                    ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq);
+                    if (cacheEntry != null) {
+                        responseMap.put(chain[ctr], cacheEntry.ocspBytes);
+                    } else {
+                        StatusInfo sInfo = new StatusInfo(chain[ctr], cid);
+                        requestList.add(new OCSPFetchCall(sInfo, ocspReq));
+                    }
+                } catch (IOException exc) {
+                    debugLog("Exception during CertId creation: " + exc);
+                }
+            }
+        } else {
+            debugLog("Unsupported status request type: " + type);
+        }
+
+        // If we were able to create one or more Fetches, go and run all
+        // of them in separate threads.  For all the threads that completed
+        // in the allotted time, put those status responses into the returned
+        // Map.
+        if (!requestList.isEmpty()) {
+            try {
+                // Set a bunch of threads to go do the fetching
+                List<Future<StatusInfo>> resultList =
+                        threadMgr.invokeAll(requestList, delay, unit);
+
+                // Go through the Futures and from any non-cancelled task,
+                // get the bytes and attach them to the responseMap.
+                for (Future<StatusInfo> task : resultList) {
+                    if (task.isDone()) {
+                        if (!task.isCancelled()) {
+                            StatusInfo info = task.get();
+                            if (info != null && info.responseData != null) {
+                                responseMap.put(info.cert,
+                                        info.responseData.ocspBytes);
+                            } else {
+                                debugLog("Completed task had no response data");
+                            }
+                        } else {
+                            debugLog("Found cancelled task");
+                        }
+                    }
+                }
+            } catch (InterruptedException | ExecutionException exc) {
+                // Not sure what else to do here
+                debugLog("Exception when getting data: " + exc);
+            }
+        }
+
+        return Collections.unmodifiableMap(responseMap);
+    }
+
+    /**
+     * Check the cache for a given {@code CertId}.
+     *
+     * @param cid the CertId of the response to look up
+     * @param ocspRequest the OCSP request structure sent by the client
+     *      in the TLS status_request[_v2] hello extension.
+     *
+     * @return the {@code ResponseCacheEntry} for a specific CertId, or
+     *      {@code null} if it is not found or a nonce extension has been
+     *      requested by the caller.
+     */
+    private ResponseCacheEntry getFromCache(CertId cid,
+            OCSPStatusRequest ocspRequest) {
+        // Determine if the nonce extension is present in the request.  If
+        // so, then do not attempt to retrieve the response from the cache.
+        for (Extension ext : ocspRequest.getExtensions()) {
+            if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) {
+                debugLog("Nonce extension found, skipping cache check");
+                return null;
+            }
+        }
+
+        ResponseCacheEntry respEntry = responseCache.get(cid);
+
+        // If the response entry has a nextUpdate and it has expired
+        // before the cache expiration, purge it from the cache
+        // and do not return it as a cache hit.
+        if (respEntry != null && respEntry.nextUpdate != null &&
+                respEntry.nextUpdate.before(new Date())) {
+            debugLog("nextUpdate threshold exceeded, purging from cache");
+            respEntry = null;
+        }
+
+        debugLog("Check cache for SN" + cid.getSerialNumber() + ": " +
+                (respEntry != null ? "HIT" : "MISS"));
+        return respEntry;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("StatusResponseManager: ");
+
+        sb.append("Core threads: ").append(threadMgr.getCorePoolSize());
+        sb.append(", Cache timeout: ");
+        if (cacheLifetime > 0) {
+            sb.append(cacheLifetime).append(" seconds");
+        } else {
+            sb.append(" indefinite");
+        }
+
+        sb.append(", Cache MaxSize: ");
+        if (cacheCapacity > 0) {
+            sb.append(cacheCapacity).append(" items");
+        } else {
+            sb.append(" unbounded");
+        }
+
+        sb.append(", Default URI: ");
+        if (defaultResponder != null) {
+            sb.append(defaultResponder);
+        } else {
+            sb.append("NONE");
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Log messages through the SSL Debug facility.
+     *
+     * @param message the message to be displayed
+     */
+    static void debugLog(String message) {
+        if (debug != null && Debug.isOn("respmgr")) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("[").append(Thread.currentThread().getName());
+            sb.append("] ").append(message);
+            System.out.println(sb.toString());
+        }
+    }
+
+    /**
+     * Inner class used to group request and response data.
+     */
+    class StatusInfo {
+        final X509Certificate cert;
+        final CertId cid;
+        final URI responder;
+        ResponseCacheEntry responseData;
+
+        /**
+         * Create a StatusInfo object from certificate data.
+         *
+         * @param subjectCert the certificate to be checked for revocation
+         * @param issuerCert the issuer of the {@code subjectCert}
+         *
+         * @throws IOException if CertId creation from the certificates fails
+         */
+        StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert)
+                throws IOException {
+            this(subjectCert, new CertId(issuerCert,
+                    new SerialNumber(subjectCert.getSerialNumber())));
+        }
+
+        /**
+         * Create a StatusInfo object from an existing subject certificate
+         * and its corresponding CertId.
+         *
+         * @param subjectCert the certificate to be checked for revocation
+         * @param cid the CertId for {@code subjectCert}
+         */
+        StatusInfo(X509Certificate subjectCert, CertId certId) {
+            cert = subjectCert;
+            cid = certId;
+            responder = getURI(cert);
+            responseData = null;
+        }
+
+        /**
+         * Copy constructor (used primarily for rescheduling).
+         * This will do a member-wise copy with the exception of the
+         * responseData and extensions fields, which should not persist
+         * in a rescheduled fetch.
+         *
+         * @param orig the original {@code StatusInfo}
+         */
+        StatusInfo(StatusInfo orig) {
+            this.cert = orig.cert;
+            this.cid = orig.cid;
+            this.responder = orig.responder;
+            this.responseData = null;
+        }
+
+        /**
+         * Return a String representation of the {@code StatusInfo}
+         *
+         * @return a {@code String} representation of this object
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder("StatusInfo:");
+            sb.append("\n\tCert: ").append(this.cert.getSubjectX500Principal());
+            sb.append("\n\tSerial: ").append(this.cert.getSerialNumber());
+            sb.append("\n\tResponder: ").append(this.responder);
+            sb.append("\n\tResponse data: ").append(this.responseData != null ?
+                    (this.responseData.ocspBytes.length + " bytes") : "<NULL>");
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Static nested class used as the data kept in the response cache.
+     */
+    static class ResponseCacheEntry {
+        final OCSPResponse.ResponseStatus status;
+        final byte[] ocspBytes;
+        final Date nextUpdate;
+        final OCSPResponse.SingleResponse singleResp;
+        final ResponderId respId;
+
+        /**
+         * Create a new cache entry from the raw bytes of the response
+         *
+         * @param responseBytes the DER encoding for the OCSP response
+         *
+         * @throws IOException if an {@code OCSPResponse} cannot be created from
+         *      the encoded bytes.
+         */
+        ResponseCacheEntry(byte[] responseBytes, CertId cid)
+                throws IOException {
+            Objects.requireNonNull(responseBytes,
+                    "Non-null responseBytes required");
+            Objects.requireNonNull(cid, "Non-null Cert ID required");
+
+            ocspBytes = responseBytes.clone();
+            OCSPResponse oResp = new OCSPResponse(ocspBytes);
+            status = oResp.getResponseStatus();
+            respId = oResp.getResponderId();
+            singleResp = oResp.getSingleResponse(cid);
+            if (status == OCSPResponse.ResponseStatus.SUCCESSFUL) {
+                if (singleResp != null) {
+                    // Pull out the nextUpdate field in advance because the
+                    // Date is cloned.
+                    nextUpdate = singleResp.getNextUpdate();
+                } else {
+                    throw new IOException("Unable to find SingleResponse for " +
+                            "SN " + cid.getSerialNumber());
+                }
+            } else {
+                nextUpdate = null;
+            }
+        }
+    }
+
+    /**
+     * Inner Callable class that does the actual work of looking up OCSP
+     * responses, first looking at the cache and doing OCSP requests if
+     * a cache miss occurs.
+     */
+    class OCSPFetchCall implements Callable<StatusInfo> {
+        StatusInfo statInfo;
+        OCSPStatusRequest ocspRequest;
+        List<Extension> extensions;
+        List<ResponderId> responderIds;
+
+        /**
+         * A constructor that builds the OCSPFetchCall from the provided
+         * StatusInfo and information from the status_request[_v2] extension.
+         *
+         * @param info the {@code StatusInfo} containing the subject
+         * certificate, CertId, and other supplemental info.
+         * @param request the {@code OCSPStatusRequest} containing any
+         * responder IDs and extensions.
+         */
+        public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) {
+            statInfo = Objects.requireNonNull(info,
+                    "Null StatusInfo not allowed");
+            ocspRequest = Objects.requireNonNull(request,
+                    "Null OCSPStatusRequest not allowed");
+            extensions = ocspRequest.getExtensions();
+            responderIds = ocspRequest.getResponderIds();
+        }
+
+        /**
+         * Get an OCSP response, either from the cache or from a responder.
+         *
+         * @return The StatusInfo object passed into the {@code OCSPFetchCall}
+         * constructor, with the {@code responseData} field filled in with the
+         * response or {@code null} if no response can be obtained.
+         */
+        @Override
+        public StatusInfo call() {
+            debugLog("Starting fetch for SN " + statInfo.cid.getSerialNumber());
+            try {
+                ResponseCacheEntry cacheEntry;
+                List<Extension> extsToSend;
+
+                if (statInfo.responder == null) {
+                    // If we have no URI then there's nothing to do but return
+                    debugLog("Null URI detected, OCSP fetch aborted.");
+                    return statInfo;
+                } else {
+                    debugLog("Attempting fetch from " + statInfo.responder);
+                }
+
+                // If the StatusResponseManager has been configured to not
+                // forward extensions, then set extensions to an empty list.
+                // We will forward the extensions unless one of two conditions
+                // occur: (1) The jdk.tls.stapling.ignoreExtensions property is
+                // true or (2) There is a non-empty ResponderId list.
+                // ResponderId selection is a feature that will be
+                // supported in the future.
+                extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ?
+                        Collections.emptyList() : extensions;
+
+                byte[] respBytes = OCSP.getOCSPBytes(
+                        Collections.singletonList(statInfo.cid),
+                        statInfo.responder, extsToSend);
+
+                if (respBytes != null) {
+                    // Place the data into the response cache
+                    cacheEntry = new ResponseCacheEntry(respBytes,
+                            statInfo.cid);
+
+                    // Get the response status and act on it appropriately
+                    debugLog("OCSP Status: " + cacheEntry.status +
+                            " (" + respBytes.length + " bytes)");
+                    if (cacheEntry.status ==
+                            OCSPResponse.ResponseStatus.SUCCESSFUL) {
+                        // Set the response in the returned StatusInfo
+                        statInfo.responseData = cacheEntry;
+
+                        // Add the response to the cache (if applicable)
+                        addToCache(statInfo.cid, cacheEntry);
+                    }
+                } else {
+                    debugLog("No data returned from OCSP Responder");
+                }
+            } catch (IOException ioe) {
+                debugLog("Caught exception: " + ioe);
+            }
+
+            return statInfo;
+        }
+
+        /**
+         * Add a response to the cache.
+         *
+         * @param certId The {@code CertId} for the OCSP response
+         * @param entry A cache entry containing the response bytes and
+         *      the {@code OCSPResponse} built from those bytes.
+         */
+        private void addToCache(CertId certId, ResponseCacheEntry entry) {
+            // If no cache lifetime has been set on entries then
+            // don't cache this response if there is no nextUpdate field
+            if (entry.nextUpdate == null && cacheLifetime == 0) {
+                debugLog("Not caching this OCSP response");
+            } else {
+                responseCache.put(certId, entry);
+                debugLog("Added response for SN " + certId.getSerialNumber() +
+                        " to cache");
+            }
+        }
+
+        /**
+         * Determine the delay to use when scheduling the task that will
+         * update the OCSP response.  This is the shorter time between the
+         * cache lifetime and the nextUpdate.  If no nextUpdate is present in
+         * the response, then only the cache lifetime is used.
+         * If cache timeouts are disabled (a zero value) and there's no
+         * nextUpdate, then the entry is not cached and no rescheduling will
+         * take place.
+         *
+         * @param nextUpdate a {@code Date} object corresponding to the
+         *      next update time from a SingleResponse.
+         *
+         * @return the number of seconds of delay before the next fetch
+         *      should be executed.  A zero value means that the fetch
+         *      should happen immediately, while a value less than zero
+         *      indicates no rescheduling should be done.
+         */
+        private long getNextTaskDelay(Date nextUpdate) {
+            long delaySec;
+            int lifetime = getCacheLifetime();
+
+            if (nextUpdate != null) {
+                long nuDiffSec = (nextUpdate.getTime() -
+                        System.currentTimeMillis()) / 1000;
+                delaySec = lifetime > 0 ? Long.min(nuDiffSec, lifetime) :
+                        nuDiffSec;
+            } else {
+                delaySec = lifetime > 0 ? lifetime : -1;
+            }
+
+            return delaySec;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/UnknownStatusRequest.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+
+final class UnknownStatusRequest implements StatusRequest  {
+
+    private final byte[] data;
+
+    UnknownStatusRequest(HandshakeInStream s, int len) throws IOException {
+        data = new byte[len];
+        if (len > 0) {
+            s.read(data);
+        }
+    }
+
+    UnknownStatusRequest(byte[] requestBytes) {
+        data = requestBytes;
+    }
+
+    @Override
+    public int length() {
+        return data.length;
+    }
+
+    @Override
+    public void send(HandshakeOutStream s) throws IOException {
+        // A raw write of the data
+        s.write(data);
+    }
+
+    @Override
+    public String toString() {
+        return "Unsupported StatusRequest, data: " +
+            Debug.toString(data);
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Wed Aug 05 12:19:38 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -184,6 +184,7 @@
         Validator v = checkTrustedInit(chain, authType, isClient);
 
         AlgorithmConstraints constraints = null;
+        List<byte[]> responseList = Collections.emptyList();
         if ((socket != null) && socket.isConnected() &&
                                         (socket instanceof SSLSocket)) {
 
@@ -195,7 +196,7 @@
 
             // check endpoint identity
             String identityAlg = sslSocket.getSSLParameters().
-                                        getEndpointIdentificationAlgorithm();
+                    getEndpointIdentificationAlgorithm();
             if (identityAlg != null && identityAlg.length() != 0) {
                 checkIdentity(session, chain[0], identityAlg, isClient,
                         getRequestedServerNames(socket));
@@ -204,7 +205,7 @@
             // create the algorithm constraints
             ProtocolVersion protocolVersion =
                 ProtocolVersion.valueOf(session.getProtocol());
-            if (protocolVersion.useTLS12PlusSpec()) {
+            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
@@ -220,13 +221,21 @@
             } else {
                 constraints = new SSLAlgorithmConstraints(sslSocket, false);
             }
+
+            // Grab any stapled OCSP responses for use in validation
+            if (session instanceof ExtendedSSLSession) {
+                responseList =
+                        ((ExtendedSSLSession)session).getStatusResponses();
+            }
         }
 
         X509Certificate[] trustedChain = null;
         if (isClient) {
-            trustedChain = validate(v, chain, constraints, null);
+            trustedChain = validate(v, chain, Collections.emptyList(),
+                    constraints, null);
         } else {
-            trustedChain = validate(v, chain, constraints, authType);
+            trustedChain = validate(v, chain, responseList, constraints,
+                    authType);
         }
         if (debug != null && Debug.isOn("trustmanager")) {
             System.out.println("Found trusted certificate:");
@@ -239,6 +248,7 @@
         Validator v = checkTrustedInit(chain, authType, isClient);
 
         AlgorithmConstraints constraints = null;
+        List<byte[]> responseList = Collections.emptyList();
         if (engine != null) {
             SSLSession session = engine.getHandshakeSession();
             if (session == null) {
@@ -247,7 +257,7 @@
 
             // check endpoint identity
             String identityAlg = engine.getSSLParameters().
-                                        getEndpointIdentificationAlgorithm();
+                    getEndpointIdentificationAlgorithm();
             if (identityAlg != null && identityAlg.length() != 0) {
                 checkIdentity(session, chain[0], identityAlg, isClient,
                         getRequestedServerNames(engine));
@@ -256,7 +266,7 @@
             // create the algorithm constraints
             ProtocolVersion protocolVersion =
                 ProtocolVersion.valueOf(session.getProtocol());
-            if (protocolVersion.useTLS12PlusSpec()) {
+            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
@@ -272,13 +282,21 @@
             } else {
                 constraints = new SSLAlgorithmConstraints(engine, false);
             }
+
+            // Grab any stapled OCSP responses for use in validation
+            if (session instanceof ExtendedSSLSession) {
+                responseList =
+                        ((ExtendedSSLSession)session).getStatusResponses();
+            }
         }
 
         X509Certificate[] trustedChain = null;
         if (isClient) {
-            trustedChain = validate(v, chain, constraints, null);
+            trustedChain = validate(v, chain, Collections.emptyList(),
+                    constraints, null);
         } else {
-            trustedChain = validate(v, chain, constraints, authType);
+            trustedChain = validate(v, chain, responseList, constraints,
+                    authType);
         }
         if (debug != null && Debug.isOn("trustmanager")) {
             System.out.println("Found trusted certificate:");
@@ -317,11 +335,12 @@
     }
 
     private static X509Certificate[] validate(Validator v,
-            X509Certificate[] chain, AlgorithmConstraints constraints,
-            String authType) throws CertificateException {
+            X509Certificate[] chain, List<byte[]> responseList,
+            AlgorithmConstraints constraints, String authType)
+            throws CertificateException {
         Object o = JsseJce.beginFipsProvider();
         try {
-            return v.validate(chain, null, constraints, authType);
+            return v.validate(chain, null, responseList, constraints, authType);
         } finally {
             JsseJce.endFipsProvider(o);
         }
--- a/jdk/src/java.base/share/classes/sun/security/validator/PKIXValidator.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/validator/PKIXValidator.java	Wed Aug 05 12:19:38 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, 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
@@ -186,6 +186,7 @@
     @Override
     X509Certificate[] engineValidate(X509Certificate[] chain,
             Collection<X509Certificate> otherCerts,
+            List<byte[]> responseList,
             AlgorithmConstraints constraints,
             Object parameter) throws CertificateException {
         if ((chain == null) || (chain.length == 0)) {
@@ -193,7 +194,7 @@
                 ("null or zero-length certificate chain");
         }
 
-        // add  new algorithm constraints checker
+        // add new algorithm constraints checker
         PKIXBuilderParameters pkixParameters =
                     (PKIXBuilderParameters) parameterTemplate.clone();
         AlgorithmChecker algorithmChecker = null;
@@ -202,6 +203,11 @@
             pkixParameters.addCertPathChecker(algorithmChecker);
         }
 
+        // attach it to the PKIXBuilderParameters.
+        if (!responseList.isEmpty()) {
+            addResponses(pkixParameters, chain, responseList);
+        }
+
         // check that chain is in correct order and check if chain contains
         // trust anchor
         X500Principal prevIssuer = null;
@@ -369,4 +375,70 @@
                 ("PKIX path building failed: " + e.toString(), e);
         }
     }
+
+    /**
+     * For OCSP Stapling, add responses that came in during the handshake
+     * into a {@code PKIXRevocationChecker} so we can evaluate them.
+     *
+     * @param pkixParams the pkixParameters object that will be used in
+     * path validation.
+     * @param chain the chain of certificates to verify
+     * @param responseList a {@code List} of zero or more byte arrays, each
+     * one being a DER-encoded OCSP response (per RFC 6960).  Entries
+     * in the List must match the order of the certificates in the
+     * chain parameter.
+     */
+    private static void addResponses(PKIXBuilderParameters pkixParams,
+            X509Certificate[] chain, List<byte[]> responseList) {
+
+        if (pkixParams.isRevocationEnabled()) {
+            try {
+                // Make a modifiable copy of the CertPathChecker list
+                PKIXRevocationChecker revChecker = null;
+                List<PKIXCertPathChecker> checkerList =
+                        new ArrayList<>(pkixParams.getCertPathCheckers());
+
+                // Find the first PKIXRevocationChecker in the list
+                for (PKIXCertPathChecker checker : checkerList) {
+                    if (checker instanceof PKIXRevocationChecker) {
+                        revChecker = (PKIXRevocationChecker)checker;
+                        break;
+                    }
+                }
+
+                // If we still haven't found one, make one
+                if (revChecker == null) {
+                    revChecker = (PKIXRevocationChecker)CertPathValidator.
+                            getInstance("PKIX").getRevocationChecker();
+                    checkerList.add(revChecker);
+                }
+
+                // Each response in the list should be in parallel with
+                // the certificate list.  If there is a zero-length response
+                // treat it as being absent.  If the user has provided their
+                // own PKIXRevocationChecker with pre-populated responses, do
+                // not overwrite them with the ones from the handshake.
+                Map<X509Certificate, byte[]> responseMap =
+                        revChecker.getOcspResponses();
+                int limit = Integer.min(chain.length, responseList.size());
+                for (int idx = 0; idx < limit; idx++) {
+                    byte[] respBytes = responseList.get(idx);
+                    if (respBytes != null && respBytes.length > 0 &&
+                            !responseMap.containsKey(chain[idx])) {
+                        responseMap.put(chain[idx], respBytes);
+                    }
+                }
+
+                // Add the responses and push it all back into the
+                // PKIXBuilderParameters
+                revChecker.setOcspResponses(responseMap);
+                pkixParams.setCertPathCheckers(checkerList);
+            } catch (NoSuchAlgorithmException exc) {
+                // This should not occur, but if it does happen then
+                // stapled OCSP responses won't be part of revocation checking.
+                // Clients can still fall back to other means of revocation
+                // checking.
+            }
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/security/validator/SimpleValidator.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/validator/SimpleValidator.java	Wed Aug 05 12:19:38 2015 -0700
@@ -122,6 +122,7 @@
     @Override
     X509Certificate[] engineValidate(X509Certificate[] chain,
             Collection<X509Certificate> otherCerts,
+            List<byte[]> responseList,
             AlgorithmConstraints constraints,
             Object parameter) throws CertificateException {
         if ((chain == null) || (chain.length == 0)) {
--- a/jdk/src/java.base/share/classes/sun/security/validator/Validator.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/validator/Validator.java	Wed Aug 05 12:19:38 2015 -0700
@@ -235,7 +235,8 @@
     public final X509Certificate[] validate(X509Certificate[] chain,
             Collection<X509Certificate> otherCerts, Object parameter)
             throws CertificateException {
-        return validate(chain, otherCerts, null, parameter);
+        return validate(chain, otherCerts, Collections.emptyList(), null,
+                parameter);
     }
 
     /**
@@ -244,6 +245,13 @@
      * @param chain the target certificate chain
      * @param otherCerts a Collection of additional X509Certificates that
      *        could be helpful for path building (or null)
+     * @param responseList a List of zero or more byte arrays, each
+     *        one being a DER-encoded OCSP response (per RFC 6960).  Entries
+     *        in the List must match the order of the certificates in the
+     *        chain parameter.  It is possible that fewer responses may be
+     *        in the list than are elements in {@code chain} and a missing
+     *        response for a matching element in {@code chain} can be
+     *        represented with a zero-length byte array.
      * @param constraints algorithm constraints for certification path
      *        processing
      * @param parameter an additional parameter with variant specific meaning.
@@ -257,9 +265,11 @@
      */
     public final X509Certificate[] validate(X509Certificate[] chain,
                 Collection<X509Certificate> otherCerts,
+                List<byte[]> responseList,
                 AlgorithmConstraints constraints,
                 Object parameter) throws CertificateException {
-        chain = engineValidate(chain, otherCerts, constraints, parameter);
+        chain = engineValidate(chain, otherCerts, responseList, constraints,
+                parameter);
 
         // omit EE extension check if EE cert is also trust anchor
         if (chain.length > 1) {
@@ -280,6 +290,7 @@
 
     abstract X509Certificate[] engineValidate(X509Certificate[] chain,
                 Collection<X509Certificate> otherCerts,
+                List<byte[]> responseList,
                 AlgorithmConstraints constraints,
                 Object parameter) throws CertificateException;
 
--- a/jdk/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/x509/PKIXExtensions.java	Wed Aug 05 12:19:38 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -77,6 +77,11 @@
     private static final int[] OCSPNoCheck_data = { 1, 3, 6, 1, 5, 5, 7,
                                                     48, 1, 5};
 
+    // Additional extensions under the PKIX arc that are not necessarily
+    // used in X.509 Certificates or CRLs.
+    private static final int OCSPNonce_data [] = { 1, 3, 6, 1, 5, 5, 7,
+                                                  48, 1, 2};
+
     /**
      * Identifies the particular public key used to sign the certificate.
      */
@@ -104,18 +109,20 @@
     public static final ObjectIdentifier CertificatePolicies_Id;
 
     /**
-     * Lists pairs of objectidentifiers of policies considered equivalent by the
-     * issuing CA to the subject CA.
+     * Lists pairs of object identifiers of policies considered equivalent by
+     * the issuing CA to the subject CA.
      */
     public static final ObjectIdentifier PolicyMappings_Id;
 
     /**
-     * Allows additional identities to be bound to the subject of the certificate.
+     * Allows additional identities to be bound to the subject of the
+     * certificate.
      */
     public static final ObjectIdentifier SubjectAlternativeName_Id;
 
     /**
-     * Allows additional identities to be associated with the certificate issuer.
+     * Allows additional identities to be associated with the certificate
+     * issuer.
      */
     public static final ObjectIdentifier IssuerAlternativeName_Id;
 
@@ -224,6 +231,12 @@
      */
     public static final ObjectIdentifier OCSPNoCheck_Id;
 
+    /**
+     * This extension is used to provide nonce data for OCSP requests
+     * or responses.
+     */
+    public static final ObjectIdentifier OCSPNonce_Id;
+
     static {
         AuthorityKey_Id = ObjectIdentifier.newInternal(AuthorityKey_data);
         SubjectKey_Id   = ObjectIdentifier.newInternal(SubjectKey_data);
@@ -266,5 +279,6 @@
             ObjectIdentifier.newInternal(SubjectInfoAccess_data);
         FreshestCRL_Id = ObjectIdentifier.newInternal(FreshestCRL_data);
         OCSPNoCheck_Id = ObjectIdentifier.newInternal(OCSPNoCheck_data);
+        OCSPNonce_Id = ObjectIdentifier.newInternal(OCSPNonce_data);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/testlibrary/CertificateBuilder.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.testlibrary;
+
+import java.io.*;
+import java.util.*;
+import java.security.*;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Extension;
+import javax.security.auth.x500.X500Principal;
+import java.math.BigInteger;
+
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+import sun.security.util.ObjectIdentifier;
+import sun.security.x509.AccessDescription;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.AuthorityInfoAccessExtension;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.SubjectKeyIdentifierExtension;
+import sun.security.x509.BasicConstraintsExtension;
+import sun.security.x509.ExtendedKeyUsageExtension;
+import sun.security.x509.DNSName;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.KeyUsageExtension;
+import sun.security.x509.SerialNumber;
+import sun.security.x509.SubjectAlternativeNameExtension;
+import sun.security.x509.URIName;
+import sun.security.x509.KeyIdentifier;
+
+/**
+ * Helper class that builds and signs X.509 certificates.
+ *
+ * A CertificateBuilder is created with a default constructor, and then
+ * uses additional public methods to set the public key, desired validity
+ * dates, serial number and extensions.  It is expected that the caller will
+ * have generated the necessary key pairs prior to using a CertificateBuilder
+ * to generate certificates.
+ *
+ * The following methods are mandatory before calling build():
+ * <UL>
+ * <LI>{@link #setSubjectName(java.lang.String)}
+ * <LI>{@link #setPublicKey(java.security.PublicKey)}
+ * <LI>{@link #setNotBefore(java.util.Date)} and
+ * {@link #setNotAfter(java.util.Date)}, or
+ * {@link #setValidity(java.util.Date, java.util.Date)}
+ * <LI>{@link #setSerialNumber(java.math.BigInteger)}
+ * </UL><BR>
+ *
+ * Additionally, the caller can either provide a {@link List} of
+ * {@link Extension} objects, or use the helper classes to add specific
+ * extension types.
+ *
+ * When all required and desired parameters are set, the
+ * {@link #build(java.security.cert.X509Certificate, java.security.PrivateKey,
+ * java.lang.String)} method can be used to create the {@link X509Certificate}
+ * object.
+ *
+ * Multiple certificates may be cut from the same settings using subsequent
+ * calls to the build method.  Settings may be cleared using the
+ * {@link #reset()} method.
+ */
+public class CertificateBuilder {
+    private final CertificateFactory factory;
+
+    private X500Principal subjectName = null;
+    private BigInteger serialNumber = null;
+    private PublicKey publicKey = null;
+    private Date notBefore = null;
+    private Date notAfter = null;
+    private final Map<String, Extension> extensions = new HashMap<>();
+    private byte[] tbsCertBytes;
+    private byte[] signatureBytes;
+
+    /**
+     * Default constructor for a {@code CertificateBuilder} object.
+     *
+     * @throws CertificateException if the underlying {@link CertificateFactory}
+     * cannot be instantiated.
+     */
+    public CertificateBuilder() throws CertificateException {
+        factory = CertificateFactory.getInstance("X.509");
+    }
+
+    /**
+     * Set the subject name for the certificate.
+     *
+     * @param name An {@link X500Principal} to be used as the subject name
+     * on this certificate.
+     */
+    public void setSubjectName(X500Principal name) {
+        subjectName = name;
+    }
+
+    /**
+     * Set the subject name for the certificate.
+     *
+     * @param name The subject name in RFC 2253 format
+     */
+    public void setSubjectName(String name) {
+        subjectName = new X500Principal(name);
+    }
+
+    /**
+     * Set the public key for this certificate.
+     *
+     * @param pubKey The {@link PublicKey} to be used on this certificate.
+     */
+    public void setPublicKey(PublicKey pubKey) {
+        publicKey = Objects.requireNonNull(pubKey, "Caught null public key");
+    }
+
+    /**
+     * Set the NotBefore date on the certificate.
+     *
+     * @param nbDate A {@link Date} object specifying the start of the
+     * certificate validity period.
+     */
+    public void setNotBefore(Date nbDate) {
+        Objects.requireNonNull(nbDate, "Caught null notBefore date");
+        notBefore = (Date)nbDate.clone();
+    }
+
+    /**
+     * Set the NotAfter date on the certificate.
+     *
+     * @param naDate A {@link Date} object specifying the end of the
+     * certificate validity period.
+     */
+    public void setNotAfter(Date naDate) {
+        Objects.requireNonNull(naDate, "Caught null notAfter date");
+        notAfter = (Date)naDate.clone();
+    }
+
+    /**
+     * Set the validity period for the certificate
+     *
+     * @param nbDate A {@link Date} object specifying the start of the
+     * certificate validity period.
+     * @param naDate A {@link Date} object specifying the end of the
+     * certificate validity period.
+     */
+    public void setValidity(Date nbDate, Date naDate) {
+        setNotBefore(nbDate);
+        setNotAfter(naDate);
+    }
+
+    /**
+     * Set the serial number on the certificate.
+     *
+     * @param serial A serial number in {@link BigInteger} form.
+     */
+    public void setSerialNumber(BigInteger serial) {
+        Objects.requireNonNull(serial, "Caught null serial number");
+        serialNumber = serial;
+    }
+
+
+    /**
+     * Add a single extension to the certificate.
+     *
+     * @param ext The extension to be added.
+     */
+    public void addExtension(Extension ext) {
+        Objects.requireNonNull(ext, "Caught null extension");
+        extensions.put(ext.getId(), ext);
+    }
+
+    /**
+     * Add multiple extensions contained in a {@code List}.
+     *
+     * @param extList The {@link List} of extensions to be added to
+     * the certificate.
+     */
+    public void addExtensions(List<Extension> extList) {
+        Objects.requireNonNull(extList, "Caught null extension list");
+        for (Extension ext : extList) {
+            extensions.put(ext.getId(), ext);
+        }
+    }
+
+    /**
+     * Helper method to add DNSName types for the SAN extension
+     *
+     * @param dnsNames A {@code List} of names to add as DNSName types
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addSubjectAltNameDNSExt(List<String> dnsNames) throws IOException {
+        if (!dnsNames.isEmpty()) {
+            GeneralNames gNames = new GeneralNames();
+            for (String name : dnsNames) {
+                gNames.add(new GeneralName(new DNSName(name)));
+            }
+            addExtension(new SubjectAlternativeNameExtension(false,
+                    gNames));
+        }
+    }
+
+    /**
+     * Helper method to add one or more OCSP URIs to the Authority Info Access
+     * certificate extension.
+     *
+     * @param locations A list of one or more OCSP responder URIs as strings
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addAIAExt(List<String> locations)
+            throws IOException {
+        if (!locations.isEmpty()) {
+            List<AccessDescription> acDescList = new ArrayList<>();
+            for (String ocspUri : locations) {
+                acDescList.add(new AccessDescription(
+                        AccessDescription.Ad_OCSP_Id,
+                        new GeneralName(new URIName(ocspUri))));
+            }
+            addExtension(new AuthorityInfoAccessExtension(acDescList));
+        }
+    }
+
+    /**
+     * Set a Key Usage extension for the certificate.  The extension will
+     * be marked critical.
+     *
+     * @param bitSettings Boolean array for all nine bit settings in the order
+     * documented in RFC 5280 section 4.2.1.3.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addKeyUsageExt(boolean[] bitSettings) throws IOException {
+        addExtension(new KeyUsageExtension(bitSettings));
+    }
+
+    /**
+     * Set the Basic Constraints Extension for a certificate.
+     *
+     * @param crit {@code true} if critical, {@code false} otherwise
+     * @param isCA {@code true} if the extension will be on a CA certificate,
+     * {@code false} otherwise
+     * @param maxPathLen The maximum path length issued by this CA.  Values
+     * less than zero will omit this field from the resulting extension and
+     * no path length constraint will be asserted.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addBasicConstraintsExt(boolean crit, boolean isCA,
+            int maxPathLen) throws IOException {
+        addExtension(new BasicConstraintsExtension(crit, isCA, maxPathLen));
+    }
+
+    /**
+     * Add the Authority Key Identifier extension.
+     *
+     * @param authorityCert The certificate of the issuing authority.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addAuthorityKeyIdExt(X509Certificate authorityCert)
+            throws IOException {
+        addAuthorityKeyIdExt(authorityCert.getPublicKey());
+    }
+
+    /**
+     * Add the Authority Key Identifier extension.
+     *
+     * @param authorityKey The public key of the issuing authority.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addAuthorityKeyIdExt(PublicKey authorityKey) throws IOException {
+        KeyIdentifier kid = new KeyIdentifier(authorityKey);
+        addExtension(new AuthorityKeyIdentifierExtension(kid, null, null));
+    }
+
+    /**
+     * Add the Subject Key Identifier extension.
+     *
+     * @param subjectKey The public key to be used in the resulting certificate
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addSubjectKeyIdExt(PublicKey subjectKey) throws IOException {
+        byte[] keyIdBytes = new KeyIdentifier(subjectKey).getIdentifier();
+        addExtension(new SubjectKeyIdentifierExtension(keyIdBytes));
+    }
+
+    /**
+     * Add the Extended Key Usage extension.
+     *
+     * @param ekuOids A {@link List} of object identifiers in string form.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    public void addExtendedKeyUsageExt(List<String> ekuOids)
+            throws IOException {
+        if (!ekuOids.isEmpty()) {
+            Vector<ObjectIdentifier> oidVector = new Vector<>();
+            for (String oid : ekuOids) {
+                oidVector.add(new ObjectIdentifier(oid));
+            }
+            addExtension(new ExtendedKeyUsageExtension(oidVector));
+        }
+    }
+
+    /**
+     * Clear all settings and return the {@code CertificateBuilder} to
+     * its default state.
+     */
+    public void reset() {
+        extensions.clear();
+        subjectName = null;
+        notBefore = null;
+        notAfter = null;
+        serialNumber = null;
+        publicKey = null;
+        signatureBytes = null;
+        tbsCertBytes = null;
+    }
+
+    /**
+     * Build the certificate.
+     *
+     * @param issuerCert The certificate of the issuing authority, or
+     * {@code null} if the resulting certificate is self-signed.
+     * @param issuerKey The private key of the issuing authority
+     * @param algName The signature algorithm name
+     *
+     * @return The resulting {@link X509Certificate}
+     *
+     * @throws IOException if an encoding error occurs.
+     * @throws CertificateException If the certificate cannot be generated
+     * by the underlying {@link CertificateFactory}
+     * @throws NoSuchAlgorithmException If an invalid signature algorithm
+     * is provided.
+     */
+    public X509Certificate build(X509Certificate issuerCert,
+            PrivateKey issuerKey, String algName)
+            throws IOException, CertificateException, NoSuchAlgorithmException {
+        // TODO: add some basic checks (key usage, basic constraints maybe)
+
+        AlgorithmId signAlg = AlgorithmId.get(algName);
+        byte[] encodedCert = encodeTopLevel(issuerCert, issuerKey, signAlg);
+        ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert);
+        return (X509Certificate)factory.generateCertificate(bais);
+    }
+
+    /**
+     * Encode the contents of the outer-most ASN.1 SEQUENCE:
+     *
+     * <PRE>
+     *  Certificate  ::=  SEQUENCE  {
+     *      tbsCertificate       TBSCertificate,
+     *      signatureAlgorithm   AlgorithmIdentifier,
+     *      signatureValue       BIT STRING  }
+     * </PRE>
+     *
+     * @param issuerCert The certificate of the issuing authority, or
+     * {@code null} if the resulting certificate is self-signed.
+     * @param issuerKey The private key of the issuing authority
+     * @param signAlg The signature algorithm object
+     *
+     * @return The DER-encoded X.509 certificate
+     *
+     * @throws CertificateException If an error occurs during the
+     * signing process.
+     * @throws IOException if an encoding error occurs.
+     */
+    private byte[] encodeTopLevel(X509Certificate issuerCert,
+            PrivateKey issuerKey, AlgorithmId signAlg)
+            throws CertificateException, IOException {
+        DerOutputStream outerSeq = new DerOutputStream();
+        DerOutputStream topLevelItems = new DerOutputStream();
+
+        tbsCertBytes = encodeTbsCert(issuerCert, signAlg);
+        topLevelItems.write(tbsCertBytes);
+        try {
+            signatureBytes = signCert(issuerKey, signAlg);
+        } catch (GeneralSecurityException ge) {
+            throw new CertificateException(ge);
+        }
+        signAlg.derEncode(topLevelItems);
+        topLevelItems.putBitString(signatureBytes);
+        outerSeq.write(DerValue.tag_Sequence, topLevelItems);
+
+        return outerSeq.toByteArray();
+    }
+
+    /**
+     * Encode the bytes for the TBSCertificate structure:
+     * <PRE>
+     *  TBSCertificate  ::=  SEQUENCE  {
+     *      version         [0]  EXPLICIT Version DEFAULT v1,
+     *      serialNumber         CertificateSerialNumber,
+     *      signature            AlgorithmIdentifier,
+     *      issuer               Name,
+     *      validity             Validity,
+     *      subject              Name,
+     *      subjectPublicKeyInfo SubjectPublicKeyInfo,
+     *      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+     *                        -- If present, version MUST be v2 or v3
+     *      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+     *                        -- If present, version MUST be v2 or v3
+     *      extensions      [3]  EXPLICIT Extensions OPTIONAL
+     *                        -- If present, version MUST be v3
+     *      }
+     *
+     * @param issuerCert The certificate of the issuing authority, or
+     * {@code null} if the resulting certificate is self-signed.
+     * @param signAlg The signature algorithm object
+     *
+     * @return The DER-encoded bytes for the TBSCertificate structure
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    private byte[] encodeTbsCert(X509Certificate issuerCert,
+            AlgorithmId signAlg) throws IOException {
+        DerOutputStream tbsCertSeq = new DerOutputStream();
+        DerOutputStream tbsCertItems = new DerOutputStream();
+
+        // Hardcode to V3
+        byte[] v3int = {0x02, 0x01, 0x02};
+        tbsCertItems.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
+                (byte)0), v3int);
+
+        // Serial Number
+        SerialNumber sn = new SerialNumber(serialNumber);
+        sn.encode(tbsCertItems);
+
+        // Algorithm ID
+        signAlg.derEncode(tbsCertItems);
+
+        // Issuer Name
+        if (issuerCert != null) {
+            tbsCertItems.write(
+                    issuerCert.getSubjectX500Principal().getEncoded());
+        } else {
+            // Self-signed
+            tbsCertItems.write(subjectName.getEncoded());
+        }
+
+        // Validity period (set as UTCTime)
+        DerOutputStream valSeq = new DerOutputStream();
+        valSeq.putUTCTime(notBefore);
+        valSeq.putUTCTime(notAfter);
+        tbsCertItems.write(DerValue.tag_Sequence, valSeq);
+
+        // Subject Name
+        tbsCertItems.write(subjectName.getEncoded());
+
+        // SubjectPublicKeyInfo
+        tbsCertItems.write(publicKey.getEncoded());
+
+        // TODO: Extensions!
+        encodeExtensions(tbsCertItems);
+
+        // Wrap it all up in a SEQUENCE and return the bytes
+        tbsCertSeq.write(DerValue.tag_Sequence, tbsCertItems);
+        return tbsCertSeq.toByteArray();
+    }
+
+    /**
+     * Encode the extensions segment for an X.509 Certificate:
+     *
+     * <PRE>
+     *  Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
+     *
+     *  Extension  ::=  SEQUENCE  {
+     *      extnID      OBJECT IDENTIFIER,
+     *      critical    BOOLEAN DEFAULT FALSE,
+     *      extnValue   OCTET STRING
+     *                  -- contains the DER encoding of an ASN.1 value
+     *                  -- corresponding to the extension type identified
+     *                  -- by extnID
+     *      }
+     * </PRE>
+     *
+     * @param tbsStream The {@code DerOutputStream} that holds the
+     * TBSCertificate contents.
+     *
+     * @throws IOException if an encoding error occurs.
+     */
+    private void encodeExtensions(DerOutputStream tbsStream)
+            throws IOException {
+        DerOutputStream extSequence = new DerOutputStream();
+        DerOutputStream extItems = new DerOutputStream();
+
+        for (Extension ext : extensions.values()) {
+            ext.encode(extItems);
+        }
+        extSequence.write(DerValue.tag_Sequence, extItems);
+        tbsStream.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
+                (byte)3), extSequence);
+    }
+
+    /**
+     * Digitally sign the X.509 certificate.
+     *
+     * @param issuerKey The private key of the issuing authority
+     * @param signAlg The signature algorithm object
+     *
+     * @return The digital signature bytes.
+     *
+     * @throws GeneralSecurityException If any errors occur during the
+     * digital signature process.
+     */
+    private byte[] signCert(PrivateKey issuerKey, AlgorithmId signAlg)
+            throws GeneralSecurityException {
+        Signature sig = Signature.getInstance(signAlg.getName());
+        sig.initSign(issuerKey);
+        sig.update(tbsCertBytes);
+        return sig.sign();
+    }
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/testlibrary/SimpleOCSPServer.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,1540 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.testlibrary;
+
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import java.security.cert.CRLReason;
+import java.security.cert.X509Certificate;
+import java.security.cert.Extension;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateEncodingException;
+import java.security.Signature;
+import java.util.*;
+import java.util.concurrent.*;
+import java.text.SimpleDateFormat;
+import java.math.BigInteger;
+
+import sun.security.x509.*;
+import sun.security.x509.PKIXExtensions;
+import sun.security.provider.certpath.ResponderId;
+import sun.security.provider.certpath.CertId;
+import sun.security.provider.certpath.OCSPResponse;
+import sun.security.provider.certpath.OCSPResponse.ResponseStatus;
+import sun.security.util.Debug;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+import sun.security.util.ObjectIdentifier;
+
+
+/**
+ * This is a simple OCSP server designed to listen and respond to incoming
+ * requests.
+ */
+public class SimpleOCSPServer {
+    private final Debug debug = Debug.getInstance("oserv");
+    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 SimpleDateFormat utcDateFmt =
+            new SimpleDateFormat("MMM dd yyyy, HH:mm:ss z");
+
+    // CertStatus values
+    public static enum CertStatus {
+        CERT_STATUS_GOOD,
+        CERT_STATUS_REVOKED,
+        CERT_STATUS_UNKNOWN,
+    }
+
+    // Fields used for the networking portion of the responder
+    private ServerSocket servSocket;
+    private InetAddress listenAddress;
+    private int listenPort;
+
+    // Keystore information (certs, keys, etc.)
+    private KeyStore keystore;
+    private X509Certificate issuerCert;
+    private X509Certificate signerCert;
+    private PrivateKey signerKey;
+
+    // Fields used for the operational portions of the server
+    private boolean logEnabled = false;
+    private ExecutorService threadPool;
+    private volatile boolean started = false;
+    private volatile boolean receivedShutdown = false;
+    private long delayMsec = 0;
+
+    // Fields used in the generation of responses
+    private long nextUpdateInterval = -1;
+    private Date nextUpdate = null;
+    private ResponderId respId;
+    private AlgorithmId sigAlgId;
+    private Map<CertId, CertStatusInfo> statusDb =
+            Collections.synchronizedMap(new HashMap<>());
+
+    /**
+     * Construct a SimpleOCSPServer using keystore, password, and alias
+     * parameters.
+     *
+     * @param ks the keystore to be used
+     * @param password the password to access key material in the keystore
+     * @param issuerAlias the alias of the issuer certificate
+     * @param signerAlias the alias of the signer certificate and key.  A
+     * value of {@code null} means that the {@code issuerAlias} will be used
+     * to look up the signer key.
+     *
+     * @throws GeneralSecurityException if there are problems accessing the
+     * keystore or finding objects within the keystore.
+     * @throws IOException if a {@code ResponderId} cannot be generated from
+     * the signer certificate.
+     */
+    public SimpleOCSPServer(KeyStore ks, String password, String issuerAlias,
+            String signerAlias) throws GeneralSecurityException, IOException {
+        this(null, 0, ks, password, issuerAlias, signerAlias);
+    }
+
+    /**
+     * Construct a SimpleOCSPServer using specific network parameters,
+     * keystore, password, and alias.
+     *
+     * @param addr the address to bind the server to.  A value of {@code null}
+     * means the server will bind to all interfaces.
+     * @param port the port to listen on.  A value of {@code 0} will mean that
+     * the server will randomly pick an open ephemeral port to bind to.
+     * @param ks the keystore to be used
+     * @param password the password to access key material in the keystore
+     * @param issuerAlias the alias of the issuer certificate
+     * @param signerAlias the alias of the signer certificate and key.  A
+     * value of {@code null} means that the {@code issuerAlias} will be used
+     * to look up the signer key.
+     *
+     * @throws GeneralSecurityException if there are problems accessing the
+     * keystore or finding objects within the keystore.
+     * @throws IOException if a {@code ResponderId} cannot be generated from
+     * the signer certificate.
+     */
+    public SimpleOCSPServer(InetAddress addr, int port, KeyStore ks,
+            String password, String issuerAlias, String signerAlias)
+            throws GeneralSecurityException, IOException {
+        Objects.requireNonNull(ks, "Null keystore provided");
+        Objects.requireNonNull(issuerAlias, "Null issuerName provided");
+
+        utcDateFmt.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+        keystore = ks;
+        issuerCert = (X509Certificate)ks.getCertificate(issuerAlias);
+        if (issuerCert == null) {
+            throw new IllegalArgumentException("Certificate for alias " +
+                    issuerAlias + " not found");
+        }
+
+        if (signerAlias != null) {
+            signerCert = (X509Certificate)ks.getCertificate(signerAlias);
+            if (signerCert == null) {
+                throw new IllegalArgumentException("Certificate for alias " +
+                    signerAlias + " not found");
+            }
+            signerKey = (PrivateKey)ks.getKey(signerAlias,
+                    password.toCharArray());
+            if (signerKey == null) {
+                throw new IllegalArgumentException("PrivateKey for alias " +
+                    signerAlias + " not found");
+            }
+        } else {
+            signerCert = issuerCert;
+            signerKey = (PrivateKey)ks.getKey(issuerAlias,
+                    password.toCharArray());
+            if (signerKey == null) {
+                throw new IllegalArgumentException("PrivateKey for alias " +
+                    issuerAlias + " not found");
+            }
+        }
+
+        sigAlgId = AlgorithmId.get("Sha256withRSA");
+        respId = new ResponderId(signerCert.getSubjectX500Principal());
+        listenAddress = addr;
+        listenPort = port;
+    }
+
+    /**
+     * Start the server.  The server will bind to the specified network
+     * address and begin listening for incoming connections.
+     *
+     * @throws IOException if any number of things go wonky.
+     */
+    public synchronized void start() throws IOException {
+        // You cannot start the server twice.
+        if (started) {
+            log("Server has already been started");
+            return;
+        } else {
+            started = true;
+        }
+
+        // Create and start the thread pool
+        threadPool = Executors.newFixedThreadPool(32, new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = Executors.defaultThreadFactory().newThread(r);
+                t.setDaemon(true);
+                return t;
+            }
+        });
+
+        threadPool.submit(new Runnable() {
+            @Override
+            public void run() {
+                try (ServerSocket sSock = new ServerSocket()) {
+                    servSocket = sSock;
+                    servSocket.setReuseAddress(true);
+                    servSocket.setSoTimeout(500);
+                    servSocket.bind(new InetSocketAddress(listenAddress,
+                            listenPort), 128);
+                    log("Listening on " + servSocket.getLocalSocketAddress());
+
+                    // Update the listenPort with the new port number.  If
+                    // the server is restarted, it will bind to the same
+                    // port rather than picking a new one.
+                    listenPort = servSocket.getLocalPort();
+
+                    // Main dispatch loop
+                    while (!receivedShutdown) {
+                        try {
+                            Socket newConnection = servSocket.accept();
+                            threadPool.submit(new OcspHandler(newConnection));
+                        } catch (SocketTimeoutException timeout) {
+                            // Nothing to do here.  If receivedShutdown
+                            // has changed to true then the loop will
+                            // exit on its own.
+                        } catch (IOException ioe) {
+                            // Something bad happened, log and force a shutdown
+                            log("Unexpected Exception: " + ioe);
+                            stop();
+                        }
+                    }
+
+                    log("Shutting down...");
+                    threadPool.shutdown();
+                } catch (IOException ioe) {
+                    err(ioe);
+                }
+
+                // Reset state variables so the server can be restarted
+                receivedShutdown = false;
+                started = false;
+            }
+        });
+    }
+
+    /**
+     * Stop the OCSP server.
+     */
+    public synchronized void stop() {
+        if (started) {
+            receivedShutdown = true;
+            log("Received shutdown notification");
+        }
+    }
+
+    /**
+     * Print {@code SimpleOCSPServer} operating parameters.
+     *
+     * @return the {@code SimpleOCSPServer} operating parameters in
+     * {@code String} form.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("OCSP Server:\n");
+        sb.append("----------------------------------------------\n");
+        sb.append("issuer: ").append(issuerCert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("signer: ").append(signerCert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("ResponderId: ").append(respId).append("\n");
+        sb.append("----------------------------------------------");
+
+        return sb.toString();
+    }
+
+    /**
+     * Helpful debug routine to hex dump byte arrays.
+     *
+     * @param data the array of bytes to dump to stdout.
+     *
+     * @return the hexdump of the byte array
+     */
+    private static String dumpHexBytes(byte[] data) {
+        return dumpHexBytes(data, 16, "\n", " ");
+    }
+
+    /**
+     *
+     * @param data the array of bytes to dump to stdout.
+     * @param itemsPerLine the number of bytes to display per line
+     * if the {@code lineDelim} character is blank then all bytes will be
+     * printed on a single line.
+     * @param lineDelim the delimiter between lines
+     * @param itemDelim the delimiter between bytes
+     *
+     * @return The hexdump of the byte array
+     */
+    private static String dumpHexBytes(byte[] data, int itemsPerLine,
+            String lineDelim, String itemDelim) {
+        StringBuilder sb = new StringBuilder();
+        if (data != null) {
+            for (int i = 0; i < data.length; i++) {
+                if (i % itemsPerLine == 0 && i != 0) {
+                    sb.append(lineDelim);
+                }
+                sb.append(String.format("%02X", data[i])).append(itemDelim);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Enable or disable the logging feature.
+     *
+     * @param enable {@code true} to enable logging, {@code false} to
+     * disable it.  The setting must be activated before the server calls
+     * its start method.  Any calls after that have no effect.
+     */
+    public void enableLog(boolean enable) {
+        if (!started) {
+            logEnabled = enable;
+        }
+    }
+
+    /**
+     * Sets the nextUpdate interval.  Intervals will be calculated relative
+     * to the server startup time.  When first set, the nextUpdate date is
+     * calculated based on the current time plus the interval.  After that,
+     * calls to getNextUpdate() will return this date if it is still
+     * later than current time.  If not, the Date will be updated to the
+     * next interval that is later than current time.  This value must be set
+     * before the server has had its start method called.  Calls made after
+     * the server has been started have no effect.
+     *
+     * @param interval the recurring time interval in seconds used to
+     * calculate nextUpdate times.   A value less than or equal to 0 will
+     * disable the nextUpdate feature.
+     */
+    public synchronized void setNextUpdateInterval(long interval) {
+        if (!started) {
+            if (interval <= 0) {
+                nextUpdateInterval = -1;
+                nextUpdate = null;
+                log("nexUpdate support has been disabled");
+            } else {
+                nextUpdateInterval = interval * 1000;
+                nextUpdate = new Date(System.currentTimeMillis() +
+                        nextUpdateInterval);
+                log("nextUpdate set to " + nextUpdate);
+            }
+        }
+    }
+
+    /**
+     * Return the nextUpdate {@code Date} object for this server.  If the
+     * nextUpdate date has already passed, set a new nextUpdate based on
+     * the nextUpdate interval and return that date.
+     *
+     * @return a {@code Date} object set to the nextUpdate field for OCSP
+     * responses.
+     */
+    private synchronized Date getNextUpdate() {
+        if (nextUpdate != null && nextUpdate.before(new Date())) {
+            long nuEpochTime = nextUpdate.getTime();
+            long currentTime = System.currentTimeMillis();
+
+            // Keep adding nextUpdate intervals until you reach a date
+            // that is later than current time.
+            while (currentTime >= nuEpochTime) {
+                nuEpochTime += nextUpdateInterval;
+            }
+
+            // Set the nextUpdate for future threads
+            nextUpdate = new Date(nuEpochTime);
+            log("nextUpdate updated to new value: " + nextUpdate);
+        }
+        return nextUpdate;
+    }
+
+    /**
+     * Add entries into the responder's status database.
+     *
+     * @param newEntries a map of {@code CertStatusInfo} objects, keyed on
+     * their serial number (as a {@code BigInteger}).  All serial numbers
+     * are assumed to have come from this responder's issuer certificate.
+     *
+     * @throws IOException if a CertId cannot be generated.
+     */
+    public void updateStatusDb(Map<BigInteger, CertStatusInfo> newEntries)
+            throws IOException {
+         if (newEntries != null) {
+            for (BigInteger serial : newEntries.keySet()) {
+                CertStatusInfo info = newEntries.get(serial);
+                if (info != null) {
+                    CertId cid = new CertId(issuerCert,
+                            new SerialNumber(serial));
+                    statusDb.put(cid, info);
+                    log("Added entry for serial " + serial + "(" +
+                            info.getType() + ")");
+                }
+            }
+        }
+    }
+
+    /**
+     * Check the status database for revocation information one one or more
+     * certificates.
+     *
+     * @param reqList the list of {@code LocalSingleRequest} objects taken
+     * from the incoming OCSP request.
+     *
+     * @return a {@code Map} of {@code CertStatusInfo} objects keyed by their
+     * {@code CertId} values, for each single request passed in.  Those
+     * CertIds not found in the statusDb will have returned List members with
+     * a status of UNKNOWN.
+     */
+    private Map<CertId, CertStatusInfo> checkStatusDb(
+            List<LocalOcspRequest.LocalSingleRequest> reqList) {
+        // TODO figure out what, if anything to do with request extensions
+        Map<CertId, CertStatusInfo> returnMap = new HashMap<>();
+
+        for (LocalOcspRequest.LocalSingleRequest req : reqList) {
+            CertId cid = req.getCertId();
+            CertStatusInfo info = statusDb.get(cid);
+            if (info != null) {
+                log("Status for SN " + cid.getSerialNumber() + ": " +
+                        info.getType());
+                returnMap.put(cid, info);
+            } else {
+                log("Status for SN " + cid.getSerialNumber() +
+                        " not found, using CERT_STATUS_UNKNOWN");
+                returnMap.put(cid,
+                        new CertStatusInfo(CertStatus.CERT_STATUS_UNKNOWN));
+            }
+        }
+
+        return Collections.unmodifiableMap(returnMap);
+    }
+
+    /**
+     * Set the digital signature algorithm used to sign OCSP responses.
+     *
+     * @param algName The algorithm name
+     *
+     * @throws NoSuchAlgorithmException if the algorithm name is invalid.
+     */
+    public void setSignatureAlgorithm(String algName)
+            throws NoSuchAlgorithmException {
+        if (!started) {
+            sigAlgId = AlgorithmId.get(algName);
+        }
+    }
+
+    /**
+     * Get the port the OCSP server is running on.
+     *
+     * @return the port that the OCSP server is running on, or -1 if the
+     * server has not yet been bound to a port.
+     */
+    public int getPort() {
+        if (servSocket != null && started) {
+            InetSocketAddress inetSock =
+                    (InetSocketAddress)servSocket.getLocalSocketAddress();
+            return inetSock.getPort();
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Set a delay between the reception of the request and production of
+     * the response.
+     *
+     * @param delayMillis the number of milliseconds to wait before acting
+     * on the incoming request.
+     */
+    public void setDelay(long delayMillis) {
+        if (!started) {
+            delayMsec = delayMillis > 0 ? delayMillis : 0;
+            if (delayMsec > 0) {
+                log("OCSP latency set to " + delayMsec + " milliseconds.");
+            } else {
+                log("OCSP latency disabled");
+            }
+        }
+    }
+
+    /**
+     * Log a message to stdout.
+     *
+     * @param message the message to log
+     */
+    private synchronized void log(String message) {
+        if (logEnabled || debug != null) {
+            System.out.println("[" + Thread.currentThread().getName() + "]: " +
+                    message);
+        }
+    }
+
+    /**
+     * Log an error message on the stderr stream.
+     *
+     * @param message the message to log
+     */
+    private static synchronized void err(String message) {
+        System.out.println("[" + Thread.currentThread().getName() + "]: " +
+                message);
+    }
+
+    /**
+     * Log exception information on the stderr stream.
+     *
+     * @param exc the exception to dump information about
+     */
+    private static synchronized void err(Throwable exc) {
+        System.out.print("[" + Thread.currentThread().getName() +
+                "]: Exception: ");
+        exc.printStackTrace(System.out);
+    }
+
+    /**
+     * The {@code CertStatusInfo} class defines an object used to return
+     * information from the internal status database.  The data in this
+     * object may be used to construct OCSP responses.
+     */
+    public static class CertStatusInfo {
+        private CertStatus certStatusType;
+        private CRLReason reason;
+        private Date revocationTime;
+
+        /**
+         * Create a Certificate status object by providing the status only.
+         * If the status is {@code REVOKED} then current time is assumed
+         * for the revocation time.
+         *
+         * @param statType the status for this entry.
+         */
+        public CertStatusInfo(CertStatus statType) {
+            this(statType, null, null);
+        }
+
+        /**
+         * Create a CertStatusInfo providing both type and revocation date
+         * (if applicable).
+         *
+         * @param statType the status for this entry.
+         * @param revDate if applicable, the date that revocation took place.
+         * A value of {@code null} indicates that current time should be used.
+         * If the value of {@code statType} is not {@code CERT_STATUS_REVOKED},
+         * then the {@code revDate} parameter is ignored.
+         */
+        public CertStatusInfo(CertStatus statType, Date revDate) {
+            this(statType, revDate, null);
+        }
+
+        /**
+         * Create a CertStatusInfo providing type, revocation date
+         * (if applicable) and revocation reason.
+         *
+         * @param statType the status for this entry.
+         * @param revDate if applicable, the date that revocation took place.
+         * A value of {@code null} indicates that current time should be used.
+         * If the value of {@code statType} is not {@code CERT_STATUS_REVOKED},
+         * then the {@code revDate} parameter is ignored.
+         * @param revReason the reason the certificate was revoked.  A value of
+         * {@code null} means that no reason was provided.
+         */
+        public CertStatusInfo(CertStatus statType, Date revDate,
+                CRLReason revReason) {
+            Objects.requireNonNull(statType, "Cert Status must be non-null");
+            certStatusType = statType;
+            switch (statType) {
+                case CERT_STATUS_GOOD:
+                case CERT_STATUS_UNKNOWN:
+                    revocationTime = null;
+                    break;
+                case CERT_STATUS_REVOKED:
+                    revocationTime = revDate != null ? (Date)revDate.clone() :
+                            new Date();
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown status type: " +
+                            statType);
+            }
+        }
+
+        /**
+         * Get the cert status type
+         *
+         * @return the status applied to this object (e.g.
+         * {@code CERT_STATUS_GOOD}, {@code CERT_STATUS_UNKNOWN}, etc.)
+         */
+        public CertStatus getType() {
+            return certStatusType;
+        }
+
+        /**
+         * Get the revocation time (if applicable).
+         *
+         * @return the revocation time as a {@code Date} object, or
+         * {@code null} if not applicable (i.e. if the certificate hasn't been
+         * revoked).
+         */
+        public Date getRevocationTime() {
+            return (revocationTime != null ? (Date)revocationTime.clone() :
+                    null);
+        }
+
+        /**
+         * Get the revocation reason.
+         *
+         * @return the revocation reason, or {@code null} if one was not
+         * provided.
+         */
+        public CRLReason getRevocationReason() {
+            return reason;
+        }
+    }
+
+    /**
+     * Runnable task that handles incoming OCSP Requests and returns
+     * responses.
+     */
+    private class OcspHandler implements Runnable {
+        private final Socket sock;
+        InetSocketAddress peerSockAddr;
+
+        /**
+         * Construct an {@code OcspHandler}.
+         *
+         * @param incomingSocket the socket the server created on accept()
+         */
+        private OcspHandler(Socket incomingSocket) {
+            sock = incomingSocket;
+        }
+
+        /**
+         * Run the OCSP Request parser and construct a response to be sent
+         * back to the client.
+         */
+        @Override
+        public void run() {
+            // If we have implemented a delay to simulate network latency
+            // wait out the delay here before any other processing.
+            try {
+                if (delayMsec > 0) {
+                    Thread.sleep(delayMsec);
+                }
+            } catch (InterruptedException ie) {
+                // Just log the interrupted sleep
+                log("Delay of " + delayMsec + " milliseconds was interrupted");
+            }
+
+            try (Socket ocspSocket = sock;
+                    InputStream in = ocspSocket.getInputStream();
+                    OutputStream out = ocspSocket.getOutputStream()) {
+                peerSockAddr =
+                        (InetSocketAddress)ocspSocket.getRemoteSocketAddress();
+                log("Received incoming connection from " + peerSockAddr);
+                String[] headerTokens = readLine(in).split(" ");
+                LocalOcspRequest ocspReq = null;
+                LocalOcspResponse ocspResp = null;
+                ResponseStatus respStat = ResponseStatus.INTERNAL_ERROR;
+                try {
+                    if (headerTokens[0] != null) {
+                        switch (headerTokens[0]) {
+                            case "POST":
+                                    ocspReq = parseHttpOcspPost(in);
+                                break;
+                            case "GET":
+                                // req = parseHttpOcspGet(in);
+                                // TODO implement the GET parsing
+                                throw new IOException("GET method unsupported");
+                            default:
+                                respStat = ResponseStatus.MALFORMED_REQUEST;
+                                throw new IOException("Not a GET or POST");
+                        }
+                    } else {
+                        respStat = ResponseStatus.MALFORMED_REQUEST;
+                        throw new IOException("Unable to get HTTP method");
+                    }
+
+                    if (ocspReq != null) {
+                        log(ocspReq.toString());
+                        // Get responses for all CertIds in the request
+                        Map<CertId, CertStatusInfo> statusMap =
+                                checkStatusDb(ocspReq.getRequests());
+                        if (statusMap.isEmpty()) {
+                            respStat = ResponseStatus.UNAUTHORIZED;
+                        } else {
+                            ocspResp = new LocalOcspResponse(
+                                    ResponseStatus.SUCCESSFUL, statusMap,
+                                    ocspReq.getExtensions());
+                        }
+                    } else {
+                        respStat = ResponseStatus.MALFORMED_REQUEST;
+                        throw new IOException("Found null request");
+                    }
+                } catch (IOException | RuntimeException exc) {
+                    err(exc);
+                }
+                if (ocspResp == null) {
+                    ocspResp = new LocalOcspResponse(respStat);
+                }
+                sendResponse(out, ocspResp);
+            } catch (IOException | CertificateException exc) {
+                err(exc);
+            }
+        }
+
+        /**
+         * Send an OCSP response on an {@code OutputStream}.
+         *
+         * @param out the {@code OutputStream} on which to send the response.
+         * @param resp the OCSP response to send.
+         *
+         * @throws IOException if an encoding error occurs.
+         */
+        public void sendResponse(OutputStream out, LocalOcspResponse resp)
+                throws IOException {
+            StringBuilder sb = new StringBuilder();
+
+            byte[] respBytes;
+            try {
+                respBytes = resp.getBytes();
+            } catch (RuntimeException re) {
+                err(re);
+                return;
+            }
+
+            sb.append("HTTP/1.0 200 OK\r\n");
+            sb.append("Content-Type: application/ocsp-response\r\n");
+            sb.append("Content-Length: ").append(respBytes.length);
+            sb.append("\r\n\r\n");
+
+            out.write(sb.toString().getBytes("UTF-8"));
+            out.write(respBytes);
+            log(resp.toString());
+        }
+
+        /**
+         * Parse the incoming HTTP POST of an OCSP Request.
+         *
+         * @param inStream the input stream from the socket bound to this
+         * {@code OcspHandler}.
+         *
+         * @return the OCSP Request as a {@code LocalOcspRequest}
+         *
+         * @throws IOException if there are network related issues or problems
+         * occur during parsing of the OCSP request.
+         * @throws CertificateException if one or more of the certificates in
+         * the OCSP request cannot be read/parsed.
+         */
+        private LocalOcspRequest parseHttpOcspPost(InputStream inStream)
+                throws IOException, CertificateException {
+            boolean endOfHeader = false;
+            boolean properContentType = false;
+            int length = -1;
+
+            while (!endOfHeader) {
+                String[] lineTokens = readLine(inStream).split(" ");
+                if (lineTokens[0].isEmpty()) {
+                    endOfHeader = true;
+                } else if (lineTokens[0].equalsIgnoreCase("Content-Type:")) {
+                    if (lineTokens[1] == null ||
+                            !lineTokens[1].equals(
+                                    "application/ocsp-request")) {
+                        log("Unknown Content-Type: " +
+                                (lineTokens[1] != null ?
+                                        lineTokens[1] : "<NULL>"));
+                        return null;
+                    } else {
+                        properContentType = true;
+                        log("Content-Type = " + lineTokens[1]);
+                    }
+                } else if (lineTokens[0].equalsIgnoreCase("Content-Length:")) {
+                    if (lineTokens[1] != null) {
+                        length = Integer.parseInt(lineTokens[1]);
+                        log("Content-Length = " + length);
+                    }
+                }
+            }
+
+            // Okay, make sure we got what we needed from the header, then
+            // read the remaining OCSP Request bytes
+            if (properContentType && length >= 0) {
+                byte[] ocspBytes = new byte[length];
+                inStream.read(ocspBytes);
+                return new LocalOcspRequest(ocspBytes);
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Read a line of text that is CRLF-delimited.
+         *
+         * @param is the {@code InputStream} tied to the socket
+         * for this {@code OcspHandler}
+         *
+         * @return a {@code String} consisting of the line of text
+         * read from the stream with the CRLF stripped.
+         *
+         * @throws IOException if any I/O error occurs.
+         */
+        private String readLine(InputStream is) throws IOException {
+            PushbackInputStream pbis = new PushbackInputStream(is);
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            boolean done = false;
+            while (!done) {
+                byte b = (byte)pbis.read();
+                if (b == '\r') {
+                    byte bNext = (byte)pbis.read();
+                    if (bNext == '\n' || bNext == -1) {
+                        done = true;
+                    } else {
+                        pbis.unread(bNext);
+                        bos.write(b);
+                    }
+                } else if (b == -1) {
+                    done = true;
+                } else {
+                    bos.write(b);
+                }
+            }
+
+            return new String(bos.toByteArray(), "UTF-8");
+        }
+    }
+
+
+    /**
+     * Simple nested class to handle OCSP requests without making
+     * changes to sun.security.provider.certpath.OCSPRequest
+     */
+    public class LocalOcspRequest {
+
+        private byte[] nonce;
+        private byte[] signature = null;
+        private AlgorithmId algId = null;
+        private int version = 0;
+        private GeneralName requestorName = null;
+        private Map<String, Extension> extensions = Collections.emptyMap();
+        private final List<LocalSingleRequest> requestList = new ArrayList<>();
+        private final List<X509Certificate> certificates = new ArrayList<>();
+
+        /**
+         * Construct a {@code LocalOcspRequest} from its DER encoding.
+         *
+         * @param requestBytes the DER-encoded bytes
+         *
+         * @throws IOException if decoding errors occur
+         * @throws CertificateException if certificates are found in the
+         * OCSP request and they do not parse correctly.
+         */
+        private LocalOcspRequest(byte[] requestBytes) throws IOException,
+                CertificateException {
+            Objects.requireNonNull(requestBytes, "Received null input");
+
+            DerInputStream dis = new DerInputStream(requestBytes);
+
+            // Parse the top-level structure, it should have no more than
+            // two elements.
+            DerValue[] topStructs = dis.getSequence(2);
+            for (DerValue dv : topStructs) {
+                if (dv.tag == DerValue.tag_Sequence) {
+                    parseTbsRequest(dv);
+                } else if (dv.isContextSpecific((byte)0)) {
+                    parseSignature(dv);
+                } else {
+                    throw new IOException("Unknown tag at top level: " +
+                            dv.tag);
+                }
+            }
+        }
+
+        /**
+         * Parse the signature block from an OCSP request
+         *
+         * @param sigSequence a {@code DerValue} containing the signature
+         * block at the outer sequence datum.
+         *
+         * @throws IOException if any non-certificate-based parsing errors occur
+         * @throws CertificateException if certificates are found in the
+         * OCSP request and they do not parse correctly.
+         */
+        private void parseSignature(DerValue sigSequence)
+                throws IOException, CertificateException {
+            DerValue[] sigItems = sigSequence.data.getSequence(3);
+            if (sigItems.length != 3) {
+                throw new IOException("Invalid number of signature items: " +
+                        "expected 3, got " + sigItems.length);
+            }
+
+            algId = AlgorithmId.parse(sigItems[0]);
+            signature = sigItems[1].getBitString();
+
+            if (sigItems[2].isContextSpecific((byte)0)) {
+                DerValue[] certDerItems = sigItems[2].data.getSequence(4);
+                int i = 0;
+                for (DerValue dv : certDerItems) {
+                    X509Certificate xc = new X509CertImpl(dv);
+                    certificates.add(xc);
+                }
+            } else {
+                throw new IOException("Invalid tag in signature block: " +
+                    sigItems[2].tag);
+            }
+        }
+
+        /**
+         * Parse the to-be-signed request data
+         *
+         * @param tbsReqSeq a {@code DerValue} object containing the to-be-
+         * signed OCSP request at the outermost SEQUENCE tag.
+         * @throws IOException if any parsing errors occur
+         */
+        private void parseTbsRequest(DerValue tbsReqSeq) throws IOException {
+            while (tbsReqSeq.data.available() > 0) {
+                DerValue dv = tbsReqSeq.data.getDerValue();
+                if (dv.isContextSpecific((byte)0)) {
+                    // The version was explicitly called out
+                    version = dv.data.getInteger();
+                } else if (dv.isContextSpecific((byte)1)) {
+                    // A GeneralName was provided
+                    requestorName = new GeneralName(dv.data.getDerValue());
+                } else if (dv.isContextSpecific((byte)2)) {
+                    // Parse the extensions
+                    DerValue[] extItems = dv.data.getSequence(2);
+                    extensions = parseExtensions(extItems);
+                } else if (dv.tag == DerValue.tag_Sequence) {
+                    while (dv.data.available() > 0) {
+                        requestList.add(new LocalSingleRequest(dv.data));
+                    }
+                }
+            }
+        }
+
+        /**
+         * Parse a SEQUENCE of extensions.  This routine is used both
+         * at the overall request level and down at the singleRequest layer.
+         *
+         * @param extDerItems an array of {@code DerValue} items, each one
+         * consisting of a DER-encoded extension.
+         *
+         * @return a {@code Map} of zero or more extensions,
+         * keyed by its object identifier in {@code String} form.
+         *
+         * @throws IOException if any parsing errors occur.
+         */
+        private Map<String, Extension> parseExtensions(DerValue[] extDerItems)
+                throws IOException {
+            Map<String, Extension> extMap = new HashMap<>();
+
+            if (extDerItems != null && extDerItems.length != 0) {
+                for (DerValue extDerVal : extDerItems) {
+                    sun.security.x509.Extension ext =
+                            new sun.security.x509.Extension(extDerVal);
+                    extMap.put(ext.getId(), ext);
+                }
+            }
+
+            return extMap;
+        }
+
+        /**
+         * Return the list of single request objects in this OCSP request.
+         *
+         * @return an unmodifiable {@code List} of zero or more requests.
+         */
+        private List<LocalSingleRequest> getRequests() {
+            return Collections.unmodifiableList(requestList);
+        }
+
+        /**
+         * Return the list of X.509 Certificates in this OCSP request.
+         *
+         * @return an unmodifiable {@code List} of zero or more
+         * {@cpde X509Certificate} objects.
+         */
+        private List<X509Certificate> getCertificates() {
+            return Collections.unmodifiableList(certificates);
+        }
+
+        /**
+         * Return the map of OCSP request extensions.
+         *
+         * @return an unmodifiable {@code Map} of zero or more
+         * {@code Extension} objects, keyed by their object identifiers
+         * in {@code String} form.
+         */
+        private Map<String, Extension> getExtensions() {
+            return Collections.unmodifiableMap(extensions);
+        }
+
+        /**
+         * Display the {@code LocalOcspRequest} in human readable form.
+         *
+         * @return a {@code String} representation of the
+         * {@code LocalOcspRequest}
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append(String.format("OCSP Request: Version %d (0x%X)",
+                    version + 1, version)).append("\n");
+            if (requestorName != null) {
+                sb.append("Requestor Name: ").append(requestorName).
+                        append("\n");
+            }
+
+            int requestCtr = 0;
+            for (LocalSingleRequest lsr : requestList) {
+                sb.append("Request [").append(requestCtr++).append("]\n");
+                sb.append(lsr).append("\n");
+            }
+            if (!extensions.isEmpty()) {
+                sb.append("Extensions (").append(extensions.size()).
+                        append(")\n");
+                for (Extension ext : extensions.values()) {
+                    sb.append("\t").append(ext).append("\n");
+                }
+            }
+            if (signature != null) {
+                sb.append("Signature: ").append(algId).append("\n");
+                sb.append(dumpHexBytes(signature)).append("\n");
+                int certCtr = 0;
+                for (X509Certificate cert : certificates) {
+                    sb.append("Certificate [").append(certCtr++).append("]").
+                            append("\n");
+                    sb.append("\tSubject: ");
+                    sb.append(cert.getSubjectX500Principal()).append("\n");
+                    sb.append("\tIssuer: ");
+                    sb.append(cert.getIssuerX500Principal()).append("\n");
+                    sb.append("\tSerial: ").append(cert.getSerialNumber());
+                }
+            }
+
+            return sb.toString();
+        }
+
+        /**
+         * Inner class designed to handle the decoding/representation of
+         * single requests within a {@code LocalOcspRequest} object.
+         */
+        public class LocalSingleRequest {
+            private final CertId cid;
+            private Map<String, Extension> extensions = Collections.emptyMap();
+
+            private LocalSingleRequest(DerInputStream dis)
+                    throws IOException {
+                DerValue[] srItems = dis.getSequence(2);
+
+                // There should be 1, possibly 2 DerValue items
+                if (srItems.length == 1 || srItems.length == 2) {
+                    // The first parsable item should be the mandatory CertId
+                    cid = new CertId(srItems[0].data);
+                    if (srItems.length == 2) {
+                        if (srItems[1].isContextSpecific((byte)0)) {
+                            DerValue[] extDerItems = srItems[1].data.getSequence(2);
+                            extensions = parseExtensions(extDerItems);
+                        } else {
+                            throw new IOException("Illegal tag in Request " +
+                                    "extensions: " + srItems[1].tag);
+                        }
+                    }
+                } else {
+                    throw new IOException("Invalid number of items in " +
+                            "Request (" + srItems.length + ")");
+                }
+            }
+
+            /**
+             * Get the {@code CertId} for this single request.
+             *
+             * @return the {@code CertId} for this single request.
+             */
+            private CertId getCertId() {
+                return cid;
+            }
+
+            /**
+             * Return the map of single request extensions.
+             *
+             * @return an unmodifiable {@code Map} of zero or more
+             * {@code Extension} objects, keyed by their object identifiers
+             * in {@code String} form.
+             */
+            private Map<String, Extension> getExtensions() {
+                return Collections.unmodifiableMap(extensions);
+            }
+
+            /**
+             * Display the {@code LocalSingleRequest} in human readable form.
+             *
+             * @return a {@code String} representation of the
+             * {@code LocalSingleRequest}
+             */
+            @Override
+            public String toString() {
+                StringBuilder sb = new StringBuilder();
+                sb.append("CertId, Algorithm = ");
+                sb.append(cid.getHashAlgorithm()).append("\n");
+                sb.append("\tIssuer Name Hash: ");
+                sb.append(dumpHexBytes(cid.getIssuerNameHash(), 256, "", ""));
+                sb.append("\n");
+                sb.append("\tIssuer Key Hash: ");
+                sb.append(dumpHexBytes(cid.getIssuerKeyHash(), 256, "", ""));
+                sb.append("\n");
+                sb.append("\tSerial Number: ").append(cid.getSerialNumber());
+                if (!extensions.isEmpty()) {
+                    sb.append("Extensions (").append(extensions.size()).
+                            append(")\n");
+                    for (Extension ext : extensions.values()) {
+                        sb.append("\t").append(ext).append("\n");
+                    }
+                }
+
+                return sb.toString();
+            }
+        }
+    }
+
+    /**
+     * Simple nested class to handle OCSP requests without making
+     * changes to sun.security.provider.certpath.OCSPResponse
+     */
+    public class LocalOcspResponse {
+        private final int version = 0;
+        private final OCSPResponse.ResponseStatus responseStatus;
+        private final Map<CertId, CertStatusInfo> respItemMap;
+        private final Date producedAtDate;
+        private final List<LocalSingleResponse> singleResponseList =
+                new ArrayList<>();
+        private final Map<String, Extension> responseExtensions;
+        private byte[] signature;
+        private final List<X509Certificate> certificates;
+        private final byte[] encodedResponse;
+
+        /**
+         * Constructor for the generation of non-successful responses
+         *
+         * @param respStat the OCSP response status.
+         *
+         * @throws IOException if an error happens during encoding
+         * @throws NullPointerException if {@code respStat} is {@code null}
+         * or {@code respStat} is successful.
+         */
+        public LocalOcspResponse(OCSPResponse.ResponseStatus respStat)
+                throws IOException {
+            this(respStat, null, null);
+        }
+
+        /**
+         * Construct a response from a list of certificate
+         * status objects and extensions.
+         *
+         * @param respStat the status of the entire response
+         * @param itemMap a {@code Map} of {@code CertId} objects and their
+         * respective revocation statuses from the server's response DB.
+         * @param reqExtensions a {@code Map} of request extensions
+         *
+         * @throws IOException if an error happens during encoding
+         * @throws NullPointerException if {@code respStat} is {@code null}
+         * or {@code respStat} is successful, and a {@code null} {@code itemMap}
+         * has been provided.
+         */
+        public LocalOcspResponse(OCSPResponse.ResponseStatus respStat,
+                Map<CertId, CertStatusInfo> itemMap,
+                Map<String, Extension> reqExtensions) throws IOException {
+            responseStatus = Objects.requireNonNull(respStat,
+                    "Illegal null response status");
+            if (responseStatus == ResponseStatus.SUCCESSFUL) {
+                respItemMap = Objects.requireNonNull(itemMap,
+                        "SUCCESSFUL responses must have a response map");
+                producedAtDate = new Date();
+
+                // Turn the answerd from the response DB query into a list
+                // of single responses.
+                for (CertId id : itemMap.keySet()) {
+                    singleResponseList.add(
+                            new LocalSingleResponse(id, itemMap.get(id)));
+                }
+
+                responseExtensions = setResponseExtensions(reqExtensions);
+                certificates = new ArrayList<>();
+                if (signerCert != issuerCert) {
+                    certificates.add(signerCert);
+                }
+                certificates.add(issuerCert);
+            } else {
+                respItemMap = null;
+                producedAtDate = null;
+                responseExtensions = null;
+                certificates = null;
+            }
+            encodedResponse = this.getBytes();
+        }
+
+        /**
+         * Set the response extensions based on the request extensions
+         * that were received.  Right now, this is limited to the
+         * OCSP nonce extension.
+         *
+         * @param reqExts a {@code Map} of zero or more request extensions
+         *
+         * @return a {@code Map} of zero or more response extensions, keyed
+         * by the extension object identifier in {@code String} form.
+         */
+        private Map<String, Extension> setResponseExtensions(
+                Map<String, Extension> reqExts) {
+            Map<String, Extension> respExts = new HashMap<>();
+            String ocspNonceStr = PKIXExtensions.OCSPNonce_Id.toString();
+
+            if (reqExts != null) {
+                for (String id : reqExts.keySet()) {
+                    if (id.equals(ocspNonceStr)) {
+                        // We found a nonce, add it into the response extensions
+                        Extension ext = reqExts.get(id);
+                        if (ext != null) {
+                            respExts.put(id, ext);
+                            log("Added OCSP Nonce to response");
+                        } else {
+                            log("Error: Found nonce entry, but found null " +
+                                    "value.  Skipping");
+                        }
+                    }
+                }
+            }
+
+            return respExts;
+        }
+
+        /**
+         * Get the DER-encoded response bytes for this response
+         *
+         * @return a byte array containing the DER-encoded bytes for
+         * the response
+         *
+         * @throws IOException if any encoding errors occur
+         */
+        private byte[] getBytes() throws IOException {
+            DerOutputStream outerSeq = new DerOutputStream();
+            DerOutputStream responseStream = new DerOutputStream();
+            responseStream.putEnumerated(responseStatus.ordinal());
+            if (responseStatus == ResponseStatus.SUCCESSFUL &&
+                    respItemMap != null) {
+                encodeResponseBytes(responseStream);
+            }
+
+            // Commit the outermost sequence bytes
+            outerSeq.write(DerValue.tag_Sequence, responseStream);
+            return outerSeq.toByteArray();
+        }
+
+        private void encodeResponseBytes(DerOutputStream responseStream)
+                throws IOException {
+            DerOutputStream explicitZero = new DerOutputStream();
+            DerOutputStream respItemStream = new DerOutputStream();
+
+            respItemStream.putOID(OCSP_BASIC_RESPONSE_OID);
+
+            byte[] basicOcspBytes = encodeBasicOcspResponse();
+            respItemStream.putOctetString(basicOcspBytes);
+            explicitZero.write(DerValue.tag_Sequence, respItemStream);
+            responseStream.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                    true, (byte)0), explicitZero);
+        }
+
+        private byte[] encodeBasicOcspResponse() throws IOException {
+            DerOutputStream outerSeq = new DerOutputStream();
+            DerOutputStream basicORItemStream = new DerOutputStream();
+
+            // Encode the tbsResponse
+            byte[] tbsResponseBytes = encodeTbsResponse();
+            basicORItemStream.write(tbsResponseBytes);
+
+            try {
+                sigAlgId.derEncode(basicORItemStream);
+
+                // Create the signature
+                Signature sig = Signature.getInstance(sigAlgId.getName());
+                sig.initSign(signerKey);
+                sig.update(tbsResponseBytes);
+                signature = sig.sign();
+                basicORItemStream.putBitString(signature);
+            } catch (GeneralSecurityException exc) {
+                err(exc);
+                throw new IOException(exc);
+            }
+
+            // Add certificates
+            try {
+                DerOutputStream certStream = new DerOutputStream();
+                ArrayList<DerValue> certList = new ArrayList<>();
+                if (signerCert != issuerCert) {
+                    certList.add(new DerValue(signerCert.getEncoded()));
+                }
+                certList.add(new DerValue(issuerCert.getEncoded()));
+                DerValue[] dvals = new DerValue[certList.size()];
+                certStream.putSequence(certList.toArray(dvals));
+                basicORItemStream.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                        true, (byte)0), certStream);
+            } catch (CertificateEncodingException cex) {
+                err(cex);
+                throw new IOException(cex);
+            }
+
+            // Commit the outermost sequence bytes
+            outerSeq.write(DerValue.tag_Sequence, basicORItemStream);
+            return outerSeq.toByteArray();
+        }
+
+        private byte[] encodeTbsResponse() throws IOException {
+            DerOutputStream outerSeq = new DerOutputStream();
+            DerOutputStream tbsStream = new DerOutputStream();
+
+            // Note: We're not going explicitly assert the version
+            tbsStream.write(respId.getEncoded());
+            tbsStream.putGeneralizedTime(producedAtDate);
+
+            // Sequence of responses
+            encodeSingleResponses(tbsStream);
+
+            // TODO: add response extension support
+            encodeExtensions(tbsStream);
+
+            outerSeq.write(DerValue.tag_Sequence, tbsStream);
+            return outerSeq.toByteArray();
+        }
+
+        private void encodeSingleResponses(DerOutputStream tbsStream)
+                throws IOException {
+            DerValue[] srDerVals = new DerValue[singleResponseList.size()];
+            int srDvCtr = 0;
+
+            for (LocalSingleResponse lsr : singleResponseList) {
+                srDerVals[srDvCtr++] = new DerValue(lsr.getBytes());
+            }
+
+            tbsStream.putSequence(srDerVals);
+        }
+
+        private void encodeExtensions(DerOutputStream tbsStream)
+                throws IOException {
+            DerOutputStream extSequence = new DerOutputStream();
+            DerOutputStream extItems = new DerOutputStream();
+
+            for (Extension ext : responseExtensions.values()) {
+                ext.encode(extItems);
+            }
+            extSequence.write(DerValue.tag_Sequence, extItems);
+            tbsStream.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
+                    (byte)1), extSequence);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("OCSP Response: ").append(responseStatus).append("\n");
+            if (responseStatus == ResponseStatus.SUCCESSFUL) {
+                sb.append("Response Type: ").
+                        append(OCSP_BASIC_RESPONSE_OID.toString()).append("\n");
+                sb.append(String.format("Version: %d (0x%X)", version + 1,
+                        version)).append("\n");
+                sb.append("Responder Id: ").append(respId.toString()).
+                        append("\n");
+                sb.append("Produced At: ").
+                        append(utcDateFmt.format(producedAtDate)).append("\n");
+
+                int srCtr = 0;
+                for (LocalSingleResponse lsr : singleResponseList) {
+                    sb.append("SingleResponse [").append(srCtr++).append("]\n");
+                    sb.append(lsr);
+                }
+
+                if (!responseExtensions.isEmpty()) {
+                    sb.append("Extensions (").append(responseExtensions.size()).
+                            append(")\n");
+                    for (Extension ext : responseExtensions.values()) {
+                        sb.append("\t").append(ext).append("\n");
+                    }
+                } else {
+                    sb.append("\n");
+                }
+
+                if (signature != null) {
+                    sb.append("Signature: ").append(sigAlgId).append("\n");
+                    sb.append(dumpHexBytes(signature)).append("\n");
+                    int certCtr = 0;
+                    for (X509Certificate cert : certificates) {
+                        sb.append("Certificate [").append(certCtr++).append("]").
+                                append("\n");
+                        sb.append("\tSubject: ");
+                        sb.append(cert.getSubjectX500Principal()).append("\n");
+                        sb.append("\tIssuer: ");
+                        sb.append(cert.getIssuerX500Principal()).append("\n");
+                        sb.append("\tSerial: ").append(cert.getSerialNumber());
+                        sb.append("\n");
+                    }
+                }
+            }
+
+            return sb.toString();
+        }
+
+        private class LocalSingleResponse {
+            private final CertId certId;
+            private final CertStatusInfo csInfo;
+            private final Date thisUpdate;
+            private final Date lsrNextUpdate;
+            private final Map<String, Extension> singleExtensions;
+
+            public LocalSingleResponse(CertId cid, CertStatusInfo info) {
+                certId = Objects.requireNonNull(cid, "CertId must be non-null");
+                csInfo = Objects.requireNonNull(info,
+                        "CertStatusInfo must be non-null");
+
+                // For now, we'll keep things simple and make the thisUpdate
+                // field the same as the producedAt date.
+                thisUpdate = producedAtDate;
+                lsrNextUpdate = getNextUpdate();
+
+                // TODO Add extensions support
+                singleExtensions = Collections.emptyMap();
+            }
+
+            @Override
+            public String toString() {
+                StringBuilder sb = new StringBuilder();
+                sb.append("Certificate Status: ").append(csInfo.getType());
+                sb.append("\n");
+                if (csInfo.getType() == CertStatus.CERT_STATUS_REVOKED) {
+                    sb.append("Revocation Time: ");
+                    sb.append(utcDateFmt.format(csInfo.getRevocationTime()));
+                    sb.append("\n");
+                    if (csInfo.getRevocationReason() != null) {
+                        sb.append("Revocation Reason: ");
+                        sb.append(csInfo.getRevocationReason()).append("\n");
+                    }
+                }
+
+                sb.append("CertId, Algorithm = ");
+                sb.append(certId.getHashAlgorithm()).append("\n");
+                sb.append("\tIssuer Name Hash: ");
+                sb.append(dumpHexBytes(certId.getIssuerNameHash(), 256, "", ""));
+                sb.append("\n");
+                sb.append("\tIssuer Key Hash: ");
+                sb.append(dumpHexBytes(certId.getIssuerKeyHash(), 256, "", ""));
+                sb.append("\n");
+                sb.append("\tSerial Number: ").append(certId.getSerialNumber());
+                sb.append("\n");
+                sb.append("This Update: ");
+                sb.append(utcDateFmt.format(thisUpdate)).append("\n");
+                if (lsrNextUpdate != null) {
+                    sb.append("Next Update: ");
+                    sb.append(utcDateFmt.format(lsrNextUpdate)).append("\n");
+                }
+
+                if (!singleExtensions.isEmpty()) {
+                    sb.append("Extensions (").append(singleExtensions.size()).
+                            append(")\n");
+                    for (Extension ext : singleExtensions.values()) {
+                        sb.append("\t").append(ext).append("\n");
+                    }
+                }
+
+                return sb.toString();
+            }
+
+            public byte[] getBytes() throws IOException {
+                byte[] nullData = { };
+                DerOutputStream responseSeq = new DerOutputStream();
+                DerOutputStream srStream = new DerOutputStream();
+
+                // Encode the CertId
+                certId.encode(srStream);
+
+                // Next, encode the CertStatus field
+                CertStatus csiType = csInfo.getType();
+                switch (csiType) {
+                    case CERT_STATUS_GOOD:
+                        srStream.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                                false, (byte)0), nullData);
+                        break;
+                    case CERT_STATUS_REVOKED:
+                        DerOutputStream revInfo = new DerOutputStream();
+                        revInfo.putGeneralizedTime(csInfo.getRevocationTime());
+                        CRLReason revReason = csInfo.getRevocationReason();
+                        if (revReason != null) {
+                            byte[] revDer = new byte[3];
+                            revDer[0] = DerValue.tag_Enumerated;
+                            revDer[1] = 1;
+                            revDer[2] = (byte)revReason.ordinal();
+                            revInfo.write(DerValue.createTag(
+                                    DerValue.TAG_CONTEXT, true, (byte)0),
+                                    revDer);
+                        }
+                        srStream.write(DerValue.createTag(
+                                DerValue.TAG_CONTEXT, true, (byte)1),
+                                revInfo);
+                        break;
+                    case CERT_STATUS_UNKNOWN:
+                        srStream.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                                false, (byte)2), nullData);
+                        break;
+                    default:
+                        throw new IOException("Unknown CertStatus: " + csiType);
+                }
+
+                // Add the necessary dates
+                srStream.putGeneralizedTime(thisUpdate);
+                if (lsrNextUpdate != null) {
+                    DerOutputStream nuStream = new DerOutputStream();
+                    nuStream.putGeneralizedTime(lsrNextUpdate);
+                    srStream.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                            true, (byte)0), nuStream);
+                }
+
+                // TODO add singleResponse Extension support
+
+                // Add the single response to the response output stream
+                responseSeq.write(DerValue.tag_Sequence, srStream);
+                return responseSeq.toByteArray();
+            }
+        }
+    }
+}
--- a/jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/test/javax/net/ssl/DTLS/NoMacInitialClientHello.java	Wed Aug 05 12:19:38 2015 -0700
@@ -29,7 +29,8 @@
  * @bug 8043758
  * @summary Datagram Transport Layer Security (DTLS)
  * @compile DTLSOverDatagram.java
- * @run main/othervm NoMacInitialClientHello
+ * @run main/othervm -Djdk.tls.client.enableStatusRequestExtension=false
+ *      NoMacInitialClientHello
  */
 
 import java.net.DatagramPacket;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/Stapling/HttpsUrlConnClient.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,797 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS
+ * @library ../../../../java/security/testlibrary
+ * @build CertificateBuilder SimpleOCSPServer
+ * @run main/othervm HttpsUrlConnClient
+ */
+
+import java.io.*;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.net.Socket;
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.GeneralSecurityException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.Certificate;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+import sun.security.validator.ValidatorException;
+
+public class HttpsUrlConnClient {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    static final byte[] LINESEP = { 10 };
+    static final Base64.Encoder B64E = Base64.getMimeEncoder(64, LINESEP);
+
+    // Turn on TLS debugging
+    static boolean debug = true;
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    static String passwd = "passphrase";
+    static String ROOT_ALIAS = "root";
+    static String INT_ALIAS = "intermediate";
+    static String SSL_ALIAS = "ssl";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // PKI components we will need for this test
+    static KeyStore rootKeystore;           // Root CA Keystore
+    static KeyStore intKeystore;            // Intermediate CA Keystore
+    static KeyStore serverKeystore;         // SSL Server Keystore
+    static KeyStore trustStore;             // SSL Client trust store
+    static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
+    static int rootOcspPort;                // Port number for root OCSP
+    static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
+    static int intOcspPort;                 // Port number for intermed. OCSP
+
+    private static final String SIMPLE_WEB_PAGE = "<HTML>\n" +
+            "<HEAD><Title>Web Page!</Title></HEAD>\n" +
+            "<BODY><H1>Web Page!</H1></BODY>\n</HTML>";
+    private static final SimpleDateFormat utcDateFmt =
+            new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+    public static void main(String[] args) throws Exception {
+        if (debug) {
+            System.setProperty("javax.net.debug", "ssl");
+        }
+
+        System.setProperty("javax.net.ssl.keyStore", "");
+        System.setProperty("javax.net.ssl.keyStorePassword", "");
+        System.setProperty("javax.net.ssl.trustStore", "");
+        System.setProperty("javax.net.ssl.trustStorePassword", "");
+
+        // Create the PKI we will use for the test and start the OCSP servers
+        createPKI();
+        utcDateFmt.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+        testPKIXParametersRevEnabled();
+
+        // shut down the OCSP responders before finishing the test
+        intOcsp.stop();
+        rootOcsp.stop();
+    }
+
+    /**
+     * Do a basic connection using PKIXParameters with revocation checking
+     * enabled and client-side OCSP disabled.  It will only pass if all
+     * stapled responses are present, valid and have a GOOD status.
+     */
+    static void testPKIXParametersRevEnabled() throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+
+        System.out.println("=====================================");
+        System.out.println("Stapling enabled, PKIXParameters with");
+        System.out.println("Revocation checking enabled ");
+        System.out.println("=====================================");
+
+        // Set the certificate entry in the intermediate OCSP responder
+        // with a revocation date of 8 hours ago.
+        X509Certificate sslCert =
+                (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,
+                        new Date(System.currentTimeMillis() -
+                                TimeUnit.HOURS.toMillis(8))));
+        intOcsp.updateStatusDb(revInfo);
+
+        // Set up revocation checking on the client with no client-side
+        // OCSP fall-back
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+        Security.setProperty("ocsp.enable", "false");
+
+        HttpsUrlConnClient sslTest = new HttpsUrlConnClient(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (!checkClientValidationFailure(tr.clientExc, BasicReason.REVOKED)) {
+            if (tr.clientExc != null) {
+                throw tr.clientExc;
+            } else {
+                throw new RuntimeException(
+                        "Expected client failure, but the client succeeded");
+            }
+        }
+
+        // In this case the server should also have thrown an exception
+        // because of the client alert
+        if (tr.serverExc instanceof SSLHandshakeException) {
+            if (!tr.serverExc.getMessage().contains(
+                    "alert: bad_certificate_status_response")) {
+                throw tr.serverExc;
+            }
+        }
+
+        System.out.println("                PASS");
+        System.out.println("=====================================\n");
+    }
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide(ServerParameters servParams) throws Exception {
+
+        // Selectively enable or disable the feature
+        System.setProperty("jdk.tls.server.enableStatusRequestExtension",
+                Boolean.toString(servParams.enabled));
+
+        // Set all the other operating parameters
+        System.setProperty("jdk.tls.stapling.cacheSize",
+                Integer.toString(servParams.cacheSize));
+        System.setProperty("jdk.tls.stapling.cacheLifetime",
+                Integer.toString(servParams.cacheLifetime));
+        System.setProperty("jdk.tls.stapling.responseTimeout",
+                Integer.toString(servParams.respTimeout));
+        System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri);
+        System.setProperty("jdk.tls.stapling.responderOverride",
+                Boolean.toString(servParams.respOverride));
+        System.setProperty("jdk.tls.stapling.ignoreExtensions",
+                Boolean.toString(servParams.ignoreExts));
+
+        // Set keystores and trust stores for the server
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(serverKeystore, passwd.toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(trustStore);
+
+        SSLContext sslc = SSLContext.getInstance("TLS");
+        sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        SSLServerSocketFactory sslssf = sslc.getServerSocketFactory();
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+        serverPort = sslServerSocket.getLocalPort();
+        log("Server Port is " + serverPort);
+
+        // Dump the private key in PKCS8 format, not encrypted.  This
+        // key dump can be used if the traffic was captured using tcpdump
+        // or wireshark to look into the encrypted packets for debug purposes.
+        if (debug) {
+            byte[] keybytes = serverKeystore.getKey(SSL_ALIAS,
+                    passwd.toCharArray()).getEncoded();
+            PKCS8EncodedKeySpec p8spec = new PKCS8EncodedKeySpec(keybytes);
+            StringBuilder keyPem = new StringBuilder();
+            keyPem.append("-----BEGIN PRIVATE KEY-----\n");
+            keyPem.append(B64E.encodeToString(p8spec.getEncoded())).append("\n");
+            keyPem.append("-----END PRIVATE KEY-----\n");
+            log("Private key is:\n" + keyPem.toString());
+        }
+
+        /*
+         * Signal Client, we're ready for his connect.
+         */
+        serverReady = true;
+
+        try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+                BufferedReader in = new BufferedReader(
+                    new InputStreamReader(sslSocket.getInputStream()));
+                OutputStream out = sslSocket.getOutputStream()) {
+            StringBuilder hdrBldr = new StringBuilder();
+            String line;
+            while ((line = in.readLine()) != null && !line.isEmpty()) {
+                hdrBldr.append(line).append("\n");
+            }
+            String headerText = hdrBldr.toString();
+            log("Header Received: " + headerText.length() + " bytes\n" +
+                    headerText);
+
+            StringBuilder sb = new StringBuilder();
+            sb.append("HTTP/1.0 200 OK\r\n");
+            sb.append("Date: ").append(utcDateFmt.format(new Date())).
+                    append("\r\n");
+            sb.append("Content-Type: text/html\r\n");
+            sb.append("Content-Length: ").append(SIMPLE_WEB_PAGE.length());
+            sb.append("\r\n\r\n");
+            out.write(sb.toString().getBytes("UTF-8"));
+            out.write(SIMPLE_WEB_PAGE.getBytes("UTF-8"));
+            out.flush();
+            log("Server replied with:\n" + sb.toString() + SIMPLE_WEB_PAGE);
+        }
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide(ClientParameters cliParams) throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Selectively enable or disable the feature
+        System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+                Boolean.toString(cliParams.enabled));
+
+        HtucSSLSocketFactory sockFac = new HtucSSLSocketFactory(cliParams);
+        HttpsURLConnection.setDefaultSSLSocketFactory(sockFac);
+        URL location = new URL("https://localhost:" + serverPort);
+        HttpsURLConnection tlsConn =
+                (HttpsURLConnection)location.openConnection();
+        tlsConn.setConnectTimeout(5000);
+        tlsConn.setReadTimeout(5000);
+        tlsConn.setDoInput(true);
+
+        try (InputStream in = tlsConn.getInputStream()) {
+            // Check the response
+            if (debug && tlsConn.getResponseCode() !=
+                    HttpURLConnection.HTTP_OK) {
+                log("Received HTTP error: " + tlsConn.getResponseCode() +
+                        " - " + tlsConn.getResponseMessage());
+                throw new IOException("HTTP error: " +
+                        tlsConn.getResponseCode());
+            }
+
+            int contentLength = tlsConn.getContentLength();
+            if (contentLength == -1) {
+                contentLength = Integer.MAX_VALUE;
+            }
+            byte[] response = new byte[contentLength > 2048 ? 2048 : contentLength];
+            int total = 0;
+            while (total < contentLength) {
+                int count = in.read(response, total, response.length - total);
+                if (count < 0)
+                    break;
+
+                total += count;
+                log("Read " + count + " bytes (" + total + " total)");
+                if (total >= response.length && total < contentLength) {
+                    response = Arrays.copyOf(response, total * 2);
+                }
+            }
+            response = Arrays.copyOf(response, total);
+            String webPage = new String(response, 0, total);
+            if (debug) {
+                log("Web page:\n" + webPage);
+            }
+        }
+    }
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    HttpsUrlConnClient(ClientParameters cliParams,
+            ServerParameters servParams) throws Exception {
+        Exception startException = null;
+        try {
+            if (separateServerThread) {
+                startServer(servParams, true);
+                startClient(cliParams, false);
+            } else {
+                startClient(cliParams, true);
+                startServer(servParams, false);
+            }
+        } catch (Exception e) {
+            startException = e;
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            if (serverThread != null) {
+                serverThread.join();
+            }
+        } else {
+            if (clientThread != null) {
+                clientThread.join();
+            }
+        }
+    }
+
+    /**
+     * Checks a validation failure to see if it failed for the reason we think
+     * it should.  This comes in as an SSLException of some sort, but it
+     * encapsulates a ValidatorException which in turn encapsulates the
+     * CertPathValidatorException we are interested in.
+     *
+     * @param e the exception thrown at the top level
+     * @param reason the underlying CertPathValidatorException BasicReason
+     * we are expecting it to have.
+     *
+     * @return true if the reason matches up, false otherwise.
+     */
+    static boolean checkClientValidationFailure(Exception e,
+            BasicReason reason) {
+        boolean result = false;
+
+        if (e instanceof SSLException) {
+            Throwable valExc = e.getCause();
+            if (valExc instanceof sun.security.validator.ValidatorException) {
+                Throwable cause = valExc.getCause();
+                if (cause instanceof CertPathValidatorException) {
+                    CertPathValidatorException cpve =
+                            (CertPathValidatorException)cause;
+                    if (cpve.getReason() == reason) {
+                        result = true;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    TestResult getResult() {
+        TestResult tr = new TestResult();
+        tr.clientExc = clientException;
+        tr.serverExc = serverException;
+        return tr;
+    }
+
+    final void startServer(ServerParameters servParams, boolean newThread)
+            throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        doServerSide(servParams);
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide(servParams);
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    final void startClient(ClientParameters cliParams, boolean newThread)
+            throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        doClientSide(cliParams);
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide(cliParams);
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+
+    /**
+     * Creates the PKI components necessary for this test, including
+     * Root CA, Intermediate CA and SSL server certificates, the keystores
+     * for each entity, a client trust store, and starts the OCSP responders.
+     */
+    private static void createPKI() throws Exception {
+        CertificateBuilder cbld = new CertificateBuilder();
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        KeyStore.Builder keyStoreBuilder =
+                KeyStore.Builder.newInstance("PKCS12", null,
+                        new KeyStore.PasswordProtection(passwd.toCharArray()));
+
+        // Generate Root, IntCA, EE keys
+        KeyPair rootCaKP = keyGen.genKeyPair();
+        log("Generated Root CA KeyPair");
+        KeyPair intCaKP = keyGen.genKeyPair();
+        log("Generated Intermediate CA KeyPair");
+        KeyPair sslKP = keyGen.genKeyPair();
+        log("Generated SSL Cert KeyPair");
+
+        // Set up the Root CA Cert
+        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
+        cbld.setPublicKey(rootCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("1"));
+        // Make a 3 year validity starting from 60 days ago
+        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+        long end = start + TimeUnit.DAYS.toMillis(1085);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        // Make our Root CA Cert!
+        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Root CA Created:\n" + certInfo(rootCert));
+
+        // Now build a keystore and add the keys and cert
+        rootKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] rootChain = {rootCert};
+        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
+                passwd.toCharArray(), rootChain);
+
+        // Now fire up the OCSP responder
+        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
+        rootOcsp.enableLog(debug);
+        rootOcsp.setNextUpdateInterval(3600);
+        rootOcsp.start();
+        Thread.sleep(1000);         // Give the server a second to start up
+        rootOcspPort = rootOcsp.getPort();
+        String rootRespURI = "http://localhost:" + rootOcspPort;
+        log("Root OCSP Responder URI is " + rootRespURI);
+
+        // Now that we have the root keystore and OCSP responder we can
+        // create our intermediate CA.
+        cbld.reset();
+        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
+        cbld.setPublicKey(intCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("100"));
+        // Make a 2 year validity starting from 30 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
+        end = start + TimeUnit.DAYS.toMillis(730);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        cbld.addAIAExt(Collections.singletonList(rootRespURI));
+        // Make our Intermediate CA Cert!
+        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Intermediate CA Created:\n" + certInfo(intCaCert));
+
+        // Provide intermediate CA cert revocation info to the Root CA
+        // OCSP responder.
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(intCaCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        rootOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        intKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] intChain = {intCaCert, rootCert};
+        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
+                passwd.toCharArray(), intChain);
+        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // Now fire up the Intermediate CA OCSP responder
+        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
+                INT_ALIAS, null);
+        intOcsp.enableLog(debug);
+        intOcsp.setNextUpdateInterval(3600);
+        intOcsp.start();
+        Thread.sleep(1000);
+        intOcspPort = intOcsp.getPort();
+        String intCaRespURI = "http://localhost:" + intOcspPort;
+        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
+
+        // Last but not least, let's make our SSLCert and add it to its own
+        // Keystore
+        cbld.reset();
+        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
+        cbld.setPublicKey(sslKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("4096"));
+        // Make a 1 year validity starting from 7 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
+        end = start + TimeUnit.DAYS.toMillis(365);
+        cbld.setValidity(new Date(start), new Date(end));
+
+        // Add extensions
+        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
+        boolean[] kuBits = {true, false, true, false, false, false,
+            false, false, false};
+        cbld.addKeyUsageExt(kuBits);
+        List<String> ekuOids = new ArrayList<>();
+        ekuOids.add("1.3.6.1.5.5.7.3.1");
+        ekuOids.add("1.3.6.1.5.5.7.3.2");
+        cbld.addExtendedKeyUsageExt(ekuOids);
+        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
+        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
+        // Make our SSL Server Cert!
+        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("SSL Certificate Created:\n" + certInfo(sslCert));
+
+        // Provide SSL server cert revocation info to the Intermeidate CA
+        // OCSP responder.
+        revInfo = new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        serverKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] sslChain = {sslCert, intCaCert, rootCert};
+        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
+                passwd.toCharArray(), sslChain);
+        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // And finally a Trust Store for the client
+        trustStore = keyStoreBuilder.getKeyStore();
+        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
+    }
+
+    private static void addCommonExts(CertificateBuilder cbld,
+            PublicKey subjKey, PublicKey authKey) throws IOException {
+        cbld.addSubjectKeyIdExt(subjKey);
+        cbld.addAuthorityKeyIdExt(authKey);
+    }
+
+    private static void addCommonCAExts(CertificateBuilder cbld)
+            throws IOException {
+        cbld.addBasicConstraintsExt(true, true, -1);
+        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
+        boolean[] kuBitSettings = {true, false, false, false, false, true,
+            true, false, false};
+        cbld.addKeyUsageExt(kuBitSettings);
+    }
+
+    /**
+     * Helper routine that dumps only a few cert fields rather than
+     * the whole toString() output.
+     *
+     * @param cert an X509Certificate to be displayed
+     *
+     * @return the String output of the issuer, subject and
+     * serial number
+     */
+    private static String certInfo(X509Certificate cert) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
+                append("\n");
+        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * Log a message on stdout
+     *
+     * @param message The message to log
+     */
+    private static void log(String message) {
+        if (debug) {
+            System.out.println(message);
+        }
+    }
+
+    // The following two classes are Simple nested class to group a handful
+    // of configuration parameters used before starting a client or server.
+    // We'll just access the data members directly for convenience.
+    static class ClientParameters {
+        boolean enabled = true;
+        PKIXBuilderParameters pkixParams = null;
+        PKIXRevocationChecker revChecker = null;
+
+        ClientParameters() { }
+    }
+
+    static class ServerParameters {
+        boolean enabled = true;
+        int cacheSize = 256;
+        int cacheLifetime = 3600;
+        int respTimeout = 5000;
+        String respUri = "";
+        boolean respOverride = false;
+        boolean ignoreExts = false;
+
+        ServerParameters() { }
+    }
+
+    static class TestResult {
+        Exception serverExc = null;
+        Exception clientExc = null;
+    }
+
+    static class HtucSSLSocketFactory extends SSLSocketFactory {
+        SSLContext sslc = SSLContext.getInstance("TLS");
+
+        HtucSSLSocketFactory(ClientParameters cliParams)
+                throws GeneralSecurityException {
+            super();
+
+            // Create the Trust Manager Factory using the PKIX variant
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+
+            // If we have a customized pkixParameters then use it
+            if (cliParams.pkixParams != null) {
+                // LIf we have a customized PKIXRevocationChecker, add
+                // it to the PKIXBuilderParameters.
+                if (cliParams.revChecker != null) {
+                    cliParams.pkixParams.addCertPathChecker(
+                            cliParams.revChecker);
+                }
+
+                ManagerFactoryParameters trustParams =
+                        new CertPathTrustManagerParameters(
+                                cliParams.pkixParams);
+                tmf.init(trustParams);
+            } else {
+                tmf.init(trustStore);
+            }
+
+            sslc.init(null, tmf.getTrustManagers(), null);
+        }
+
+        @Override
+        public Socket createSocket(Socket s, String host, int port,
+                boolean autoClose) throws IOException {
+            Socket sock =  sslc.getSocketFactory().createSocket(s, host, port,
+                    autoClose);
+            setCiphers(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port)
+                throws IOException {
+            Socket sock = sslc.getSocketFactory().createSocket(host, port);
+            setCiphers(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port,
+                InetAddress localAddress, int localPort) throws IOException {
+            Socket sock = sslc.getSocketFactory().createSocket(host, port,
+                    localAddress, localPort);
+            setCiphers(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port)
+                throws IOException {
+            Socket sock =  sslc.getSocketFactory().createSocket(host, port);
+            setCiphers(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port,
+                InetAddress localAddress, int localPort)
+                throws IOException {
+            Socket sock =  sslc.getSocketFactory().createSocket(host, port,
+                    localAddress, localPort);
+            setCiphers(sock);
+            return sock;
+        }
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            return sslc.getDefaultSSLParameters().getCipherSuites();
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return sslc.getSupportedSSLParameters().getCipherSuites();
+        }
+
+        private static void setCiphers(Socket sock) {
+            if (sock instanceof SSLSocket) {
+                String[] ciphers = { "TLS_RSA_WITH_AES_128_CBC_SHA" };
+                ((SSLSocket)sock).setEnabledCipherSuites(ciphers);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/Stapling/SSLEngineWithStapling.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,655 @@
+/*
+ * Copyright (c) 2003, 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS
+ * @library ../../../../java/security/testlibrary
+ * @build CertificateBuilder SimpleOCSPServer
+ * @run main/othervm SSLEngineWithStapling
+ */
+
+/**
+ * A SSLEngine usage example which simplifies the presentation
+ * by removing the I/O and multi-threading concerns.
+ *
+ * The test creates two SSLEngines, simulating a client and server.
+ * The "transport" layer consists two byte buffers:  think of them
+ * as directly connected pipes.
+ *
+ * Note, this is a *very* simple example: real code will be much more
+ * involved.  For example, different threading and I/O models could be
+ * used, transport mechanisms could close unexpectedly, and so on.
+ *
+ * When this application runs, notice that several messages
+ * (wrap/unwrap) pass before any application data is consumed or
+ * produced.  (For more information, please see the SSL/TLS
+ * specifications.)  There may several steps for a successful handshake,
+ * so it's typical to see the following series of operations:
+ *
+ *      client          server          message
+ *      ======          ======          =======
+ *      wrap()          ...             ClientHello
+ *      ...             unwrap()        ClientHello
+ *      ...             wrap()          ServerHello/Certificate
+ *      unwrap()        ...             ServerHello/Certificate
+ *      wrap()          ...             ClientKeyExchange
+ *      wrap()          ...             ChangeCipherSpec
+ *      wrap()          ...             Finished
+ *      ...             unwrap()        ClientKeyExchange
+ *      ...             unwrap()        ChangeCipherSpec
+ *      ...             unwrap()        Finished
+ *      ...             wrap()          ChangeCipherSpec
+ *      ...             wrap()          Finished
+ *      unwrap()        ...             ChangeCipherSpec
+ *      unwrap()        ...             Finished
+ */
+
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.io.*;
+import java.math.BigInteger;
+import java.security.*;
+import java.nio.*;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509Certificate;
+import java.security.cert.X509CertSelector;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+
+public class SSLEngineWithStapling {
+
+    /*
+     * Enables logging of the SSLEngine operations.
+     */
+    private static final boolean logging = true;
+
+    /*
+     * Enables the JSSE system debugging system property:
+     *
+     *     -Djavax.net.debug=all
+     *
+     * This gives a lot of low-level information about operations underway,
+     * including specific handshake messages, and might be best examined
+     * after gaining some familiarity with this application.
+     */
+    private static final boolean debug = false;
+
+    private SSLEngine clientEngine;     // client Engine
+    private ByteBuffer clientOut;       // write side of clientEngine
+    private ByteBuffer clientIn;        // read side of clientEngine
+
+    private SSLEngine serverEngine;     // server Engine
+    private ByteBuffer serverOut;       // write side of serverEngine
+    private ByteBuffer serverIn;        // read side of serverEngine
+
+    /*
+     * For data transport, this example uses local ByteBuffers.  This
+     * isn't really useful, but the purpose of this example is to show
+     * SSLEngine concepts, not how to do network transport.
+     */
+    private ByteBuffer cTOs;            // "reliable" transport client->server
+    private ByteBuffer sTOc;            // "reliable" transport server->client
+
+    /*
+     * The following is to set up the keystores.
+     */
+    static final String passwd = "passphrase";
+    static final String ROOT_ALIAS = "root";
+    static final String INT_ALIAS = "intermediate";
+    static final String SSL_ALIAS = "ssl";
+
+    // PKI components we will need for this test
+    static KeyStore rootKeystore;           // Root CA Keystore
+    static KeyStore intKeystore;            // Intermediate CA Keystore
+    static KeyStore serverKeystore;         // SSL Server Keystore
+    static KeyStore trustStore;             // SSL Client trust store
+    static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
+    static int rootOcspPort;                // Port number for root OCSP
+    static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
+    static int intOcspPort;                 // Port number for intermed. OCSP
+
+    /*
+     * Main entry point for this test.
+     */
+    public static void main(String args[]) throws Exception {
+        if (debug) {
+            System.setProperty("javax.net.debug", "ssl");
+        }
+
+        // Create the PKI we will use for the test and start the OCSP servers
+        createPKI();
+
+        // Set the certificate entry in the intermediate OCSP responder
+        // with a revocation date of 8 hours ago.
+        X509Certificate sslCert =
+                (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,
+                        new Date(System.currentTimeMillis() -
+                                TimeUnit.HOURS.toMillis(8))));
+        intOcsp.updateStatusDb(revInfo);
+
+        SSLEngineWithStapling test = new SSLEngineWithStapling();
+        try {
+            test.runTest();
+            throw new RuntimeException("Expected failure due to revocation " +
+                    "did not occur");
+        } catch (Exception e) {
+            if (!checkClientValidationFailure(e,
+                    CertPathValidatorException.BasicReason.REVOKED)) {
+                System.out.println("*** Didn't find the exception we wanted");
+                throw e;
+            }
+        }
+
+        System.out.println("Test Passed.");
+    }
+
+    /*
+     * Create an initialized SSLContext to use for these tests.
+     */
+    public SSLEngineWithStapling() throws Exception {
+        System.setProperty("javax.net.ssl.keyStore", "");
+        System.setProperty("javax.net.ssl.keyStorePassword", "");
+        System.setProperty("javax.net.ssl.trustStore", "");
+        System.setProperty("javax.net.ssl.trustStorePassword", "");
+
+        // Enable OCSP Stapling on both client and server sides, but turn off
+        // client-side OCSP for revocation checking.  This ensures that the
+        // revocation information from the test has to come via stapling.
+        System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+                Boolean.toString(true));
+        System.setProperty("jdk.tls.server.enableStatusRequestExtension",
+                Boolean.toString(true));
+        Security.setProperty("ocsp.enable", "false");
+    }
+
+    /*
+     * Run the test.
+     *
+     * Sit in a tight loop, both engines calling wrap/unwrap regardless
+     * of whether data is available or not.  We do this until both engines
+     * report back they are closed.
+     *
+     * The main loop handles all of the I/O phases of the SSLEngine's
+     * lifetime:
+     *
+     *     initial handshaking
+     *     application data transfer
+     *     engine closing
+     *
+     * One could easily separate these phases into separate
+     * sections of code.
+     */
+    private void runTest() throws Exception {
+        boolean dataDone = false;
+
+        createSSLEngines();
+        createBuffers();
+
+        SSLEngineResult clientResult;   // results from client's last operation
+        SSLEngineResult serverResult;   // results from server's last operation
+
+        /*
+         * Examining the SSLEngineResults could be much more involved,
+         * and may alter the overall flow of the application.
+         *
+         * For example, if we received a BUFFER_OVERFLOW when trying
+         * to write to the output pipe, we could reallocate a larger
+         * pipe, but instead we wait for the peer to drain it.
+         */
+        while (!isEngineClosed(clientEngine) ||
+                !isEngineClosed(serverEngine)) {
+
+            log("================");
+
+            clientResult = clientEngine.wrap(clientOut, cTOs);
+            log("client wrap: ", clientResult);
+            runDelegatedTasks(clientResult, clientEngine);
+
+            serverResult = serverEngine.wrap(serverOut, sTOc);
+            log("server wrap: ", serverResult);
+            runDelegatedTasks(serverResult, serverEngine);
+
+            cTOs.flip();
+            sTOc.flip();
+
+            log("----");
+
+            clientResult = clientEngine.unwrap(sTOc, clientIn);
+            log("client unwrap: ", clientResult);
+            runDelegatedTasks(clientResult, clientEngine);
+
+            serverResult = serverEngine.unwrap(cTOs, serverIn);
+            log("server unwrap: ", serverResult);
+            runDelegatedTasks(serverResult, serverEngine);
+
+            cTOs.compact();
+            sTOc.compact();
+
+            /*
+             * After we've transfered all application data between the client
+             * and server, we close the clientEngine's outbound stream.
+             * This generates a close_notify handshake message, which the
+             * server engine receives and responds by closing itself.
+             */
+            if (!dataDone && (clientOut.limit() == serverIn.position()) &&
+                    (serverOut.limit() == clientIn.position())) {
+
+                /*
+                 * A sanity check to ensure we got what was sent.
+                 */
+                checkTransfer(serverOut, clientIn);
+                checkTransfer(clientOut, serverIn);
+
+                log("\tClosing clientEngine's *OUTBOUND*...");
+                clientEngine.closeOutbound();
+                dataDone = true;
+            }
+        }
+    }
+
+    /*
+     * Using the SSLContext created during object creation,
+     * create/configure the SSLEngines we'll use for this test.
+     */
+    private void createSSLEngines() throws Exception {
+        // Initialize the KeyManager and TrustManager for the server
+        KeyManagerFactory servKmf = KeyManagerFactory.getInstance("PKIX");
+        servKmf.init(serverKeystore, passwd.toCharArray());
+        TrustManagerFactory servTmf =
+                TrustManagerFactory.getInstance("PKIX");
+        servTmf.init(trustStore);
+
+        // Initialize the TrustManager for the client with revocation checking
+        PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        pkixParams.setRevocationEnabled(true);
+        ManagerFactoryParameters mfp =
+                new CertPathTrustManagerParameters(pkixParams);
+        TrustManagerFactory cliTmf =
+                TrustManagerFactory.getInstance("PKIX");
+        cliTmf.init(mfp);
+
+        // Create the SSLContexts from the factories
+        SSLContext servCtx = SSLContext.getInstance("TLS");
+        servCtx.init(servKmf.getKeyManagers(), servTmf.getTrustManagers(),
+                null);
+        SSLContext cliCtx = SSLContext.getInstance("TLS");
+        cliCtx.init(null, cliTmf.getTrustManagers(), null);
+
+
+        /*
+         * Configure the serverEngine to act as a server in the SSL/TLS
+         * handshake.
+         */
+        serverEngine = servCtx.createSSLEngine();
+        serverEngine.setUseClientMode(false);
+        serverEngine.setNeedClientAuth(false);
+
+        /*
+         * Similar to above, but using client mode instead.
+         */
+        clientEngine = cliCtx.createSSLEngine("client", 80);
+        clientEngine.setUseClientMode(true);
+    }
+
+    /*
+     * Create and size the buffers appropriately.
+     */
+    private void createBuffers() {
+
+        /*
+         * We'll assume the buffer sizes are the same
+         * between client and server.
+         */
+        SSLSession session = clientEngine.getSession();
+        int appBufferMax = session.getApplicationBufferSize();
+        int netBufferMax = session.getPacketBufferSize();
+
+        /*
+         * We'll make the input buffers a bit bigger than the max needed
+         * size, so that unwrap()s following a successful data transfer
+         * won't generate BUFFER_OVERFLOWS.
+         *
+         * We'll use a mix of direct and indirect ByteBuffers for
+         * tutorial purposes only.  In reality, only use direct
+         * ByteBuffers when they give a clear performance enhancement.
+         */
+        clientIn = ByteBuffer.allocate(appBufferMax + 50);
+        serverIn = ByteBuffer.allocate(appBufferMax + 50);
+
+        cTOs = ByteBuffer.allocateDirect(netBufferMax);
+        sTOc = ByteBuffer.allocateDirect(netBufferMax);
+
+        clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
+        serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
+    }
+
+    /*
+     * If the result indicates that we have outstanding tasks to do,
+     * go ahead and run them in this thread.
+     */
+    private static void runDelegatedTasks(SSLEngineResult result,
+            SSLEngine engine) throws Exception {
+
+        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                log("\trunning delegated task...");
+                runnable.run();
+            }
+            HandshakeStatus hsStatus = engine.getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                    "handshake shouldn't need additional tasks");
+            }
+            log("\tnew HandshakeStatus: " + hsStatus);
+        }
+    }
+
+    private static boolean isEngineClosed(SSLEngine engine) {
+        return (engine.isOutboundDone() && engine.isInboundDone());
+    }
+
+    /*
+     * Simple check to make sure everything came across as expected.
+     */
+    private static void checkTransfer(ByteBuffer a, ByteBuffer b)
+            throws Exception {
+        a.flip();
+        b.flip();
+
+        if (!a.equals(b)) {
+            throw new Exception("Data didn't transfer cleanly");
+        } else {
+            log("\tData transferred cleanly");
+        }
+
+        a.position(a.limit());
+        b.position(b.limit());
+        a.limit(a.capacity());
+        b.limit(b.capacity());
+    }
+
+    /*
+     * Logging code
+     */
+    private static boolean resultOnce = true;
+
+    private static void log(String str, SSLEngineResult result) {
+        if (!logging) {
+            return;
+        }
+        if (resultOnce) {
+            resultOnce = false;
+            System.out.println("The format of the SSLEngineResult is: \n" +
+                "\t\"getStatus() / getHandshakeStatus()\" +\n" +
+                "\t\"bytesConsumed() / bytesProduced()\"\n");
+        }
+        HandshakeStatus hsStatus = result.getHandshakeStatus();
+        log(str +
+            result.getStatus() + "/" + hsStatus + ", " +
+            result.bytesConsumed() + "/" + result.bytesProduced() +
+            " bytes");
+        if (hsStatus == HandshakeStatus.FINISHED) {
+            log("\t...ready for application data");
+        }
+    }
+
+    private static void log(String str) {
+        if (logging) {
+            System.out.println(str);
+        }
+    }
+
+        /**
+     * Creates the PKI components necessary for this test, including
+     * Root CA, Intermediate CA and SSL server certificates, the keystores
+     * for each entity, a client trust store, and starts the OCSP responders.
+     */
+    private static void createPKI() throws Exception {
+        CertificateBuilder cbld = new CertificateBuilder();
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        KeyStore.Builder keyStoreBuilder =
+                KeyStore.Builder.newInstance("PKCS12", null,
+                        new KeyStore.PasswordProtection(passwd.toCharArray()));
+
+        // Generate Root, IntCA, EE keys
+        KeyPair rootCaKP = keyGen.genKeyPair();
+        log("Generated Root CA KeyPair");
+        KeyPair intCaKP = keyGen.genKeyPair();
+        log("Generated Intermediate CA KeyPair");
+        KeyPair sslKP = keyGen.genKeyPair();
+        log("Generated SSL Cert KeyPair");
+
+        // Set up the Root CA Cert
+        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
+        cbld.setPublicKey(rootCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("1"));
+        // Make a 3 year validity starting from 60 days ago
+        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+        long end = start + TimeUnit.DAYS.toMillis(1085);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        // Make our Root CA Cert!
+        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Root CA Created:\n" + certInfo(rootCert));
+
+        // Now build a keystore and add the keys and cert
+        rootKeystore = keyStoreBuilder.getKeyStore();
+        java.security.cert.Certificate[] rootChain = {rootCert};
+        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
+                passwd.toCharArray(), rootChain);
+
+        // Now fire up the OCSP responder
+        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
+        rootOcsp.enableLog(logging);
+        rootOcsp.setNextUpdateInterval(3600);
+        rootOcsp.start();
+        Thread.sleep(1000);         // Give the server a second to start up
+        rootOcspPort = rootOcsp.getPort();
+        String rootRespURI = "http://localhost:" + rootOcspPort;
+        log("Root OCSP Responder URI is " + rootRespURI);
+
+        // Now that we have the root keystore and OCSP responder we can
+        // create our intermediate CA.
+        cbld.reset();
+        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
+        cbld.setPublicKey(intCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("100"));
+        // Make a 2 year validity starting from 30 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
+        end = start + TimeUnit.DAYS.toMillis(730);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        cbld.addAIAExt(Collections.singletonList(rootRespURI));
+        // Make our Intermediate CA Cert!
+        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Intermediate CA Created:\n" + certInfo(intCaCert));
+
+        // Provide intermediate CA cert revocation info to the Root CA
+        // OCSP responder.
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(intCaCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        rootOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        intKeystore = keyStoreBuilder.getKeyStore();
+        java.security.cert.Certificate[] intChain = {intCaCert, rootCert};
+        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
+                passwd.toCharArray(), intChain);
+        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // Now fire up the Intermediate CA OCSP responder
+        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
+                INT_ALIAS, null);
+        intOcsp.enableLog(logging);
+        intOcsp.setNextUpdateInterval(3600);
+        intOcsp.start();
+        Thread.sleep(1000);
+        intOcspPort = intOcsp.getPort();
+        String intCaRespURI = "http://localhost:" + intOcspPort;
+        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
+
+        // Last but not least, let's make our SSLCert and add it to its own
+        // Keystore
+        cbld.reset();
+        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
+        cbld.setPublicKey(sslKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("4096"));
+        // Make a 1 year validity starting from 7 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
+        end = start + TimeUnit.DAYS.toMillis(365);
+        cbld.setValidity(new Date(start), new Date(end));
+
+        // Add extensions
+        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
+        boolean[] kuBits = {true, false, true, false, false, false,
+            false, false, false};
+        cbld.addKeyUsageExt(kuBits);
+        List<String> ekuOids = new ArrayList<>();
+        ekuOids.add("1.3.6.1.5.5.7.3.1");
+        ekuOids.add("1.3.6.1.5.5.7.3.2");
+        cbld.addExtendedKeyUsageExt(ekuOids);
+        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
+        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
+        // Make our SSL Server Cert!
+        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("SSL Certificate Created:\n" + certInfo(sslCert));
+
+        // Provide SSL server cert revocation info to the Intermeidate CA
+        // OCSP responder.
+        revInfo = new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        serverKeystore = keyStoreBuilder.getKeyStore();
+        java.security.cert.Certificate[] sslChain = {sslCert, intCaCert, rootCert};
+        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
+                passwd.toCharArray(), sslChain);
+        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // And finally a Trust Store for the client
+        trustStore = keyStoreBuilder.getKeyStore();
+        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
+    }
+
+    private static void addCommonExts(CertificateBuilder cbld,
+            PublicKey subjKey, PublicKey authKey) throws IOException {
+        cbld.addSubjectKeyIdExt(subjKey);
+        cbld.addAuthorityKeyIdExt(authKey);
+    }
+
+    private static void addCommonCAExts(CertificateBuilder cbld)
+            throws IOException {
+        cbld.addBasicConstraintsExt(true, true, -1);
+        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
+        boolean[] kuBitSettings = {true, false, false, false, false, true,
+            true, false, false};
+        cbld.addKeyUsageExt(kuBitSettings);
+    }
+
+    /**
+     * Helper routine that dumps only a few cert fields rather than
+     * the whole toString() output.
+     *
+     * @param cert an X509Certificate to be displayed
+     *
+     * @return the String output of the issuer, subject and
+     * serial number
+     */
+    private static String certInfo(X509Certificate cert) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
+                append("\n");
+        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * Checks a validation failure to see if it failed for the reason we think
+     * it should.  This comes in as an SSLException of some sort, but it
+     * encapsulates a ValidatorException which in turn encapsulates the
+     * CertPathValidatorException we are interested in.
+     *
+     * @param e the exception thrown at the top level
+     * @param reason the underlying CertPathValidatorException BasicReason
+     * we are expecting it to have.
+     *
+     * @return true if the reason matches up, false otherwise.
+     */
+    static boolean checkClientValidationFailure(Exception e,
+            CertPathValidatorException.BasicReason reason) {
+        boolean result = false;
+
+        if (e instanceof SSLException) {
+            Throwable sslhe = e.getCause();
+            if (sslhe instanceof SSLHandshakeException) {
+                Throwable valExc = sslhe.getCause();
+                if (valExc instanceof sun.security.validator.ValidatorException) {
+                    Throwable cause = valExc.getCause();
+                    if (cause instanceof CertPathValidatorException) {
+                        CertPathValidatorException cpve =
+                                (CertPathValidatorException)cause;
+                        if (cpve.getReason() == reason) {
+                            result = true;
+                        }
+                    }
+                }
+            }
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/Stapling/SSLSocketWithStapling.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,905 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS
+ * @library ../../../../java/security/testlibrary
+ * @build CertificateBuilder SimpleOCSPServer
+ * @run main/othervm SSLSocketWithStapling
+ */
+
+import java.io.*;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.Certificate;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.PKIXRevocationChecker.Option;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+
+public class SSLSocketWithStapling {
+
+    /*
+     * =============================================================
+     * Set the various variables needed for the tests, then
+     * specify what tests to run on each side.
+     */
+
+    // Turn on TLS debugging
+    static boolean debug = false;
+
+    /*
+     * Should we run the client or server in a separate thread?
+     * Both sides can throw exceptions, but do you have a preference
+     * as to which side should be the main thread.
+     */
+    static boolean separateServerThread = true;
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    static String passwd = "passphrase";
+    static String ROOT_ALIAS = "root";
+    static String INT_ALIAS = "intermediate";
+    static String SSL_ALIAS = "ssl";
+
+    /*
+     * Is the server ready to serve?
+     */
+    volatile static boolean serverReady = false;
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    // PKI components we will need for this test
+    static KeyStore rootKeystore;           // Root CA Keystore
+    static KeyStore intKeystore;            // Intermediate CA Keystore
+    static KeyStore serverKeystore;         // SSL Server Keystore
+    static KeyStore trustStore;             // SSL Client trust store
+    static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
+    static int rootOcspPort;                // Port number for root OCSP
+    static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
+    static int intOcspPort;                 // Port number for intermed. OCSP
+
+    /*
+     * If the client or server is doing some kind of object creation
+     * that the other side depends on, and that thread prematurely
+     * exits, you may experience a hang.  The test harness will
+     * terminate all hung threads after its timeout has expired,
+     * currently 3 minutes by default, but you might try to be
+     * smart about it....
+     */
+    public static void main(String[] args) throws Exception {
+        if (debug) {
+            System.setProperty("javax.net.debug", "ssl");
+        }
+
+        // Create the PKI we will use for the test and start the OCSP servers
+        createPKI();
+
+        testAllDefault();
+        testPKIXParametersRevEnabled();
+        testRevokedCertificate();
+        testHardFailFallback();
+        testSoftFailFallback();
+        testLatencyNoStaple(false);
+        testLatencyNoStaple(true);
+
+        // shut down the OCSP responders before finishing the test
+        intOcsp.stop();
+        rootOcsp.stop();
+    }
+
+    /**
+     * Default test using no externally-configured PKIXBuilderParameters
+     */
+    static void testAllDefault() throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+                new HashMap<>();
+
+        // We will prove revocation checking is disabled by marking the SSL
+        // certificate as revoked.  The test would only pass if revocation
+        // checking did not happen.
+        X509Certificate sslCert =
+                (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+        Date fiveMinsAgo = new Date(System.currentTimeMillis() -
+                TimeUnit.MINUTES.toMillis(5));
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,
+                        fiveMinsAgo));
+        intOcsp.updateStatusDb(revInfo);
+
+        System.out.println("=======================================");
+        System.out.println("Stapling enabled, default configuration");
+        System.out.println("=======================================");
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (tr.clientExc != null) {
+            throw tr.clientExc;
+        } else if (tr.serverExc != null) {
+            throw tr.serverExc;
+        }
+
+        // Return the ssl certificate to non-revoked status
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        System.out.println("                PASS");
+        System.out.println("=======================================\n");
+    }
+
+    /**
+     * Do a basic connection using PKIXParameters with revocation checking
+     * enabled and client-side OCSP disabled.  It will only pass if all
+     * stapled responses are present, valid and have a GOOD status.
+     */
+    static void testPKIXParametersRevEnabled() throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+
+        System.out.println("=====================================");
+        System.out.println("Stapling enabled, PKIXParameters with");
+        System.out.println("Revocation checking enabled ");
+        System.out.println("=====================================");
+
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+        Security.setProperty("ocsp.enable", "false");
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (tr.clientExc != null) {
+            throw tr.clientExc;
+        } else if (tr.serverExc != null) {
+            throw tr.serverExc;
+        }
+
+        System.out.println("                PASS");
+        System.out.println("=====================================\n");
+    }
+
+    /**
+     * Perform a test where the certificate is revoked and placed in the
+     * TLS handshake.  Client-side OCSP is disabled, so this test will only
+     * pass if the OCSP response is found, since we will check the
+     * CertPathValidatorException reason for revoked status.
+     */
+    static void testRevokedCertificate() throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+                new HashMap<>();
+
+        // We will prove revocation checking is disabled by marking the SSL
+        // certificate as revoked.  The test would only pass if revocation
+        // checking did not happen.
+        X509Certificate sslCert =
+                (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+        Date fiveMinsAgo = new Date(System.currentTimeMillis() -
+                TimeUnit.MINUTES.toMillis(5));
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,
+                        fiveMinsAgo));
+        intOcsp.updateStatusDb(revInfo);
+
+        System.out.println("=======================================");
+        System.out.println("Stapling enabled, default configuration");
+        System.out.println("=======================================");
+
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+        Security.setProperty("ocsp.enable", "false");
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (!checkClientValidationFailure(tr.clientExc, BasicReason.REVOKED)) {
+            if (tr.clientExc != null) {
+                throw tr.clientExc;
+            } else {
+                throw new RuntimeException(
+                        "Expected client failure, but the client succeeded");
+            }
+        }
+
+        // Return the ssl certificate to non-revoked status
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        System.out.println("                 PASS");
+        System.out.println("=======================================\n");
+    }
+
+    /**
+     * Test a case where client-side stapling is attempted, but does not
+     * occur because OCSP responders are unreachable.  This should use a
+     * default hard-fail behavior.
+     */
+    static void testHardFailFallback() throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+
+        // Stop the OCSP responders and give a 1 second delay before
+        // running the test.
+        intOcsp.stop();
+        rootOcsp.stop();
+        Thread.sleep(1000);
+
+        System.out.println("=======================================");
+        System.out.println("Stapling enbled in client and server,");
+        System.out.println("but OCSP responders disabled.");
+        System.out.println("PKIXParameters with Revocation checking");
+        System.out.println("enabled.");
+        System.out.println("=======================================");
+
+        Security.setProperty("ocsp.enable", "true");
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (!checkClientValidationFailure(tr.clientExc,
+                BasicReason.UNDETERMINED_REVOCATION_STATUS)) {
+            if (tr.clientExc != null) {
+                throw tr.clientExc;
+            } else {
+                throw new RuntimeException(
+                        "Expected client failure, but the client succeeded");
+            }
+        }
+
+        System.out.println("                 PASS");
+        System.out.println("=======================================\n");
+
+        // Start the OCSP responders up again
+        intOcsp.start();
+        rootOcsp.start();
+    }
+
+    /**
+     * Test a case where client-side stapling is attempted, but does not
+     * occur because OCSP responders are unreachable.  Client-side OCSP
+     * checking is enabled for this, with SOFT_FAIL.
+     */
+    static void testSoftFailFallback() throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+
+        // Stop the OCSP responders and give a 1 second delay before
+        // running the test.
+        intOcsp.stop();
+        rootOcsp.stop();
+        Thread.sleep(1000);
+
+        System.out.println("=======================================");
+        System.out.println("Stapling enbled in client and server,");
+        System.out.println("but OCSP responders disabled.");
+        System.out.println("PKIXParameters with Revocation checking");
+        System.out.println("enabled and SOFT_FAIL.");
+        System.out.println("=======================================");
+
+        Security.setProperty("ocsp.enable", "true");
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+        CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
+        cliParams.revChecker =
+                (PKIXRevocationChecker)cpv.getRevocationChecker();
+        cliParams.revChecker.setOptions(EnumSet.of(Option.SOFT_FAIL));
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (tr.clientExc != null) {
+            throw tr.clientExc;
+        } else if (tr.serverExc != null) {
+            throw tr.serverExc;
+        }
+
+        System.out.println("                 PASS");
+        System.out.println("=======================================\n");
+
+        // Start the OCSP responders up again
+        intOcsp.start();
+        rootOcsp.start();
+    }
+
+    /**
+     * This test initiates stapling from the client, but the server does not
+     * support OCSP stapling for this connection.  In this case it happens
+     * because the latency of the OCSP responders is longer than the server
+     * is willing to wait.  To keep the test streamlined, we will set the server
+     * latency to a 1 second wait, and set the responder latency to 3 seconds.
+     *
+     * @param fallback if we allow client-side OCSP fallback, which
+     * will change the result from the client failing with CPVE (no fallback)
+     * to a pass (fallback active).
+     */
+    static void testLatencyNoStaple(Boolean fallback) throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        serverReady = false;
+
+        // Stop the OCSP responders and give a 1 second delay before
+        // running the test.
+        intOcsp.stop();
+        rootOcsp.stop();
+        Thread.sleep(1000);
+        intOcsp.setDelay(3000);
+        rootOcsp.setDelay(3000);
+        rootOcsp.start();
+        intOcsp.start();
+        Thread.sleep(1000);
+
+        System.out.println("========================================");
+        System.out.println("Stapling enbled in client.  Server does");
+        System.out.println("not support stapling due to OCSP latency.");
+        System.out.println("PKIXParameters with Revocation checking");
+        System.out.println("enabled, client-side OCSP checking is.");
+        System.out.println(fallback ? "enabled" : "disabled");
+        System.out.println("========================================");
+
+        Security.setProperty("ocsp.enable", fallback.toString());
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+        servParams.respTimeout = 1000;
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+
+        if (fallback) {
+            if (tr.clientExc != null) {
+                throw tr.clientExc;
+            } else if (tr.serverExc != null) {
+                throw tr.serverExc;
+            }
+        } else {
+            if (!checkClientValidationFailure(tr.clientExc,
+                    BasicReason.UNDETERMINED_REVOCATION_STATUS)) {
+                if (tr.clientExc != null) {
+                    throw tr.clientExc;
+                } else {
+                    throw new RuntimeException(
+                        "Expected client failure, but the client succeeded");
+                }
+            }
+        }
+        System.out.println("                 PASS");
+        System.out.println("========================================\n");
+
+        // Remove the OCSP responder latency
+        intOcsp.stop();
+        rootOcsp.stop();
+        Thread.sleep(1000);
+        intOcsp.setDelay(0);
+        rootOcsp.setDelay(0);
+        rootOcsp.start();
+        intOcsp.start();
+    }
+
+    /*
+     * Define the server side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doServerSide(ServerParameters servParams) throws Exception {
+
+        // Selectively enable or disable the feature
+        System.setProperty("jdk.tls.server.enableStatusRequestExtension",
+                Boolean.toString(servParams.enabled));
+
+        // Set all the other operating parameters
+        System.setProperty("jdk.tls.stapling.cacheSize",
+                Integer.toString(servParams.cacheSize));
+        System.setProperty("jdk.tls.stapling.cacheLifetime",
+                Integer.toString(servParams.cacheLifetime));
+        System.setProperty("jdk.tls.stapling.responseTimeout",
+                Integer.toString(servParams.respTimeout));
+        System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri);
+        System.setProperty("jdk.tls.stapling.responderOverride",
+                Boolean.toString(servParams.respOverride));
+        System.setProperty("jdk.tls.stapling.ignoreExtensions",
+                Boolean.toString(servParams.ignoreExts));
+
+        // Set keystores and trust stores for the server
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(serverKeystore, passwd.toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(trustStore);
+
+        SSLContext sslc = SSLContext.getInstance("TLS");
+        sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        SSLServerSocketFactory sslssf = sslc.getServerSocketFactory();
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket) sslssf.createServerSocket(serverPort);
+
+        serverPort = sslServerSocket.getLocalPort();
+
+        /*
+         * Signal Client, we're ready for his connect.
+         */
+        serverReady = true;
+
+        try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+                InputStream sslIS = sslSocket.getInputStream();
+                OutputStream sslOS = sslSocket.getOutputStream()) {
+            int numberIn = sslIS.read();
+            int numberSent = 85;
+            log("Server received number: " + numberIn);
+            sslOS.write(numberSent);
+            sslOS.flush();
+            log("Server sent number: " + numberSent);
+        }
+    }
+
+    /*
+     * Define the client side of the test.
+     *
+     * If the server prematurely exits, serverReady will be set to true
+     * to avoid infinite hangs.
+     */
+    void doClientSide(ClientParameters cliParams) throws Exception {
+
+        /*
+         * Wait for server to get started.
+         */
+        while (!serverReady) {
+            Thread.sleep(50);
+        }
+
+        // Selectively enable or disable the feature
+        System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+                Boolean.toString(cliParams.enabled));
+
+        // Create the Trust Manager Factory using the PKIX variant
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+
+        // If we have a customized pkixParameters then use it
+        if (cliParams.pkixParams != null) {
+            // LIf we have a customized PKIXRevocationChecker, add
+            // it to the PKIXBuilderParameters.
+            if (cliParams.revChecker != null) {
+                cliParams.pkixParams.addCertPathChecker(cliParams.revChecker);
+            }
+
+            ManagerFactoryParameters trustParams =
+                    new CertPathTrustManagerParameters(cliParams.pkixParams);
+            tmf.init(trustParams);
+        } else {
+            tmf.init(trustStore);
+        }
+
+        SSLContext sslc = SSLContext.getInstance("TLS");
+        sslc.init(null, tmf.getTrustManagers(), null);
+
+        SSLSocketFactory sslsf = sslc.getSocketFactory();
+        try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost",
+                serverPort);
+                InputStream sslIS = sslSocket.getInputStream();
+                OutputStream sslOS = sslSocket.getOutputStream()) {
+            int numberSent = 80;
+            sslOS.write(numberSent);
+            sslOS.flush();
+            log("Client sent number: " + numberSent);
+            int numberIn = sslIS.read();
+            log("Client received number:" + numberIn);
+        }
+    }
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    SSLSocketWithStapling(ClientParameters cliParams,
+            ServerParameters servParams) throws Exception {
+        Exception startException = null;
+        try {
+            if (separateServerThread) {
+                startServer(servParams, true);
+                startClient(cliParams, false);
+            } else {
+                startClient(cliParams, true);
+                startServer(servParams, false);
+            }
+        } catch (Exception e) {
+            startException = e;
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            if (serverThread != null) {
+                serverThread.join();
+            }
+        } else {
+            if (clientThread != null) {
+                clientThread.join();
+            }
+        }
+    }
+
+    /**
+     * Checks a validation failure to see if it failed for the reason we think
+     * it should.  This comes in as an SSLException of some sort, but it
+     * encapsulates a ValidatorException which in turn encapsulates the
+     * CertPathValidatorException we are interested in.
+     *
+     * @param e the exception thrown at the top level
+     * @param reason the underlying CertPathValidatorException BasicReason
+     * we are expecting it to have.
+     *
+     * @return true if the reason matches up, false otherwise.
+     */
+    static boolean checkClientValidationFailure(Exception e,
+            BasicReason reason) {
+        boolean result = false;
+
+        if (e instanceof SSLException) {
+            Throwable valExc = e.getCause();
+            if (valExc instanceof sun.security.validator.ValidatorException) {
+                Throwable cause = valExc.getCause();
+                if (cause instanceof CertPathValidatorException) {
+                    CertPathValidatorException cpve =
+                            (CertPathValidatorException)cause;
+                    if (cpve.getReason() == reason) {
+                        result = true;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    TestResult getResult() {
+        TestResult tr = new TestResult();
+        tr.clientExc = clientException;
+        tr.serverExc = serverException;
+        return tr;
+    }
+
+    void startServer(ServerParameters servParams, boolean newThread)
+            throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide(servParams);
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide(servParams);
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(ClientParameters cliParams, boolean newThread)
+            throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide(cliParams);
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide(cliParams);
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+
+    /**
+     * Creates the PKI components necessary for this test, including
+     * Root CA, Intermediate CA and SSL server certificates, the keystores
+     * for each entity, a client trust store, and starts the OCSP responders.
+     */
+    private static void createPKI() throws Exception {
+        CertificateBuilder cbld = new CertificateBuilder();
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        KeyStore.Builder keyStoreBuilder =
+                KeyStore.Builder.newInstance("PKCS12", null,
+                        new KeyStore.PasswordProtection(passwd.toCharArray()));
+
+        // Generate Root, IntCA, EE keys
+        KeyPair rootCaKP = keyGen.genKeyPair();
+        log("Generated Root CA KeyPair");
+        KeyPair intCaKP = keyGen.genKeyPair();
+        log("Generated Intermediate CA KeyPair");
+        KeyPair sslKP = keyGen.genKeyPair();
+        log("Generated SSL Cert KeyPair");
+
+        // Set up the Root CA Cert
+        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
+        cbld.setPublicKey(rootCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("1"));
+        // Make a 3 year validity starting from 60 days ago
+        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+        long end = start + TimeUnit.DAYS.toMillis(1085);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        // Make our Root CA Cert!
+        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Root CA Created:\n" + certInfo(rootCert));
+
+        // Now build a keystore and add the keys and cert
+        rootKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] rootChain = {rootCert};
+        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
+                passwd.toCharArray(), rootChain);
+
+        // Now fire up the OCSP responder
+        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
+        rootOcsp.enableLog(debug);
+        rootOcsp.setNextUpdateInterval(3600);
+        rootOcsp.start();
+        Thread.sleep(1000);         // Give the server a second to start up
+        rootOcspPort = rootOcsp.getPort();
+        String rootRespURI = "http://localhost:" + rootOcspPort;
+        log("Root OCSP Responder URI is " + rootRespURI);
+
+        // Now that we have the root keystore and OCSP responder we can
+        // create our intermediate CA.
+        cbld.reset();
+        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
+        cbld.setPublicKey(intCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("100"));
+        // Make a 2 year validity starting from 30 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
+        end = start + TimeUnit.DAYS.toMillis(730);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        cbld.addAIAExt(Collections.singletonList(rootRespURI));
+        // Make our Intermediate CA Cert!
+        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Intermediate CA Created:\n" + certInfo(intCaCert));
+
+        // Provide intermediate CA cert revocation info to the Root CA
+        // OCSP responder.
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(intCaCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        rootOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        intKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] intChain = {intCaCert, rootCert};
+        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
+                passwd.toCharArray(), intChain);
+        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // Now fire up the Intermediate CA OCSP responder
+        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
+                INT_ALIAS, null);
+        intOcsp.enableLog(debug);
+        intOcsp.setNextUpdateInterval(3600);
+        intOcsp.start();
+        Thread.sleep(1000);
+        intOcspPort = intOcsp.getPort();
+        String intCaRespURI = "http://localhost:" + intOcspPort;
+        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
+
+        // Last but not least, let's make our SSLCert and add it to its own
+        // Keystore
+        cbld.reset();
+        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
+        cbld.setPublicKey(sslKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("4096"));
+        // Make a 1 year validity starting from 7 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
+        end = start + TimeUnit.DAYS.toMillis(365);
+        cbld.setValidity(new Date(start), new Date(end));
+
+        // Add extensions
+        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
+        boolean[] kuBits = {true, false, true, false, false, false,
+            false, false, false};
+        cbld.addKeyUsageExt(kuBits);
+        List<String> ekuOids = new ArrayList<>();
+        ekuOids.add("1.3.6.1.5.5.7.3.1");
+        ekuOids.add("1.3.6.1.5.5.7.3.2");
+        cbld.addExtendedKeyUsageExt(ekuOids);
+        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
+        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
+        // Make our SSL Server Cert!
+        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("SSL Certificate Created:\n" + certInfo(sslCert));
+
+        // Provide SSL server cert revocation info to the Intermeidate CA
+        // OCSP responder.
+        revInfo = new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        serverKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] sslChain = {sslCert, intCaCert, rootCert};
+        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
+                passwd.toCharArray(), sslChain);
+        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // And finally a Trust Store for the client
+        trustStore = keyStoreBuilder.getKeyStore();
+        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
+    }
+
+    private static void addCommonExts(CertificateBuilder cbld,
+            PublicKey subjKey, PublicKey authKey) throws IOException {
+        cbld.addSubjectKeyIdExt(subjKey);
+        cbld.addAuthorityKeyIdExt(authKey);
+    }
+
+    private static void addCommonCAExts(CertificateBuilder cbld)
+            throws IOException {
+        cbld.addBasicConstraintsExt(true, true, -1);
+        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
+        boolean[] kuBitSettings = {true, false, false, false, false, true,
+            true, false, false};
+        cbld.addKeyUsageExt(kuBitSettings);
+    }
+
+    /**
+     * Helper routine that dumps only a few cert fields rather than
+     * the whole toString() output.
+     *
+     * @param cert an X509Certificate to be displayed
+     *
+     * @return the String output of the issuer, subject and
+     * serial number
+     */
+    private static String certInfo(X509Certificate cert) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
+                append("\n");
+        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * Log a message on stdout
+     *
+     * @param message The message to log
+     */
+    private static void log(String message) {
+        if (debug) {
+            System.out.println(message);
+        }
+    }
+
+    // The following two classes are Simple nested class to group a handful
+    // of configuration parameters used before starting a client or server.
+    // We'll just access the data members directly for convenience.
+    static class ClientParameters {
+        boolean enabled = true;
+        PKIXBuilderParameters pkixParams = null;
+        PKIXRevocationChecker revChecker = null;
+
+        ClientParameters() { }
+    }
+
+    static class ServerParameters {
+        boolean enabled = true;
+        int cacheSize = 256;
+        int cacheLifetime = 3600;
+        int respTimeout = 5000;
+        String respUri = "";
+        boolean respOverride = false;
+        boolean ignoreExts = false;
+
+        ServerParameters() { }
+    }
+
+    static class TestResult {
+        Exception serverExc = null;
+        Exception clientExc = null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/provider/certpath/Extensions/OCSPNonceExtensionTests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary Unit tests for OCSPNonceExtension objects
+ */
+
+import java.security.cert.Extension;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.*;
+
+import sun.security.util.DerValue;
+import sun.security.util.DerInputStream;
+import sun.security.util.ObjectIdentifier;
+import sun.security.provider.certpath.OCSPNonceExtension;
+import sun.security.x509.PKIXExtensions;
+
+public class OCSPNonceExtensionTests {
+    public static final boolean DEBUG = true;
+    public static final String OCSP_NONCE_OID = "1.3.6.1.5.5.7.48.1.2";
+    public static final String ELEMENT_NONCE = "nonce";
+    public static final String EXT_NAME = "OCSPNonce";
+
+    // DER encoding for OCSP nonce extension:
+    // OID = 1.3.6.1.5.5.7.48.1.2
+    // Critical = true
+    // 48 bytes of 0xDEADBEEF
+    public static final byte[] OCSP_NONCE_DER = {
+          48,   66,    6,    9,   43,    6,    1,    5,
+           5,    7,   48,    1,    2,    1,    1,   -1,
+           4,   50,    4,   48,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,
+    };
+
+    // 16 bytes of 0xDEADBEEF
+    public static final byte[] DEADBEEF_16 = {
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+         -34,  -83,  -66,  -17,  -34,  -83,  -66,  -17,
+    };
+
+    // DER encoded extension using 16 bytes of DEADBEEF
+    public static final byte[] OCSP_NONCE_DB16 = {
+          48,   31,    6,    9,   43,    6,    1,    5,
+           5,    7,   48,    1,    2,    4,   18,    4,
+          16,  -34,  -83,  -66,  -17,  -34,  -83,  -66,
+         -17,  -34,  -83,  -66,  -17,  -34,  -83,  -66,
+         -17
+    };
+
+    public static void main(String [] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("CTOR Test (provide length)", testCtorByLength);
+            put("CTOR Test (provide extension DER encoding)",
+                    testCtorSuperByDerValue);
+            put("Use set() call to provide random data", testResetValue);
+            put("Test get() method", testGet);
+            put("test set() method", testSet);
+            put("Test getElements() method", testGetElements);
+            put("Test getName() method", testGetName);
+            put("Test delete() method", testDelete);
+        }};
+
+        System.out.println("============ Tests ============");
+        int testNo = 0;
+        int numberFailed = 0;
+        Map.Entry<Boolean, String> result;
+        for (String testName : testList.keySet()) {
+            System.out.println("Test " + ++testNo + ": " + testName);
+            result = testList.get(testName).runTest();
+            System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL"));
+            System.out.println(" " +
+                    (result.getValue() != null ? result.getValue() : ""));
+            System.out.println("-------------------------------------------");
+            if (!result.getKey()) {
+                numberFailed++;
+            }
+        }
+        System.out.println("End Results: " + (testList.size() - numberFailed) +
+                " Passed" + ", " + numberFailed + " Failed.");
+        if (numberFailed > 0) {
+            throw new RuntimeException(
+                    "One or more tests failed, see test output for details");
+        }
+    }
+
+    private static void dumpHexBytes(byte[] data) {
+        if (data != null) {
+            for (int i = 0; i < data.length; i++) {
+                if (i % 16 == 0 && i != 0) {
+                    System.out.print("\n");
+                }
+                System.out.print(String.format("%02X ", data[i]));
+            }
+            System.out.print("\n");
+        }
+    }
+
+    private static void debuglog(String message) {
+        if (DEBUG) {
+            System.out.println(message);
+        }
+    }
+
+    public static void verifyExtStructure(byte[] derData) throws IOException {
+        debuglog("verifyASN1Extension() received " + derData.length + " bytes");
+        DerInputStream dis = new DerInputStream(derData);
+
+        // The sequenceItems array should be either two or three elements
+        // long.  If three, then the criticality bit setting has been asserted.
+        DerValue[] sequenceItems = dis.getSequence(3);
+        debuglog("Found sequence containing " + sequenceItems.length +
+                " elements");
+        if (sequenceItems.length != 2 && sequenceItems.length != 3) {
+            throw new RuntimeException("Incorrect number of items found in " +
+                    "the SEQUENCE (Got " + sequenceItems.length +
+                    ", expected 2 or 3 items)");
+        }
+
+        int seqIndex = 0;
+        ObjectIdentifier extOid = sequenceItems[seqIndex++].getOID();
+        debuglog("Found OID: " + extOid.toString());
+        if (!extOid.equals((Object)PKIXExtensions.OCSPNonce_Id)) {
+            throw new RuntimeException("Incorrect OID (Got " +
+                    extOid.toString() + ", expected " +
+                    PKIXExtensions.OCSPNonce_Id.toString() + ")");
+        }
+
+        if (sequenceItems.length == 3) {
+            // Non-default criticality bit setting should be at index 1
+            boolean isCrit = sequenceItems[seqIndex++].getBoolean();
+            debuglog("Found BOOLEAN (critical): " + isCrit);
+        }
+
+        // The extnValue is an encapsulating OCTET STRING that contains the
+        // extension's value.  For the OCSP Nonce, that value itself is also
+        // an OCTET STRING consisting of the random bytes.
+        DerValue extnValue =
+                new DerValue(sequenceItems[seqIndex++].getOctetString());
+        byte[] nonceData = extnValue.getOctetString();
+        debuglog("Found " + nonceData.length + " bytes of nonce data");
+    }
+
+    public interface TestCase {
+        Map.Entry<Boolean, String> runTest();
+    }
+
+    public static final TestCase testCtorByLength = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+                Extension nonceByLen = new OCSPNonceExtension(32);
+
+                // Verify overall encoded extension structure
+                nonceByLen.encode(baos);
+                verifyExtStructure(baos.toByteArray());
+
+                // Verify the name, elements, and data conform to
+                // expected values for this specific object.
+                boolean crit = nonceByLen.isCritical();
+                String oid = nonceByLen.getId();
+                DerValue nonceData = new DerValue(nonceByLen.getValue());
+
+                if (crit) {
+                    message = "Extension incorrectly marked critical";
+                } else if (!oid.equals(OCSP_NONCE_OID)) {
+                    message = "Incorrect OID (Got " + oid + ", Expected " +
+                            OCSP_NONCE_OID + ")";
+                } else if (nonceData.getTag() != DerValue.tag_OctetString) {
+                    message = "Incorrect nonce data tag type (Got " +
+                            String.format("0x%02X", nonceData.getTag()) +
+                            ", Expected 0x04)";
+                } else if (nonceData.getOctetString().length != 32) {
+                    message = "Incorrect nonce byte length (Got " +
+                            nonceData.getOctetString().length +
+                            ", Expected 32)";
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testCtorSuperByDerValue = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+                Extension nonceByDer = new sun.security.x509.Extension(
+                        new DerValue(OCSP_NONCE_DER));
+
+                // Verify overall encoded extension structure
+                nonceByDer.encode(baos);
+                verifyExtStructure(baos.toByteArray());
+
+                // Verify the name, elements, and data conform to
+                // expected values for this specific object.
+                boolean crit = nonceByDer.isCritical();
+                String oid = nonceByDer.getId();
+                DerValue nonceData = new DerValue(nonceByDer.getValue());
+
+                if (!crit) {
+                    message = "Extension lacks expected criticality setting";
+                } else if (!oid.equals(OCSP_NONCE_OID)) {
+                    message = "Incorrect OID (Got " + oid + ", Expected " +
+                            OCSP_NONCE_OID + ")";
+                } else if (nonceData.getTag() != DerValue.tag_OctetString) {
+                    message = "Incorrect nonce data tag type (Got " +
+                            String.format("0x%02X", nonceData.getTag()) +
+                            ", Expected 0x04)";
+                } else if (nonceData.getOctetString().length != 48) {
+                    message = "Incorrect nonce byte length (Got " +
+                            nonceData.getOctetString().length +
+                            ", Expected 48)";
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testResetValue = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+                OCSPNonceExtension nonce = new OCSPNonceExtension(32);
+
+                // Reset the nonce data to reflect 16 bytes of DEADBEEF
+                nonce.set(OCSPNonceExtension.NONCE, (Object)DEADBEEF_16);
+
+                // Verify overall encoded extension content
+                nonce.encode(baos);
+                dumpHexBytes(OCSP_NONCE_DB16);
+                System.out.println();
+                dumpHexBytes(baos.toByteArray());
+
+                pass = Arrays.equals(baos.toByteArray(), OCSP_NONCE_DB16);
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testSet = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32);
+
+                // Set the nonce data to 16 bytes of DEADBEEF
+                nonceByLen.set(ELEMENT_NONCE, DEADBEEF_16);
+                byte[] nonceData = (byte[])nonceByLen.get(ELEMENT_NONCE);
+                if (!Arrays.equals(nonceData, DEADBEEF_16)) {
+                    throw new RuntimeException("Retuned nonce data does not " +
+                            "match expected result");
+                }
+
+                // Now try to set a value using an object that is not a byte
+                // array
+                int[] INT_DB_16 = {
+                    0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF
+                };
+                try {
+                    nonceByLen.set(ELEMENT_NONCE, INT_DB_16);
+                    throw new RuntimeException("Accepted get() for " +
+                            "unsupported element name");
+                } catch (IOException ioe) { }     // Expected result
+
+                // And try setting a value using an unknown element name
+                try {
+                    nonceByLen.set("FOO", DEADBEEF_16);
+                    throw new RuntimeException("Accepted get() for " +
+                            "unsupported element name");
+                } catch (IOException ioe) { }     // Expected result
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+        public static final TestCase testGet = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32);
+
+                // Grab the nonce data by its correct element name
+                byte[] nonceData = (byte[])nonceByLen.get(ELEMENT_NONCE);
+                if (nonceData == null || nonceData.length != 32) {
+                    throw new RuntimeException("Unexpected return value from " +
+                            "get() method: either null or incorrect length");
+                }
+
+                // Now try to get any kind of data using an element name that
+                // doesn't exist for this extension.
+                try {
+                    nonceByLen.get("FOO");
+                    throw new RuntimeException("Accepted get() for " +
+                            "unsupported element name");
+                } catch (IOException ioe) { }     // Expected result
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testGetElements = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32);
+
+                int elementCount = 0;
+                boolean foundElement = false;
+
+                // There should be exactly one element and its name should
+                // be "nonce"
+                for (Enumeration<String> elements = nonceByLen.getElements();
+                        elements.hasMoreElements(); elementCount++) {
+                    if (elements.nextElement().equals(ELEMENT_NONCE)) {
+                        foundElement = true;
+                    }
+                }
+
+                if (!foundElement || elementCount != 1) {
+                    throw new RuntimeException("Unexpected or missing " +
+                            "Enumeration element");
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testGetName = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32);
+                pass = new Boolean(nonceByLen.getName().equals(EXT_NAME));
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testDelete = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32);
+
+                // First verify that there's data to begin with
+                byte[] nonceData = (byte[])nonceByLen.get(ELEMENT_NONCE);
+                if (nonceData == null || nonceData.length != 32) {
+                    throw new RuntimeException("Unexpected return value from " +
+                            "get() method: either null or incorrect length");
+                }
+
+                // Attempt to delete using an element name that doesn't exist
+                // for this extension.
+                try {
+                    nonceByLen.delete("FOO");
+                    throw new RuntimeException("Accepted delete() for " +
+                            "unsupported element name");
+                } catch (IOException ioe) { }     // Expected result
+
+                // Now attempt to properly delete the extension data
+                nonceByLen.delete(ELEMENT_NONCE);
+                nonceData = (byte[])nonceByLen.get(ELEMENT_NONCE);
+                if (nonceData != null) {
+                    throw new RuntimeException("Unexpected non-null return");
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/provider/certpath/ResponderId/ResponderIdTests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS (ResponderId tests)
+ */
+
+import java.io.*;
+import java.security.cert.*;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import javax.security.auth.x500.X500Principal;
+import sun.security.x509.KeyIdentifier;
+import sun.security.provider.certpath.ResponderId;
+
+/*
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class ResponderIdTests {
+
+    private static final boolean debug = true;
+
+    // Source certificate created with the following command:
+    // keytool -genkeypair -alias test1 -keyalg rsa -keysize 2048 \
+    //   -validity 7300 -keystore test1.jks \
+    //   -dname "CN=SelfSignedResponder, OU=Validation Services, O=FakeCompany"
+    private static final String RESP_CERT_1 =
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIIDQzCCAiugAwIBAgIEXTqCCjANBgkqhkiG9w0BAQsFADBSMRQwEgYDVQQKEwtG\n" +
+        "YWtlQ29tcGFueTEcMBoGA1UECxMTVmFsaWRhdGlvbiBTZXJ2aWNlczEcMBoGA1UE\n" +
+        "AxMTU2VsZlNpZ25lZFJlc3BvbmRlcjAeFw0xNDA4MTcwNDM2MzBaFw0zNDA4MTIw\n" +
+        "NDM2MzBaMFIxFDASBgNVBAoTC0Zha2VDb21wYW55MRwwGgYDVQQLExNWYWxpZGF0\n" +
+        "aW9uIFNlcnZpY2VzMRwwGgYDVQQDExNTZWxmU2lnbmVkUmVzcG9uZGVyMIIBIjAN\n" +
+        "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt2Cmw2k9tviLxaxE8aWNuoosWKL\n" +
+        "h+K4mNcDGKSoiChsqRqeJEnOxijDZqyFwfkaXvpAduFqYjz+Lij2HumvAjHDTui6\n" +
+        "bGcbsndRDPjvVo1S7f1oWsg7oiA8Lzmjl452S7UNBsDX5Dt1e84Xxwi40B1J2y8D\n" +
+        "FRPfYRWRlC1Z4kzqkBBa7JhANS+W8KDstFZxL4AwWH/byNwB5dl2j04ohg/Ar54e\n" +
+        "mu08PIH3hmi0pAu5wn9ariA7UA5lFWRJzvgGXV5J+QVEFuvKmeJ/Q6tU5OBJGw98\n" +
+        "zjd7F5B0iE+rJHTNF1aGaQfIorz04onV2WjH2VZA18AaMwqlY2br1SBdTQIDAQAB\n" +
+        "oyEwHzAdBgNVHQ4EFgQUG09HasSTYaTIh/CxxV/rcJV1LvowDQYJKoZIhvcNAQEL\n" +
+        "BQADggEBAIcUomNpZxGkocIzzybLyeyC6vLF1k0/unuPAHZLDP3o2JTstPhLHOCg\n" +
+        "FYw1VG2i23pjwKK2x/o80tJAOmW6vowbAPnNmtNIYO3gB/ZGiKeORoGKBCRDNvFa\n" +
+        "6ZrWxwTzT3EpVwRe7ameES0uP8+S4q2P5LhwMIMw7vGHoOQJgkAh/NUiCli1qRnJ\n" +
+        "FYd6cHMJJK5gF2FqQ7tdbA26pS06bkIEvil2M5wyKKWOydOa/pr1LgMf9KxljJ8J\n" +
+        "XlAOO/mGZGkYmWnQaQuBIDyWunWYlhsyCXMa8AScgs0uUeQp19tO7R0f03q/JXoZ\n" +
+        "1At1gZiMS7SdQaRWP5q+FunAeFWjsFE=\n" +
+        "-----END CERTIFICATE-----";
+
+    private static final String RESP_CERT_1_SUBJ =
+        "CN=SelfSignedResponder, OU=Validation Services, O=FakeCompany";
+
+    private static X509Certificate cert = null;
+
+    // The expected DER-encoding for a byName ResponderId derived
+    // from RESP_CERT_1
+    private static final byte[] EXP_NAME_ID_BYTES = {
+        -95,   84,   48,   82,   49,   20,   48,   18,
+          6,    3,   85,    4,   10,   19,   11,   70,
+         97,  107,  101,   67,  111,  109,  112,   97,
+        110,  121,   49,   28,   48,   26,    6,    3,
+         85,    4,   11,   19,   19,   86,   97,  108,
+        105,  100,   97,  116,  105,  111,  110,   32,
+         83,  101,  114,  118,  105,   99,  101,  115,
+         49,   28,   48,   26,    6,    3,   85,    4,
+          3,   19,   19,   83,  101,  108,  102,   83,
+        105,  103,  110,  101,  100,   82,  101,  115,
+        112,  111,  110,  100,  101,  114
+    };
+
+    // The expected DER-encoding for a byKey ResponderId derived
+    // from RESP_CERT_1
+    private static final byte[] EXP_KEY_ID_BYTES = {
+         -94,   22,    4,   20,   27,   79,   71,  106,
+         -60, -109,   97,  -92,  -56, -121,  -16,  -79,
+         -59,   95,  -21,  112, -107,  117,   46,   -6
+    };
+
+    // The DER encoding of a byKey ResponderId, but using an
+    // incorrect explicit tagging (CONTEXT CONSTRUCTED 3)
+    private static final byte[] INV_EXPLICIT_TAG_KEY_ID = {
+         -93,   22,    4,   20,   27,   79,   71,  106,
+         -60, -109,   97,  -92,  -56, -121,  -16,  -79,
+         -59,   95,  -21,  112, -107,  117,   46,   -6
+    };
+
+    // These two ResponderId objects will have objects attached to them
+    // after the pos_CtorByName and pos_CtorByKeyId tests run.  Those
+    // two tests should always be the first two that run.
+    public static ResponderId respByName;
+    public static ResponderId respByKeyId;
+
+    public static void main(String[] args) throws Exception {
+        List<TestCase> testList = new ArrayList<>();
+
+        testList.add(pos_CtorByName);
+        testList.add(pos_CtorByKeyId);
+        testList.add(pos_CtorByEncoding);
+        testList.add(neg_CtorByEncoding);
+        testList.add(pos_Equality);
+        testList.add(pos_GetEncoded);
+        testList.add(pos_GetRespName);
+        testList.add(pos_GetRespKeyId);
+
+        // Load the certificate object we can use for subsequent tests
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        cert = (X509Certificate)cf.generateCertificate(
+                new ByteArrayInputStream(RESP_CERT_1.getBytes()));
+
+        System.out.println("============ Tests ============");
+        int testNo = 0;
+        int numberFailed = 0;
+        Map.Entry<Boolean, String> result;
+        for (TestCase test : testList) {
+            System.out.println("Test " + ++testNo + ": " + test.getName());
+            result = test.runTest();
+            System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL"));
+            System.out.println(" " +
+                    (result.getValue() != null ? result.getValue() : ""));
+            System.out.println("-------------------------------------------");
+            if (!result.getKey()) {
+                numberFailed++;
+            }
+        }
+        System.out.println("End Results: " + (testList.size() - numberFailed) +
+                " Passed" + ", " + numberFailed + " Failed.");
+        if (numberFailed > 0) {
+            throw new RuntimeException(
+                    "One or more tests failed, see test output for details");
+        }
+    }
+
+    private static void dumpHexBytes(byte[] data) {
+        if (data != null) {
+            for (int i = 0; i < data.length; i++) {
+                if (i % 16 == 0 && i != 0) {
+                    System.out.print("\n");
+                }
+                System.out.print(String.format("%02X ", data[i]));
+            }
+            System.out.print("\n");
+        }
+    }
+
+    public interface TestCase {
+        String getName();
+        Map.Entry<Boolean, String> runTest();
+    }
+
+    public static final TestCase pos_CtorByName = new TestCase() {
+        @Override
+        public String getName() {
+            return "CTOR Test (by-name)";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                respByName = new ResponderId(cert.getSubjectX500Principal());
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase pos_CtorByKeyId = new TestCase() {
+        @Override
+        public String getName() {
+            return "CTOR Test (by-keyID)";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                respByKeyId = new ResponderId(cert.getPublicKey());
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase pos_CtorByEncoding = new TestCase() {
+        @Override
+        public String getName() {
+            return "CTOR Test (encoded bytes)";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                ResponderId ridByNameBytes = new ResponderId(EXP_NAME_ID_BYTES);
+                ResponderId ridByKeyIdBytes = new ResponderId(EXP_KEY_ID_BYTES);
+
+                if (!ridByNameBytes.equals(respByName)) {
+                    throw new RuntimeException(
+                            "Equals failed: respNameFromBytes vs. respByName");
+                } else if (!ridByKeyIdBytes.equals(respByKeyId)) {
+                    throw new RuntimeException(
+                            "Equals failed: respKeyFromBytes vs. respByKeyId");
+                }
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase neg_CtorByEncoding = new TestCase() {
+        @Override
+        public String getName() {
+            return "CTOR Test (by encoding, unknown explicit tag)";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                ResponderId ridByKeyIdBytes =
+                        new ResponderId(INV_EXPLICIT_TAG_KEY_ID);
+                throw new RuntimeException("Expected IOException not thrown");
+            } catch (IOException ioe) {
+                // Make sure it's the IOException we're looking for
+                if (ioe.getMessage().contains("Invalid ResponderId content")) {
+                    pass = Boolean.TRUE;
+                } else {
+                    ioe.printStackTrace(System.out);
+                    message = ioe.getClass().getName();
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+
+    public static final TestCase pos_Equality = new TestCase() {
+        @Override
+        public String getName() {
+            return "Simple Equality Test";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+
+            try {
+                // byName ResponderId equality test
+                ResponderId compName =
+                    new ResponderId(new X500Principal(RESP_CERT_1_SUBJ));
+                if (!respByName.equals(compName)) {
+                    message = "ResponderId mismatch in byName comparison";
+                } else if (respByKeyId.equals(compName)) {
+                    message = "Invalid ResponderId match in byKeyId comparison";
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase pos_GetEncoded = new TestCase() {
+        @Override
+        public String getName() {
+            return "Get Encoded Value";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+
+            try {
+                // Pull out byName and byKey encodings, they should match
+                // the expected values
+                if (!Arrays.equals(respByName.getEncoded(), EXP_NAME_ID_BYTES)) {
+                    message = "ResponderId byName encoding did not " +
+                            "match expected value";
+                } else if (!Arrays.equals(respByKeyId.getEncoded(), EXP_KEY_ID_BYTES)) {
+                    message = "ResponderId byKeyId encoding did not " +
+                            "match expected value";
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase pos_GetRespName = new TestCase() {
+        @Override
+        public String getName() {
+            return "Get Underlying Responder Name";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+
+            try {
+                // Test methods for pulling out the underlying
+                // X500Principal object
+                X500Principal testPrincipal =
+                        new X500Principal(RESP_CERT_1_SUBJ);
+                if (!respByName.getResponderName().equals(testPrincipal)) {
+                    message = "ResponderId Name did not match expected value";
+                } else if (respByKeyId.getResponderName() != null) {
+                    message = "Non-null responder name returned from " +
+                            "ResponderId constructed byKey";
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase pos_GetRespKeyId = new TestCase() {
+        @Override
+        public String getName() {
+            return "Get Underlying Responder Key ID";
+        }
+
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+
+            try {
+                // Test methods for pulling out the underlying
+                // KeyIdentifier object.  Note: There is a minute chance that
+                // an RSA public key, once hashed into a key ID might collide
+                // with the one extracted from the certificate used to create
+                // respByKeyId.  This is so unlikely to happen it is considered
+                // virtually impossible.
+                KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+                kpg.initialize(2048);
+                KeyPair rsaKey = kpg.generateKeyPair();
+                KeyIdentifier testKeyId = new KeyIdentifier(rsaKey.getPublic());
+
+                if (respByKeyId.getKeyIdentifier().equals(testKeyId)) {
+                    message = "Unexpected match in ResponderId Key ID";
+                } else if (respByName.getKeyIdentifier() != null) {
+                    message = "Non-null key ID returned from " +
+                            "ResponderId constructed byName";
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+}
--- a/jdk/test/sun/security/ssl/ExtensionType/OptimalListSize.java	Wed Aug 05 11:06:49 2015 -0700
+++ b/jdk/test/sun/security/ssl/ExtensionType/OptimalListSize.java	Wed Aug 05 12:19:38 2015 -0700
@@ -36,6 +36,6 @@
     public static void main(String[] args) throws Throwable {
         OptimalCapacity.ofArrayList(
                 Class.forName("sun.security.ssl.ExtensionType"),
-                "knownExtensions", 13);
+                "knownExtensions", 14);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/BogusStatusRequest.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+
+final class BogusStatusRequest implements StatusRequest {
+    BogusStatusRequest() { }
+
+    @Override
+    public int length() { return 0; }
+
+    @Override
+    public void send(HandshakeOutStream s) throws IOException { }
+
+    @Override
+    public String toString() {
+        return "BogusStatusRequest";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/CertStatusReqExtensionTests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS (CertStatusReqExtension tests)
+ * @build CertStatusReqExtensionTests BogusStatusRequest TestCase TestUtils
+ * @run main/othervm sun.security.ssl.CertStatusReqExtensionTests
+ */
+
+import java.io.IOException;
+import java.util.*;
+import java.nio.ByteBuffer;
+
+/*
+ * Checks that the hash value for a certificate's issuer name is generated
+ * correctly. Requires any certificate that is not self-signed.
+ *
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class CertStatusReqExtensionTests {
+
+    private static final boolean debug = false;
+
+    // Default status_request extension (type = ocsp, OCSPStatusRequest
+    // with no responder IDs or extensions
+    private static final byte[] CSRE_DEF_OSR = {1, 0, 0, 0, 0};
+
+    // A status_request extension using a user-defined type (0xFF) and
+    // an underlying no-Responder ID/no-extension OCSPStatusRequest
+    private static final byte[] CSRE_TYPE_FF = {-1, 0, 0, 0, 0};
+
+    // A CertStatusReqExtension with 5 ResponderIds and 1 Extension
+    private static final byte[] CSRE_REQ_RID_EXTS = {
+           1,    0,  -13,    0,   59,  -95,   57,   48,
+          55,   49,   16,   48,   14,    6,    3,   85,
+           4,   10,   19,    7,   83,  111,  109,  101,
+          73,  110,   99,   49,   16,   48,   14,    6,
+           3,   85,    4,   11,   19,    7,   83,  111,
+         109,  101,   80,   75,   73,   49,   17,   48,
+          15,    6,    3,   85,    4,    3,   19,    8,
+          83,  111,  109,  101,   79,   67,   83,   80,
+           0,   68,  -95,   66,   48,   64,   49,   13,
+          48,   11,    6,    3,   85,    4,   10,   19,
+           4,   79,  104,   77,  121,   49,   14,   48,
+          12,    6,    3,   85,    4,   11,   19,    5,
+          66,  101,   97,  114,  115,   49,   15,   48,
+          13,    6,    3,   85,    4,   11,   19,    6,
+          84,  105,  103,  101,  114,  115,   49,   14,
+          48,   12,    6,    3,   85,    4,    3,   19,
+           5,   76,  105,  111,  110,  115,    0,   58,
+         -95,   56,   48,   54,   49,   16,   48,   14,
+           6,    3,   85,    4,   10,   19,    7,   67,
+         111,  109,  112,   97,  110,  121,   49,   13,
+          48,   11,    6,    3,   85,    4,   11,   19,
+           4,   87,  101,  115,  116,   49,   19,   48,
+          17,    6,    3,   85,    4,    3,   19,   10,
+          82,  101,  115,  112,  111,  110,  100,  101,
+         114,   49,    0,   24,  -94,   22,    4,   20,
+         -67,  -36,  114,  121,   92,  -79,  116,   -1,
+         102, -107,    7,  -21,   18, -113,   64,   76,
+          96,   -7,  -66,  -63,    0,   24,  -94,   22,
+           4,   20,  -51,  -69,  107,  -82,  -39,  -87,
+          45,   25,   41,   28,  -76,  -68,  -11, -110,
+         -94,  -97,   62,   47,   58, -125,    0,   51,
+          48,   49,   48,   47,    6,    9,   43,    6,
+           1,    5,    5,    7,   48,    1,    2,    4,
+          34,    4,   32,  -26,  -81, -120,  -61, -127,
+         -79,    0,  -39,  -54,   49,    3,  -51,  -57,
+         -85,   19, -126,   94,   -2,   21,   26,   98,
+           6,  105,  -35,  -37,  -29,  -73,  101,   53,
+          44,   15,  -19
+    };
+
+    public static void main(String[] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("CTOR (default)", testCtorDefault);
+            put("CTOR (int, StatusRequest)", testCtorStatReqs);
+            put("CTOR (HandshakeInStream, length, getReqType, getRequest)",
+                    testCtorInStream);
+        }};
+
+        TestUtils.runTests(testList);
+    }
+
+    public static final TestCase testCtorDefault = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                CertStatusReqExtension csreDef = new CertStatusReqExtension();
+                HandshakeOutStream hsout =
+                        new HandshakeOutStream(null);
+                csreDef.send(hsout);
+                TestUtils.valueCheck(wrapExtData(null), hsout.toByteArray());
+
+                // The length should be 4 (2 bytes for the type, 2 for the
+                // encoding of zero-length
+                if (csreDef.length() != 4) {
+                    throw new RuntimeException("Incorrect length from " +
+                            "default object.  Expected 4, got " +
+                            csreDef.length());
+                }
+
+                // Since there's no data, there are no status_type or request
+                // data fields defined.  Both should return null in this case
+                if (csreDef.getType() != null) {
+                    throw new RuntimeException("Default CSRE returned " +
+                            "non-null status_type");
+                } else if (csreDef.getRequest() != null) {
+                    throw new RuntimeException("Default CSRE returned " +
+                            "non-null request object");
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testCtorStatReqs = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                HandshakeOutStream hsout =
+                        new HandshakeOutStream(null);
+                StatusRequest basicStatReq = new OCSPStatusRequest();
+
+                // Create an extension using a default-style OCSPStatusRequest
+                // (no responder IDs, no extensions).
+                CertStatusReqExtension csre1 = new CertStatusReqExtension(
+                        StatusRequestType.OCSP, basicStatReq);
+                csre1.send(hsout);
+                TestUtils.valueCheck(wrapExtData(CSRE_DEF_OSR),
+                        hsout.toByteArray());
+                hsout.reset();
+
+                // Create the extension using a StatusRequestType not already
+                // instantiated as a static StatusRequestType
+                // (e.g. OCSP/OCSP_MULTI)
+                CertStatusReqExtension csre2 =
+                        new CertStatusReqExtension(StatusRequestType.get(-1),
+                                basicStatReq);
+                csre2.send(hsout);
+                TestUtils.valueCheck(wrapExtData(CSRE_TYPE_FF),
+                        hsout.toByteArray());
+
+                // Create the extension using a StatusRequest that
+                // does not match the status_type field
+                // This should throw an IllegalArgumentException
+                try {
+                    CertStatusReqExtension csreBadRequest =
+                            new CertStatusReqExtension(StatusRequestType.OCSP,
+                                    new BogusStatusRequest());
+                    throw new RuntimeException("Constructor accepted a " +
+                            "StatusRequest that is inconsistent with " +
+                            "the status_type");
+                } catch (IllegalArgumentException iae) { }
+
+                // We don't allow a null value for the StatusRequestType
+                // parameter in this constructor.
+                try {
+                    CertStatusReqExtension csreBadRequest =
+                            new CertStatusReqExtension(null, basicStatReq);
+                    throw new RuntimeException("Constructor accepted a " +
+                            "null StatusRequestType");
+                } catch (NullPointerException npe) { }
+
+                // We also don't allow a null value for the StatusRequest
+                // parameter in this constructor.
+                try {
+                    CertStatusReqExtension csreBadRequest =
+                            new CertStatusReqExtension(StatusRequestType.OCSP,
+                                    null);
+                    throw new RuntimeException("Constructor accepted a " +
+                            "null StatusRequest");
+                } catch (NullPointerException npe) { }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor that builds the ob ject using data from
+    // a HandshakeInStream
+    // This also tests the length, getReqType and getRequest methods
+    public static final TestCase testCtorInStream = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            OCSPStatusRequest osr;
+
+            try {
+                // To simulate the extension coming in a ServerHello, the
+                // type and length would already be read by HelloExtensions
+                // and there is no extension data
+                HandshakeInStream hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(new byte[0]));
+                CertStatusReqExtension csre =
+                        new CertStatusReqExtension(hsis, hsis.available());
+                // Verify length/type/request
+                if (csre.length() != 4) {
+                     throw new RuntimeException("Invalid length: received " +
+                            csre.length() + ", expected 4");
+                } else if (csre.getType() != null) {
+                    throw new RuntimeException("Non-null type from default " +
+                            "extension");
+                } else if (csre.getRequest() != null) {
+                    throw new RuntimeException("Non-null request from default " +
+                            "extension");
+                }
+
+                // Try the an extension with a default OCSPStatusRequest
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(CSRE_DEF_OSR));
+                csre = new CertStatusReqExtension(hsis, hsis.available());
+                if (csre.length() != (CSRE_DEF_OSR.length + 4)) {
+                    throw new RuntimeException("Invalid length: received " +
+                            csre.length() + ", expected " +
+                            CSRE_DEF_OSR.length + 4);
+                } else if (!csre.getType().equals(StatusRequestType.OCSP)) {
+                    throw new RuntimeException("Unknown status_type: " +
+                            String.format("0x%02X", csre.getType().id));
+                } else {
+                    osr = (OCSPStatusRequest)csre.getRequest();
+                    if (!osr.getResponderIds().isEmpty() ||
+                            !osr.getExtensions().isEmpty()) {
+                        throw new RuntimeException("Non-default " +
+                                "OCSPStatusRequest found in extension");
+                    }
+                }
+
+                // Try with a non-default extension
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(CSRE_REQ_RID_EXTS));
+                csre = new CertStatusReqExtension(hsis, hsis.available());
+                if (csre.length() != (CSRE_REQ_RID_EXTS.length + 4)) {
+                    throw new RuntimeException("Invalid length: received " +
+                            csre.length() + ", expected " +
+                            CSRE_REQ_RID_EXTS.length + 4);
+                } else if (!(csre.getType().equals(StatusRequestType.OCSP))) {
+                    throw new RuntimeException("Unknown status_type: " +
+                            String.format("0x%02X", csre.getType().id));
+                } else {
+                    osr = (OCSPStatusRequest)csre.getRequest();
+                    if (osr.getResponderIds().size() != 5 ||
+                            osr.getExtensions().size() != 1) {
+                        throw new RuntimeException("Incorrect number of " +
+                                "ResponderIds or Extensions found in " +
+                                "OCSPStatusRequest");
+                    }
+                }
+
+                // Create a CSRE that asserts status_request and has the
+                // proper length, but really is a bunch of random junk inside
+                // In this case, it will create an UnknownStatusRequest to
+                // handle the unparseable data.
+                byte[] junkData = new byte[48];
+                Random r = new Random(System.currentTimeMillis());
+                r.nextBytes(junkData);
+                junkData[0] = 7;        // Ensure it isn't a valid status_type
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(junkData));
+                csre = new CertStatusReqExtension(hsis, hsis.available());
+                StatusRequest sr = csre.getRequest();
+                if (!(sr instanceof UnknownStatusRequest)) {
+                    throw new RuntimeException("Expected returned status " +
+                            "request to be of type UnknownStatusRequest but " +
+                            "received " + sr.getClass().getName());
+                } else if (csre.length() != (junkData.length + 4)) {
+                    throw new RuntimeException("Invalid length: received " +
+                            csre.length() + ", expected " +
+                            junkData.length + 4);
+                }
+
+                // Set the leading byte to 1 (OCSP type) and run again
+                // It should pass the argument check and fail trying to parse
+                // the underlying StatusRequest.
+                junkData[0] = (byte)StatusRequestType.OCSP.id;
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(junkData));
+                try {
+                    csre = new CertStatusReqExtension(hsis, hsis.available());
+                    throw new RuntimeException("Expected CTOR exception did " +
+                            "not occur");
+                } catch (IOException ioe) { }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Take CSRE extension data and add extension type and length decorations
+    private static byte[] wrapExtData(byte[] extData) {
+        int bufferLen = (extData != null ? extData.length : 0) + 4;
+        ByteBuffer bb = ByteBuffer.allocate(bufferLen);
+        bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST.id);
+        bb.putShort((short)(extData != null ? extData.length: 0));
+        if (extData != null) {
+            bb.put(extData);
+        }
+        return bb.array();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/CertStatusReqItemV2Tests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS (CertStatusReqItemv2 tests)
+ * @build CertStatusReqItemV2Tests BogusStatusRequest TestCase TestUtils
+ * @run main/othervm sun.security.ssl.CertStatusReqItemV2Tests
+ */
+
+import java.security.cert.*;
+import java.util.*;
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLException;
+import javax.security.auth.x500.X500Principal;
+import sun.security.provider.certpath.ResponderId;
+import sun.security.provider.certpath.OCSPNonceExtension;
+
+/*
+ * Checks that the hash value for a certificate's issuer name is generated
+ * correctly. Requires any certificate that is not self-signed.
+ *
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class CertStatusReqItemV2Tests {
+
+    private static final boolean debug = false;
+
+    private static final byte[] DEF_CSRIV2_OCSP_MULTI_BYTES = {
+           2,    0,    4,    0,    0,    0,    0
+    };
+
+    private static final byte[] DEF_CSRIV2_OCSP_BYTES = {
+           1,    0,    4,    0,    0,    0,    0
+    };
+
+    // This is a CSRIV2 (ocsp_multi) that has a single
+    // responder ID and no extensions.
+    private static final byte[] CSRIV2_1RID = {
+            2,    0,   32,     0,   28,    0,   26,  -95,
+           24,   48,   22,    49,   20,   48,   18,    6,
+            3,   85,    4,     3,   19,   11,   79,   67,
+           83,   80,   32,    83,  105,  103,  110,  101,
+          114,    0 ,   0
+    };
+
+    // This is a CSRIV2 (ocsp_multi) that has a single
+    // responder ID and no extensions.  The request_length
+    // field is too short in this case.
+    private static final byte[] CSRIV2_LENGTH_TOO_SHORT = {
+            2,    0,   27,     0,   28,    0,   26,  -95,
+           24,   48,   22,    49,   20,   48,   18,    6,
+            3,   85,    4,     3,   19,   11,   79,   67,
+           83,   80,   32,    83,  105,  103,  110,  101,
+          114,    0 ,   0
+    };
+
+    // This is a CSRIV2 (ocsp_multi) that has a single
+    // responder ID and no extensions.  The request_length
+    // field is too long in this case.
+    private static final byte[] CSRIV2_LENGTH_TOO_LONG = {
+            2,    0,   54,     0,   28,    0,   26,  -95,
+           24,   48,   22,    49,   20,   48,   18,    6,
+            3,   85,    4,     3,   19,   11,   79,   67,
+           83,   80,   32,    83,  105,  103,  110,  101,
+          114,    0 ,   0
+    };
+
+    // A CSRIV2 (ocsp) with one Responder ID (byName: CN=OCSP Signer)
+    // and a nonce extension (32 bytes).
+    private static final byte[] CSRIV2_OCSP_1RID_1EXT = {
+            1,    0,   83,    0,   28,    0,   26,  -95,
+           24,   48,   22,   49,   20,   48,   18,    6,
+            3,   85,    4,    3,   19,   11,   79,   67,
+           83,   80,   32,   83,  105,  103,  110,  101,
+          114,    0,   51,   48,   49,   48,   47,    6,
+            9,   43,    6,    1,    5,    5,    7,   48,
+            1,    2,    4,   34,    4,   32,  -34,  -83,
+          -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
+          -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
+          -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
+          -66,  -17,  -34,  -83,  -66,  -17
+    };
+
+    public static void main(String[] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("CTOR (Default)", testCtorTypeStatReq);
+            put("CTOR (Byte array)", testCtorByteArray);
+            put("CTOR (invalid lengths)", testCtorInvalidLengths);
+        }};
+
+        TestUtils.runTests(testList);
+    }
+
+    public static final TestCase testCtorTypeStatReq = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                // Attempt to create CSRIv2 objects using null pointers
+                // for either parameter.  In either case NPE should be thrown
+                CertStatusReqItemV2 csriNull;
+                try {
+                    csriNull = new CertStatusReqItemV2(null,
+                            new OCSPStatusRequest());
+                    throw new RuntimeException("Did not catch expected NPE " +
+                            "for null status_type parameter");
+                } catch (NullPointerException npe) { }
+
+                try {
+                    csriNull = new CertStatusReqItemV2(StatusRequestType.OCSP,
+                            null);
+                    throw new RuntimeException("Did not catch expected NPE " +
+                            "for null StatusRequest parameter");
+                } catch (NullPointerException npe) { }
+
+                // Create an "ocsp_multi" type request using a default
+                // (no Responder IDs, no Extensions) OCSPStatusRequest
+                CertStatusReqItemV2 csriMulti =
+                        new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
+                                new OCSPStatusRequest());
+                HandshakeOutStream hsout = new HandshakeOutStream(null);
+                csriMulti.send(hsout);
+                TestUtils.valueCheck(DEF_CSRIV2_OCSP_MULTI_BYTES,
+                        hsout.toByteArray());
+                hsout.reset();
+
+                // Create an "ocsp" type request using a default
+                // (no Responder IDs, no Extensions) OCSPStatusRequest
+                CertStatusReqItemV2 csriSingle =
+                        new CertStatusReqItemV2(StatusRequestType.OCSP,
+                                new OCSPStatusRequest(new LinkedList<>(),
+                                        new LinkedList<>()));
+                csriSingle.send(hsout);
+                TestUtils.valueCheck(DEF_CSRIV2_OCSP_BYTES,
+                        hsout.toByteArray());
+
+                // Create the CertStatusRequestItemV2 with a user-defined
+                // StatusRequestType value
+                CertStatusReqItemV2 csriNine =
+                        new CertStatusReqItemV2(StatusRequestType.get(9),
+                                new OCSPStatusRequest(null, null));
+                if (csriNine.getType().id != 9) {
+                    throw new RuntimeException("Expected status_type = 9, " +
+                            "got " + csriNine.getType().id);
+                } else {
+                    StatusRequest sr = csriNine.getRequest();
+                    if (!(sr instanceof OCSPStatusRequest)) {
+                        throw new RuntimeException("Expected " +
+                                "OCSPStatusRequest, got " +
+                                sr.getClass().getName());
+                    }
+                }
+
+                // Create the CertStatusRequestItemV2 with a StatusRequest
+                // that does not match the status_type argument.
+                // We expect IllegalArgumentException in this case.
+                try {
+                    CertStatusReqItemV2 csriBadSR = new CertStatusReqItemV2(
+                            StatusRequestType.OCSP_MULTI,
+                            new BogusStatusRequest());
+                    throw new RuntimeException("Constructor accepted a " +
+                            "StatusRequest that is inconsistent with " +
+                            "the status_type");
+                } catch (IllegalArgumentException iae) {
+                    // The expected result...nothing to do here
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor form that takes the data from a byte array
+    public static final TestCase testCtorByteArray = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                StatusRequestType sType;
+                StatusRequest sReq;
+                ResponderId checkRid =
+                        new ResponderId(new X500Principal("CN=OCSP Signer"));
+                Extension checkExt = new OCSPNonceExtension(32);
+
+                CertStatusReqItemV2 csriv =
+                        new CertStatusReqItemV2(CSRIV2_OCSP_1RID_1EXT);
+                sType = csriv.getType();
+                if (sType != StatusRequestType.OCSP) {
+                    throw new RuntimeException("Unexpected StatusRequestType " +
+                            sType.getClass().getName());
+                }
+
+                sReq = csriv.getRequest();
+                if (sReq instanceof OCSPStatusRequest) {
+                    OCSPStatusRequest osr = (OCSPStatusRequest)sReq;
+                    List<ResponderId> ridList = osr.getResponderIds();
+                    List<Extension> extList = osr.getExtensions();
+
+                    if (ridList.size() != 1 || !ridList.contains(checkRid)) {
+                        throw new RuntimeException("Responder list mismatch");
+                    } else if (extList.size() !=  1 ||
+                            !extList.get(0).getId().equals(checkExt.getId())) {
+                        throw new RuntimeException("Extension list mismatch");
+                    }
+                } else {
+                    throw new RuntimeException("Expected OCSPStatusRequest " +
+                            "from decoded bytes, got " +
+                            sReq.getClass().getName());
+                }
+
+                // Create a CSRIV2 out of random data.  A non-OCSP/OCSP_MULTI
+                // type will be forcibly set and the outer length field will
+                // be correct.
+                // The constructor should create a StatusRequestType object
+                // and an UnknownStatusRequest object consisting of the
+                // data segment.
+                byte[] junkData = new byte[48];
+                Random r = new Random(System.currentTimeMillis());
+                r.nextBytes(junkData);
+                junkData[0] = 7;        // status_type = 7
+                junkData[1] = 0;
+                junkData[2] = 45;       // request_length = 45
+                csriv = new CertStatusReqItemV2(junkData);
+
+                sType = csriv.getType();
+                sReq = csriv.getRequest();
+                if (sType.id != junkData[0]) {
+                    throw new RuntimeException("StatusRequestType mismatch: " +
+                            "expected 7, got " + sType.id);
+                }
+                if (sReq instanceof UnknownStatusRequest) {
+                    // Verify the underlying StatusRequest bytes have been
+                    // preserved correctly.
+                    HandshakeOutStream hsout = new HandshakeOutStream(null);
+                    sReq.send(hsout);
+                    byte[] srDataOut = hsout.toByteArray();
+                    TestUtils.valueCheck(srDataOut, junkData, 0, 3,
+                            srDataOut.length);
+                } else {
+                    throw new RuntimeException("StatusRequest mismatch: " +
+                            "expected UnknownStatusRequest, got " +
+                            sReq.getClass().getName());
+                }
+
+                // Test the parsing of the default OCSP/OCSP_MULTI extensions
+                // and make sure the underlying StatusRequestType and
+                // StatusRequest objects are correct.
+                csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_MULTI_BYTES);
+                sType = csriv.getType();
+                sReq = csriv.getRequest();
+                if (sType != StatusRequestType.OCSP_MULTI) {
+                    throw new RuntimeException("StatusRequestType mismatch: " +
+                            "expected OCSP_MULTI (2), got " + sType.id);
+                }
+                if (!(sReq instanceof OCSPStatusRequest)) {
+                    throw new RuntimeException("StatusRequest mismatch: " +
+                            "expected OCSPStatusRequest, got " +
+                            sReq.getClass().getName());
+                }
+
+                csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_BYTES);
+                sType = csriv.getType();
+                sReq = csriv.getRequest();
+                if (sType != StatusRequestType.OCSP) {
+                    throw new RuntimeException("StatusRequestType mismatch: " +
+                            "expected OCSP (1), got " + sType.id);
+                }
+                if (!(sReq instanceof OCSPStatusRequest)) {
+                    throw new RuntimeException("StatusRequest mismatch: " +
+                            "expected OCSPStatusRequest, got " +
+                            sReq.getClass().getName());
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testCtorInvalidLengths = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                try {
+                    CertStatusReqItemV2 csriTooShort =
+                            new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_SHORT);
+                    throw new RuntimeException("Expected exception not thrown");
+                } catch (SSLException ssle) { }
+
+                try {
+                    CertStatusReqItemV2 csriTooLong =
+                            new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_LONG);
+                    throw new RuntimeException("Expected exception not thrown");
+                } catch (SSLException ssle) { }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor form that takes the data from HandshakeInputStream
+    public static final TestCase testCtorInputStream = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                StatusRequestType sType;
+                StatusRequest sReq;
+                ResponderId checkRid =
+                        new ResponderId(new X500Principal("CN=OCSP Signer"));
+                Extension checkExt = new OCSPNonceExtension(32);
+
+                HandshakeInStream hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(CSRIV2_OCSP_1RID_1EXT));
+                CertStatusReqItemV2 csriv = new CertStatusReqItemV2(hsis);
+                sType = csriv.getType();
+                if (sType != StatusRequestType.OCSP) {
+                    throw new RuntimeException("Unexpected StatusRequestType " +
+                            sType.getClass().getName());
+                }
+
+                sReq = csriv.getRequest();
+                if (sReq instanceof OCSPStatusRequest) {
+                    OCSPStatusRequest osr = (OCSPStatusRequest)sReq;
+                    List<ResponderId> ridList = osr.getResponderIds();
+                    List<Extension> extList = osr.getExtensions();
+
+                    if (ridList.size() != 1 || !ridList.contains(checkRid)) {
+                        throw new RuntimeException("Responder list mismatch");
+                    } else if (extList.size() !=  1 ||
+                            !extList.get(0).getId().equals(checkExt.getId())) {
+                        throw new RuntimeException("Extension list mismatch");
+                    }
+                } else {
+                    throw new RuntimeException("Expected OCSPStatusRequest " +
+                            "from decoded bytes, got " +
+                            sReq.getClass().getName());
+                }
+
+                // Create a CSRIV2 out of random data.  A non-OCSP/OCSP_MULTI
+                // type will be forcibly set and the outer length field will
+                // be correct.
+                // The constructor should create a StatusRequestType object
+                // and an UnknownStatusRequest object consisting of the
+                // data segment.
+                byte[] junkData = new byte[48];
+                Random r = new Random(System.currentTimeMillis());
+                r.nextBytes(junkData);
+                junkData[0] = 7;        // status_type = 7
+                junkData[1] = 0;
+                junkData[2] = 45;       // request_length = 45
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(junkData));
+                csriv = new CertStatusReqItemV2(hsis);
+
+                sType = csriv.getType();
+                sReq = csriv.getRequest();
+                if (sType.id != junkData[0]) {
+                    throw new RuntimeException("StatusRequestType mismatch: " +
+                            "expected 7, got " + sType.id);
+                }
+                if (sReq instanceof UnknownStatusRequest) {
+                    // Verify the underlying StatusRequest bytes have been
+                    // preserved correctly.
+                    HandshakeOutStream hsout = new HandshakeOutStream(null);
+                    sReq.send(hsout);
+                    byte[] srDataOut = hsout.toByteArray();
+                    TestUtils.valueCheck(srDataOut, junkData, 0, 3,
+                            srDataOut.length);
+                } else {
+                    throw new RuntimeException("StatusRequest mismatch: " +
+                            "expected UnknownStatusRequest, got " +
+                            sReq.getClass().getName());
+                }
+
+                // Test the parsing of the default OCSP/OCSP_MULTI extensions
+                // and make sure the underlying StatusRequestType and
+                // StatusRequest objects are correct.
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(
+                        ByteBuffer.wrap(DEF_CSRIV2_OCSP_MULTI_BYTES));
+                csriv = new CertStatusReqItemV2(hsis);
+                sType = csriv.getType();
+                sReq = csriv.getRequest();
+                if (sType != StatusRequestType.OCSP_MULTI) {
+                    throw new RuntimeException("StatusRequestType mismatch: " +
+                            "expected OCSP_MULTI (2), got " + sType.id);
+                }
+                if (!(sReq instanceof OCSPStatusRequest)) {
+                    throw new RuntimeException("StatusRequest mismatch: " +
+                            "expected OCSPStatusRequest, got " +
+                            sReq.getClass().getName());
+                }
+
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(DEF_CSRIV2_OCSP_BYTES));
+                csriv = new CertStatusReqItemV2(hsis);
+                sType = csriv.getType();
+                sReq = csriv.getRequest();
+                if (sType != StatusRequestType.OCSP) {
+                    throw new RuntimeException("StatusRequestType mismatch: " +
+                            "expected OCSP (1), got " + sType.id);
+                }
+                if (!(sReq instanceof OCSPStatusRequest)) {
+                    throw new RuntimeException("StatusRequest mismatch: " +
+                            "expected OCSPStatusRequest, got " +
+                            sReq.getClass().getName());
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/CertStatusReqListV2ExtensionTests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS (CertStatusReqListV2Extension tests)
+ * @build CertStatusReqListV2ExtensionTests TestCase TestUtils
+ * @run main/othervm sun.security.ssl.CertStatusReqListV2ExtensionTests
+ */
+
+import java.io.IOException;
+import java.util.*;
+import java.nio.ByteBuffer;
+import javax.net.ssl.*;
+
+/*
+ * Checks that the hash value for a certificate's issuer name is generated
+ * correctly. Requires any certificate that is not self-signed.
+ *
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class CertStatusReqListV2ExtensionTests {
+
+    private static final boolean debug = false;
+
+    // Default status_request_v2 extension with two items
+    // 1. Type = ocsp_multi, OCSPStatusRequest is default
+    // 2. Type = ocsp, OCSPStatusRequest is default
+    private static final byte[] CSRLV2_DEF = {
+           0,   14,    2,    0,    4,    0,    0,    0,
+           0,    1,    0,    4,    0,    0,    0,    0
+    };
+
+    // A status_request_v2 where the item list length is
+    // longer than the provided data
+    private static final byte[] CSRLV2_LEN_TOO_LONG = {
+           0,   18,    2,    0,    4,    0,    0,    0,
+           0,    1,    0,    4,    0,    0,    0,    0
+    };
+
+    // A status_request_v2 where the item list length is
+    // shorter than the provided data
+    private static final byte[] CSRLV2_LEN_TOO_SHORT = {
+           0,   11,    2,    0,    4,    0,    0,    0,
+           0,    1,    0,    4,    0,    0,    0,    0
+    };
+
+    // A status_request_v2 extension with a zero-length
+    // certificate_status_req_list (not allowed by the spec)
+    private static final byte[] CSRLV2_INVALID_ZEROLEN = {0, 0};
+
+    // A status_request_v2 extension with two items (ocsp_multi and ocsp)
+    // using OCSPStatusRequests with 5 ResponderIds and 1 Extension each.
+    private static final byte[] CSRLV2_TWO_NON_DEF_ITEMS = {
+            2,   90,    2,    1,   42,    0,  -13,    0,
+           59,  -95,   57,   48,   55,   49,   16,   48,
+           14,    6,    3,   85,    4,   10,   19,    7,
+           83,  111,  109,  101,   73,  110,   99,   49,
+           16,   48,   14,    6,    3,   85,    4,   11,
+           19,    7,   83,  111,  109,  101,   80,   75,
+           73,   49,   17,   48,   15,    6,    3,   85,
+            4,    3,   19,    8,   83,  111,  109,  101,
+           79,   67,   83,   80,    0,   68,  -95,   66,
+           48,   64,   49,   13,   48,   11,    6,    3,
+           85,    4,   10,   19,    4,   79,  104,   77,
+          121,   49,   14,   48,   12,    6,    3,   85,
+            4,   11,   19,    5,   66,  101,   97,  114,
+          115,   49,   15,   48,   13,    6,    3,   85,
+            4,   11,   19,    6,   84,  105,  103,  101,
+          114,  115,   49,   14,   48,   12,    6,    3,
+           85,    4,    3,   19,    5,   76,  105,  111,
+          110,  115,    0,   58,  -95,   56,   48,   54,
+           49,   16,   48,   14,    6,    3,   85,    4,
+           10,   19,    7,   67,  111,  109,  112,   97,
+          110,  121,   49,   13,   48,   11,    6,    3,
+           85,    4,   11,   19,    4,   87,  101,  115,
+          116,   49,   19,   48,   17,    6,    3,   85,
+            4,    3,   19,   10,   82,  101,  115,  112,
+          111,  110,  100,  101,  114,   49,    0,   24,
+          -94,   22,    4,   20,  -67,  -36,  114,  121,
+           92,  -79,  116,   -1,  102, -107,    7,  -21,
+           18, -113,   64,   76,   96,   -7,  -66,  -63,
+            0,   24,  -94,   22,    4,   20,  -51,  -69,
+          107,  -82,  -39,  -87,   45,   25,   41,   28,
+          -76,  -68,  -11, -110,  -94,  -97,   62,   47,
+           58, -125,    0,   51,   48,   49,   48,   47,
+            6,    9,   43,    6,    1,    5,    5,    7,
+           48,    1,    2,    4,   34,    4,   32,  -26,
+          -81, -120,  -61, -127,  -79,    0,  -39,  -54,
+           49,    3,  -51,  -57,  -85,   19, -126,   94,
+           -2,   21,   26,   98,    6,  105,  -35,  -37,
+          -29,  -73,  101,   53,   44,   15,  -19,    1,
+            1,   42,    0,  -13,    0,   59,  -95,   57,
+           48,   55,   49,   16,   48,   14,    6,    3,
+           85,    4,   10,   19,    7,   83,  111,  109,
+          101,   73,  110,   99,   49,   16,   48,   14,
+            6,    3,   85,    4,   11,   19,    7,   83,
+          111,  109,  101,   80,   75,   73,   49,   17,
+           48,   15,    6,    3,   85,    4,    3,   19,
+            8,   83,  111,  109,  101,   79,   67,   83,
+           80,    0,   68,  -95,   66,   48,   64,   49,
+           13,   48,   11,    6,    3,   85,    4,   10,
+           19,    4,   79,  104,   77,  121,   49,   14,
+           48,   12,    6,    3,   85,    4,   11,   19,
+            5,   66,  101,   97,  114,  115,   49,   15,
+           48,   13,    6,    3,   85,    4,   11,   19,
+            6,   84,  105,  103,  101,  114,  115,   49,
+           14,   48,   12,    6,    3,   85,    4,    3,
+           19,    5,   76,  105,  111,  110,  115,    0,
+           58,  -95,   56,   48,   54,   49,   16,   48,
+           14,    6,    3,   85,    4,   10,   19,    7,
+           67,  111,  109,  112,   97,  110,  121,   49,
+           13,   48,   11,    6,    3,   85,    4,   11,
+           19,    4,   87,  101,  115,  116,   49,   19,
+           48,   17,    6,    3,   85,    4,    3,   19,
+           10,   82,  101,  115,  112,  111,  110,  100,
+          101,  114,   49,    0,   24,  -94,   22,    4,
+           20,  -67,  -36,  114,  121,   92,  -79,  116,
+           -1,  102, -107,    7,  -21,   18, -113,   64,
+           76,   96,   -7,  -66,  -63,    0,   24,  -94,
+           22,    4,   20,  -51,  -69,  107,  -82,  -39,
+          -87,   45,   25,   41,   28,  -76,  -68,  -11,
+         -110,  -94,  -97,   62,   47,   58, -125,    0,
+           51,   48,   49,   48,   47,    6,    9,   43,
+            6,    1,    5,    5,    7,   48,    1,    2,
+            4,   34,    4,   32,  -26,  -81, -120,  -61,
+         -127,  -79,    0,  -39,  -54,   49,    3,  -51,
+          -57,  -85,   19, -126,   94,   -2,   21,   26,
+           98,    6,  105,  -35,  -37,  -29,  -73,  101,
+           53,   44,   15,  -19
+    };
+
+    public static void main(String[] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("CTOR (default)", testCtorDefault);
+            put("CTOR (List<CertStatusReqItemV2)", testCtorItemList);
+            put("CTOR (HandshakeInStream, getRequestList",
+                    testCtorInStream);
+        }};
+
+        TestUtils.runTests(testList);
+    }
+
+    public static final TestCase testCtorDefault = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                CertStatusReqListV2Extension csrlV2 =
+                        new CertStatusReqListV2Extension();
+                HandshakeOutStream hsout = new HandshakeOutStream(null);
+                csrlV2.send(hsout);
+                TestUtils.valueCheck(wrapExtData(new byte[0]),
+                        hsout.toByteArray());
+
+                // The length should be 4 (2 bytes for the type, 2 for the
+                // encoding of zero-length
+                if (csrlV2.length() != 4) {
+                    throw new RuntimeException("Incorrect length from " +
+                            "default object.  Expected 4, got " +
+                            csrlV2.length());
+                }
+
+                // Since there's no data, there are no status_type or request
+                // data fields defined.  An empty, unmodifiable list should be
+                // returned when obtained from the extension.
+                List<CertStatusReqItemV2> itemList = csrlV2.getRequestItems();
+                if (!itemList.isEmpty()) {
+                    throw new RuntimeException("Default CSRLV2 returned " +
+                            "non-empty request list");
+                } else {
+                    try {
+                        itemList.add(new CertStatusReqItemV2(
+                                StatusRequestType.OCSP_MULTI,
+                                new OCSPStatusRequest()));
+                        throw new RuntimeException("Returned itemList is " +
+                                "modifiable!");
+                    } catch (UnsupportedOperationException uoe) { }
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    public static final TestCase testCtorItemList = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            OCSPStatusRequest osr = new OCSPStatusRequest();
+            List<CertStatusReqItemV2> noItems = Collections.emptyList();
+            List<CertStatusReqItemV2> defList =
+                    new ArrayList<CertStatusReqItemV2>() {{
+                add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, osr));
+                add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr));
+            }};
+            List<CertStatusReqItemV2> unknownTypesList =
+                    new ArrayList<CertStatusReqItemV2>() {{
+                add(new CertStatusReqItemV2(StatusRequestType.get(8),
+                        new UnknownStatusRequest(new byte[0])));
+                add(new CertStatusReqItemV2(StatusRequestType.get(12),
+                        new UnknownStatusRequest(new byte[5])));
+            }};
+
+            try {
+                HandshakeOutStream hsout = new HandshakeOutStream(null);
+                StatusRequest basicStatReq = new OCSPStatusRequest();
+
+                // Create an extension using a default-style OCSPStatusRequest
+                // (no responder IDs, no extensions).
+                CertStatusReqListV2Extension csrlv2 =
+                        new CertStatusReqListV2Extension(defList);
+                csrlv2.send(hsout);
+                TestUtils.valueCheck(wrapExtData(CSRLV2_DEF),
+                        hsout.toByteArray());
+                hsout.reset();
+
+                // Create the extension using a StatusRequestType not already
+                // instantiated as a static StatusRequestType
+                // (e.g. OCSP/OCSP_MULTI)
+                csrlv2 = new CertStatusReqListV2Extension(unknownTypesList);
+                List<CertStatusReqItemV2> itemList = csrlv2.getRequestItems();
+                if (itemList.size() != unknownTypesList.size()) {
+                    throw new RuntimeException("Custom CSRLV2 returned " +
+                            "an incorrect number of items: expected " +
+                            unknownTypesList.size() + ", got " +
+                            itemList.size());
+                } else {
+                    // Verify that the list is unmodifiable
+                    try {
+                        itemList.add(new CertStatusReqItemV2(
+                                StatusRequestType.OCSP_MULTI,
+                                new OCSPStatusRequest()));
+                        throw new RuntimeException("Returned itemList is " +
+                                "modifiable!");
+                    } catch (UnsupportedOperationException uoe) { }
+                }
+
+                // Pass a null value for the item list.  This should throw
+                // an exception
+                try {
+                    CertStatusReqListV2Extension csrlv2Null =
+                            new CertStatusReqListV2Extension(null);
+                    throw new RuntimeException("Constructor accepted a " +
+                            "null request list");
+                } catch (NullPointerException npe) { }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor that builds the ob ject using data from
+    // a HandshakeInStream
+    // This also tests the length, getReqType and getRequest methods
+    public static final TestCase testCtorInStream = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            OCSPStatusRequest osr;
+            CertStatusReqListV2Extension csrlv2;
+
+            try {
+                // To simulate the extension coming in a ServerHello, the
+                // type and length would already be read by HelloExtensions
+                // and there is no extension data
+                HandshakeInStream hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(new byte[0]));
+                csrlv2 = new CertStatusReqListV2Extension(hsis,
+                        hsis.available());
+
+                // Verify length/request list
+                if (csrlv2.length() != 4) {
+                     throw new RuntimeException("Invalid length: received " +
+                            csrlv2.length() + ", expected 4");
+                } else {
+                    List<CertStatusReqItemV2> itemList =
+                            csrlv2.getRequestItems();
+                    if (!itemList.isEmpty()) {
+                        throw new RuntimeException("Default CSRLV2 returned " +
+                                "non-empty request list");
+                    } else {
+                        try {
+                            itemList.add(new CertStatusReqItemV2(
+                                    StatusRequestType.OCSP_MULTI,
+                                    new OCSPStatusRequest()));
+                            throw new RuntimeException("Returned itemList is " +
+                                    "modifiable!");
+                        } catch (UnsupportedOperationException uoe) { }
+                    }
+                }
+
+                // Try the an extension with our basic client-generated
+                // status_request_v2 (2 items, ocsp_multi and ocsp, each with
+                // a default OCSPStatusRequest
+                hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_DEF));
+                csrlv2 = new CertStatusReqListV2Extension(hsis,
+                        hsis.available());
+                if (csrlv2.length() != (CSRLV2_DEF.length + 4)) {
+                    throw new RuntimeException("Invalid length: received " +
+                            csrlv2.length() + ", expected " +
+                            CSRLV2_DEF.length + 4);
+                } else {
+                    List<CertStatusReqItemV2> itemList =
+                            csrlv2.getRequestItems();
+                    if (itemList.size() != 2) {
+                        throw new RuntimeException("Unexpected number of " +
+                                "items request list, expected 2, got " +
+                                itemList.size());
+                    } else {
+                        try {
+                            itemList.add(new CertStatusReqItemV2(
+                                    StatusRequestType.OCSP_MULTI,
+                                    new OCSPStatusRequest()));
+                            throw new RuntimeException("Returned itemList is " +
+                                    "modifiable!");
+                        } catch (UnsupportedOperationException uoe) { }
+                    }
+                }
+
+                // Try incoming data with an illegal zero-length
+                // certificate_status_req_list
+                try {
+                    hsis = new HandshakeInStream();
+                    hsis.incomingRecord(
+                            ByteBuffer.wrap(CSRLV2_INVALID_ZEROLEN));
+                    csrlv2 = new CertStatusReqListV2Extension(hsis,
+                            hsis.available());
+                    throw new RuntimeException("Unxpected successful " +
+                            "object construction");
+                } catch (SSLException ssle) { }
+
+                // Try extensions where the certificate_status_req_list length
+                // is either too long or too short
+                try {
+                    hsis = new HandshakeInStream();
+                    hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_LEN_TOO_LONG));
+                    csrlv2 = new CertStatusReqListV2Extension(hsis,
+                            hsis.available());
+                    throw new RuntimeException("Unxpected successful " +
+                            "object construction");
+                } catch (SSLException ssle) { }
+
+                try {
+                    hsis = new HandshakeInStream();
+                    hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_LEN_TOO_SHORT));
+                    csrlv2 = new CertStatusReqListV2Extension(hsis,
+                            hsis.available());
+                    throw new RuntimeException("Unxpected successful " +
+                            "object construction");
+                } catch (SSLException ssle) { }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Take CSRE extension data and add extension type and length decorations
+    private static byte[] wrapExtData(byte[] extData) {
+        int bufferLen = extData.length + 4;
+        ByteBuffer bb = ByteBuffer.allocate(bufferLen);
+
+        bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST_V2.id);
+        bb.putShort((short)extData.length);
+        if (extData.length != 0) {
+            bb.put(extData);
+        }
+        return bb.array();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/OCSPStatusRequestTests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS (OCSPStatusRequestTests tests)
+ * @build OCSPStatusRequestTests TestCase TestUtils
+ * @run main/othervm sun.security.ssl.OCSPStatusRequestTests
+ */
+
+import java.security.cert.*;
+import java.util.*;
+import java.nio.ByteBuffer;
+import javax.security.auth.x500.X500Principal;
+import sun.security.provider.certpath.ResponderId;
+import sun.security.provider.certpath.OCSPNonceExtension;
+
+/*
+ * Checks that the hash value for a certificate's issuer name is generated
+ * correctly. Requires any certificate that is not self-signed.
+ *
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class OCSPStatusRequestTests {
+
+    private static final boolean debug = false;
+
+    // The default (no Responder IDs or Extensions)
+    private static final byte[] DEF_OCSPREQ_BYTES = { 0, 0, 0, 0 };
+
+    // OCSP Extension with one Responder ID (byName: CN=OCSP Signer) and
+    // a nonce extension (32 bytes).
+    private static final byte[] OCSPREQ_1RID_1EXT = {
+            0,   28,    0,   26,  -95,   24,   48,   22,
+           49,   20,   48,   18,    6,    3,   85,    4,
+            3,   19,   11,   79,   67,   83,   80,   32,
+           83,  105,  103,  110,  101,  114,    0,   51,
+           48,   49,   48,   47,    6,    9,   43,    6,
+            1,    5,    5,    7,   48,    1,    2,    4,
+           34,    4,   32,  -34,  -83,  -66,  -17,  -34,
+          -83,  -66,  -17,  -34,  -83,  -66,  -17,  -34,
+          -83,  -66,  -17,  -34,  -83,  -66,  -17,  -34,
+          -83,  -66,  -17,  -34,  -83,  -66,  -17,  -34,
+          -83,  -66,  -17
+    };
+
+    public static void main(String[] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("CTOR (default)", testCtorDefault);
+            put("CTOR (Responder Id and Extension)", testCtorRidsExts);
+            put("CTOR (HandshakeInStream)", testCtorInStream);
+            put("CTOR (byte array)", testCtorByteArray);
+            put("Length tests", testLength);
+            put("Equals tests", testEquals);
+        }};
+
+        TestUtils.runTests(testList);
+    }
+
+    // Test the default constructor and its encoding
+    public static final TestCase testCtorDefault = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                // Create a OCSPStatusRequest with a single ResponderId
+                // and Extension
+                OCSPStatusRequest osrDefault = new OCSPStatusRequest();
+                HandshakeOutStream hsout = new HandshakeOutStream(null);
+                osrDefault.send(hsout);
+                System.out.println("Encoded Result:");
+                TestUtils.dumpBytes(hsout.toByteArray());
+
+                TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor form that allows the user to specify zero
+    // or more ResponderId objects and/or Extensions
+    public static final TestCase testCtorRidsExts = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                List<ResponderId> ridList = new LinkedList<ResponderId>() {{
+                    add(new ResponderId(new X500Principal("CN=OCSP Signer")));
+                }};
+                List<Extension> extList = new LinkedList<Extension>() {{
+                    add(new OCSPNonceExtension(32));
+                }};
+
+                // Default-style OCSPStatusRequest using both empty Lists and
+                // null inputs
+                OCSPStatusRequest osrDef1 =
+                        new OCSPStatusRequest(new LinkedList<ResponderId>(),
+                                null);
+                OCSPStatusRequest osrDef2 =
+                        new OCSPStatusRequest(null,
+                                new LinkedList<Extension>());
+                HandshakeOutStream hsout = new HandshakeOutStream(null);
+                osrDef1.send(hsout);
+                System.out.println("Encoded Result:");
+                TestUtils.dumpBytes(hsout.toByteArray());
+                TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
+
+                hsout.reset();
+                osrDef2.send(hsout);
+                System.out.println("Encoded Result:");
+                TestUtils.dumpBytes(hsout.toByteArray());
+                TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
+
+                hsout.reset();
+                OCSPStatusRequest osrWithItems =
+                        new OCSPStatusRequest(ridList, extList);
+                osrWithItems.send(hsout);
+                System.out.println("Encoded Result:");
+                byte[] encodedData = hsout.toByteArray();
+                TestUtils.dumpBytes(encodedData);
+                // Check everything except the last 32 bytes (nonce data)
+                TestUtils.valueCheck(OCSPREQ_1RID_1EXT, encodedData, 0, 0,
+                        encodedData.length - 32);
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor that builds the ob ject using data from
+    // a HandshakeInStream
+    public static final TestCase testCtorInStream = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                ResponderId checkRid =
+                        new ResponderId(new X500Principal("CN=OCSP Signer"));
+                Extension checkExt = new OCSPNonceExtension(32);
+
+                HandshakeInStream hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
+                OCSPStatusRequest osr = new OCSPStatusRequest(hsis);
+
+                List<ResponderId> ridList = osr.getResponderIds();
+                List<Extension> extList = osr.getExtensions();
+
+                if (ridList.size() != 1 || !ridList.contains(checkRid)) {
+                    throw new RuntimeException("Responder list mismatch");
+                } else if (extList.size() !=  1 ||
+                        !extList.get(0).getId().equals(checkExt.getId())) {
+                    throw new RuntimeException("Extension list mismatch");
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the constructor form that takes the data from a byte array
+    public static final TestCase testCtorByteArray = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                ResponderId checkRid =
+                        new ResponderId(new X500Principal("CN=OCSP Signer"));
+                Extension checkExt = new OCSPNonceExtension(32);
+
+                OCSPStatusRequest osr =
+                        new OCSPStatusRequest(OCSPREQ_1RID_1EXT);
+
+                List<ResponderId> ridList = osr.getResponderIds();
+                List<Extension> extList = osr.getExtensions();
+
+                if (ridList.size() != 1 || !ridList.contains(checkRid)) {
+                    throw new RuntimeException("Responder list mismatch");
+                } else if (extList.size() !=  1 ||
+                        !extList.get(0).getId().equals(checkExt.getId())) {
+                    throw new RuntimeException("Extension list mismatch");
+                }
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the length functions for both default and non-default
+    // OCSPStatusRequest objects
+    public static final TestCase testLength = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                HandshakeInStream hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
+                OCSPStatusRequest osr = new OCSPStatusRequest(hsis);
+                OCSPStatusRequest osrDefault = new OCSPStatusRequest();
+
+                if (osrDefault.length() != DEF_OCSPREQ_BYTES.length) {
+                    throw new RuntimeException("Invalid length for default: " +
+                            "Expected" + DEF_OCSPREQ_BYTES.length +
+                            ", received " + osrDefault.length());
+                } else if (osr.length() != OCSPREQ_1RID_1EXT.length) {
+                    throw new RuntimeException("Invalid length for default: " +
+                            "Expected" + OCSPREQ_1RID_1EXT.length +
+                            ", received " + osr.length());
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test the equals method with default and non-default objects
+    public static final TestCase testEquals = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            try {
+                // Make two different lists with the same ResponderId values
+                // and also make a extension list
+                List<ResponderId> ridList1 = new LinkedList<ResponderId>() {{
+                    add(new ResponderId(new X500Principal("CN=OCSP Signer")));
+                }};
+                List<ResponderId> ridList2 = new LinkedList<ResponderId>() {{
+                    add(new ResponderId(new X500Principal("CN=OCSP Signer")));
+                }};
+                List<Extension> extList = new LinkedList<Extension>() {{
+                    add(new OCSPNonceExtension(32));
+                }};
+
+                // We expect two default OCSP objects to be equal
+                OCSPStatusRequest osrDefault = new OCSPStatusRequest();
+                if (!osrDefault.equals(new OCSPStatusRequest())) {
+                    throw new RuntimeException("Default OCSPStatusRequest" +
+                            " equality test failed");
+                }
+
+                // null test (expect false return)
+                if (osrDefault.equals(null)) {
+                    throw new RuntimeException("OCSPStatusRequest matched" +
+                            " unexpectedly");
+                }
+
+                // Self-reference test
+                OCSPStatusRequest osrSelfRef = osrDefault;
+                if (!osrDefault.equals(osrSelfRef)) {
+                    throw new RuntimeException("Default OCSPStatusRequest" +
+                            " equality test failed");
+                }
+
+                // Two OCSPStatusRequests with matching ResponderIds should
+                // be considered equal
+                OCSPStatusRequest osrByList1 =
+                        new OCSPStatusRequest(ridList1, null);
+                OCSPStatusRequest osrByList2 = new OCSPStatusRequest(ridList2,
+                        Collections.emptyList());
+                if (!osrByList1.equals(osrByList2)) {
+                    throw new RuntimeException("Single Responder ID " +
+                            "OCSPStatusRequest equality test failed");
+                }
+
+                // We expect OCSPStatusRequests with different nonces to be
+                // considered unequal.
+                HandshakeInStream hsis = new HandshakeInStream();
+                hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
+                OCSPStatusRequest osrStream = new OCSPStatusRequest(hsis);
+                OCSPStatusRequest osrRidExt = new OCSPStatusRequest(ridList1,
+                        extList);
+                if (osrStream.equals(osrRidExt)) {
+                    throw new RuntimeException("OCSPStatusRequest matched" +
+                            " unexpectedly");
+                }
+
+                pass = Boolean.TRUE;
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/StatusResponseManagerTests.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+/*
+ * @test
+ * @bug 8046321
+ * @summary OCSP Stapling for TLS (StatusResponseManager tests)
+ * @library ../../../../java/security/testlibrary
+ * @build CertificateBuilder SimpleOCSPServer
+ * @build StatusResponseManagerTests TestCase TestUtils
+ * @run main/othervm -Djavax.net.debug=ssl:respmgr
+ *      sun.security.ssl.StatusResponseManagerTests
+ */
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.*;
+import java.util.*;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+
+/*
+ * Checks that the hash value for a certificate's issuer name is generated
+ * correctly. Requires any certificate that is not self-signed.
+ *
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class StatusResponseManagerTests {
+
+    private static final boolean debug = true;
+    private static final boolean ocspDebug = false;
+
+    // PKI components we will need for this test
+    static String passwd = "passphrase";
+    static String ROOT_ALIAS = "root";
+    static String INT_ALIAS = "intermediate";
+    static String SSL_ALIAS = "ssl";
+    static KeyStore rootKeystore;           // Root CA Keystore
+    static KeyStore intKeystore;            // Intermediate CA Keystore
+    static KeyStore serverKeystore;         // SSL Server Keystore
+    static KeyStore trustStore;             // SSL Client trust store
+    static X509Certificate rootCert;
+    static X509Certificate intCert;
+    static X509Certificate sslCert;
+    static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
+    static int rootOcspPort;                // Port number for root OCSP
+    static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
+    static int intOcspPort;                 // Port number for intermed. OCSP
+
+    static X509Certificate[] chain;
+
+    public static void main(String[] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("Basic OCSP fetch test", testOcspFetch);
+            put("Clear StatusResponseManager cache", testClearSRM);
+            put("Basic OCSP_MULTI fetch test", testOcspMultiFetch);
+            put("Test Cache Expiration", testCacheExpiry);
+        }};
+
+        // Create the CAs and OCSP responders
+        createPKI();
+
+        // Grab the certificates and make a chain we can reuse for tests
+        sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+        intCert = (X509Certificate)intKeystore.getCertificate(INT_ALIAS);
+        rootCert = (X509Certificate)rootKeystore.getCertificate(ROOT_ALIAS);
+        chain = new X509Certificate[3];
+        chain[0] = sslCert;
+        chain[1] = intCert;
+        chain[2] = rootCert;
+
+        TestUtils.runTests(testList);
+
+        intOcsp.stop();
+        rootOcsp.stop();
+    }
+
+    // Test a simple RFC 6066 server-side fetch
+    public static final TestCase testOcspFetch = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            StatusRequest oReq = new OCSPStatusRequest();
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                Map<X509Certificate, byte[]> responseMap = srm.get(
+                        StatusRequestType.OCSP, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be one entry in the returned map and
+                // one entry in the cache when the operation is complete.
+                if (responseMap.size() != 1) {
+                    message = "Incorrect number of responses: expected 1, got "
+                            + responseMap.size();
+                } else if (!responseMap.containsKey(sslCert)) {
+                    message = "Response map key is incorrect, expected " +
+                            sslCert.getSubjectX500Principal().toString();
+                } else if (srm.size() != 1) {
+                    message = "Incorrect number of cache entries: " +
+                            "expected 1, got " + srm.size();
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test clearing the StatusResponseManager cache.
+    public static final TestCase testClearSRM = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            StatusRequest oReq = new OCSPStatusRequest();
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be two entries in the returned map and
+                // two entries in the cache when the operation is complete.
+                if (srm.size() != 2) {
+                    message = "Incorrect number of responses: expected 2, got "
+                            + srm.size();
+                } else {
+                    // Next, clear the SRM, then check the size again
+                    srm.clear();
+                    if (srm.size() != 0) {
+                        message = "Incorrect number of responses: expected 0," +
+                                " got " + srm.size();
+                    } else {
+                        pass = Boolean.TRUE;
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test a simple RFC 6961 server-side fetch
+    public static final TestCase testOcspMultiFetch = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            StatusRequest oReq = new OCSPStatusRequest();
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                Map<X509Certificate, byte[]> responseMap = srm.get(
+                        StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be two entries in the returned map and
+                // two entries in the cache when the operation is complete.
+                if (responseMap.size() != 2) {
+                    message = "Incorrect number of responses: expected 2, got "
+                            + responseMap.size();
+                } else if (!responseMap.containsKey(sslCert) ||
+                        !responseMap.containsKey(intCert)) {
+                    message = "Response map keys are incorrect, expected " +
+                            sslCert.getSubjectX500Principal().toString() +
+                            " and " +
+                            intCert.getSubjectX500Principal().toString();
+                } else if (srm.size() != 2) {
+                    message = "Incorrect number of cache entries: " +
+                            "expected 2, got " + srm.size();
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test cache expiration
+    public static final TestCase testCacheExpiry = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            // For this test, we will set the cache expiry to 5 seconds
+            System.setProperty("jdk.tls.stapling.cacheLifetime", "5");
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            StatusRequest oReq = new OCSPStatusRequest();
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be two entries in the returned map and
+                // two entries in the cache when the operation is complete.
+                if (srm.size() != 2) {
+                    message = "Incorrect number of responses: expected 2, got "
+                            + srm.size();
+                } else {
+                    // Next, wait for more than 5 seconds so the responses
+                    // in the SRM will expire.
+                    Thread.sleep(7000);
+                    if (srm.size() != 0) {
+                        message = "Incorrect number of responses: expected 0," +
+                                " got " + srm.size();
+                    } else {
+                        pass = Boolean.TRUE;
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            // Set the cache lifetime back to the default
+            System.setProperty("jdk.tls.stapling.cacheLifetime", "");
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    /**
+     * Creates the PKI components necessary for this test, including
+     * Root CA, Intermediate CA and SSL server certificates, the keystores
+     * for each entity, a client trust store, and starts the OCSP responders.
+     */
+    private static void createPKI() throws Exception {
+        CertificateBuilder cbld = new CertificateBuilder();
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        KeyStore.Builder keyStoreBuilder =
+                KeyStore.Builder.newInstance("PKCS12", null,
+                        new KeyStore.PasswordProtection(passwd.toCharArray()));
+
+        // Generate Root, IntCA, EE keys
+        KeyPair rootCaKP = keyGen.genKeyPair();
+        log("Generated Root CA KeyPair");
+        KeyPair intCaKP = keyGen.genKeyPair();
+        log("Generated Intermediate CA KeyPair");
+        KeyPair sslKP = keyGen.genKeyPair();
+        log("Generated SSL Cert KeyPair");
+
+        // Set up the Root CA Cert
+        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
+        cbld.setPublicKey(rootCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("1"));
+        // Make a 3 year validity starting from 60 days ago
+        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+        long end = start + TimeUnit.DAYS.toMillis(1085);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        // Make our Root CA Cert!
+        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Root CA Created:\n" + certInfo(rootCert));
+
+        // Now build a keystore and add the keys and cert
+        rootKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] rootChain = {rootCert};
+        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
+                passwd.toCharArray(), rootChain);
+
+        // Now fire up the OCSP responder
+        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
+        rootOcsp.enableLog(ocspDebug);
+        rootOcsp.setNextUpdateInterval(3600);
+        rootOcsp.start();
+        Thread.sleep(1000);         // Give the server a second to start up
+        rootOcspPort = rootOcsp.getPort();
+        String rootRespURI = "http://localhost:" + rootOcspPort;
+        log("Root OCSP Responder URI is " + rootRespURI);
+
+        // Now that we have the root keystore and OCSP responder we can
+        // create our intermediate CA.
+        cbld.reset();
+        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
+        cbld.setPublicKey(intCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("100"));
+        // Make a 2 year validity starting from 30 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
+        end = start + TimeUnit.DAYS.toMillis(730);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        cbld.addAIAExt(Collections.singletonList(rootRespURI));
+        // Make our Intermediate CA Cert!
+        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Intermediate CA Created:\n" + certInfo(intCaCert));
+
+        // Provide intermediate CA cert revocation info to the Root CA
+        // OCSP responder.
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(intCaCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        rootOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        intKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] intChain = {intCaCert, rootCert};
+        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
+                passwd.toCharArray(), intChain);
+        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // Now fire up the Intermediate CA OCSP responder
+        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
+                INT_ALIAS, null);
+        intOcsp.enableLog(ocspDebug);
+        intOcsp.setNextUpdateInterval(3600);
+        intOcsp.start();
+        Thread.sleep(1000);
+        intOcspPort = intOcsp.getPort();
+        String intCaRespURI = "http://localhost:" + intOcspPort;
+        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
+
+        // Last but not least, let's make our SSLCert and add it to its own
+        // Keystore
+        cbld.reset();
+        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
+        cbld.setPublicKey(sslKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("4096"));
+        // Make a 1 year validity starting from 7 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
+        end = start + TimeUnit.DAYS.toMillis(365);
+        cbld.setValidity(new Date(start), new Date(end));
+
+        // Add extensions
+        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
+        boolean[] kuBits = {true, false, true, false, false, false,
+            false, false, false};
+        cbld.addKeyUsageExt(kuBits);
+        List<String> ekuOids = new ArrayList<>();
+        ekuOids.add("1.3.6.1.5.5.7.3.1");
+        ekuOids.add("1.3.6.1.5.5.7.3.2");
+        cbld.addExtendedKeyUsageExt(ekuOids);
+        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
+        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
+        // Make our SSL Server Cert!
+        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("SSL Certificate Created:\n" + certInfo(sslCert));
+
+        // Provide SSL server cert revocation info to the Intermeidate CA
+        // OCSP responder.
+        revInfo = new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        serverKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] sslChain = {sslCert, intCaCert, rootCert};
+        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
+                passwd.toCharArray(), sslChain);
+        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // And finally a Trust Store for the client
+        trustStore = keyStoreBuilder.getKeyStore();
+        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
+    }
+
+    private static void addCommonExts(CertificateBuilder cbld,
+            PublicKey subjKey, PublicKey authKey) throws IOException {
+        cbld.addSubjectKeyIdExt(subjKey);
+        cbld.addAuthorityKeyIdExt(authKey);
+    }
+
+    private static void addCommonCAExts(CertificateBuilder cbld)
+            throws IOException {
+        cbld.addBasicConstraintsExt(true, true, -1);
+        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
+        boolean[] kuBitSettings = {true, false, false, false, false, true,
+            true, false, false};
+        cbld.addKeyUsageExt(kuBitSettings);
+    }
+
+    /**
+     * Helper routine that dumps only a few cert fields rather than
+     * the whole toString() output.
+     *
+     * @param cert An X509Certificate to be displayed
+     *
+     * @return The {@link String} output of the issuer, subject and
+     * serial number
+     */
+    private static String certInfo(X509Certificate cert) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
+                append("\n");
+        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * Log a message on stdout
+     *
+     * @param message The message to log
+     */
+    private static void log(String message) {
+        if (debug) {
+            System.out.println(message);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/TEST.properties	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,1 @@
+bootclasspath.dirs=.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/TestCase.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.util.Map;
+
+public interface TestCase {
+    Map.Entry<Boolean, String> runTest();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/StatusStapling/TestUtils.java	Wed Aug 05 12:19:38 2015 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2015, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Map;
+
+public class TestUtils {
+
+    // private constructor to prevent instantiation for this utility class
+    private TestUtils() {
+        throw new AssertionError();
+    }
+
+    public static void runTests(Map<String, TestCase> testList) {
+        int testNo = 0;
+        int numberFailed = 0;
+        Map.Entry<Boolean, String> result;
+
+        System.out.println("============ Tests ============");
+        for (String testName : testList.keySet()) {
+            System.out.println("Test " + ++testNo + ": " + testName);
+            result = testList.get(testName).runTest();
+            System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL"));
+            System.out.println(" " +
+                    (result.getValue() != null ? result.getValue() : ""));
+            System.out.println("-------------------------------------------");
+            if (!result.getKey()) {
+                numberFailed++;
+            }
+        }
+
+        System.out.println("End Results: " + (testList.size() - numberFailed) +
+                " Passed" + ", " + numberFailed + " Failed.");
+        if (numberFailed > 0) {
+            throw new RuntimeException(
+                    "One or more tests failed, see test output for details");
+        }
+    }
+
+    public static void dumpBytes(byte[] data) {
+        dumpBytes(ByteBuffer.wrap(data));
+    }
+
+    public static void dumpBytes(ByteBuffer data) {
+        int i = 0;
+
+        data.mark();
+        while (data.hasRemaining()) {
+            if (i % 16 == 0 && i != 0) {
+                System.out.print("\n");
+            }
+            System.out.print(String.format("%02X ", data.get()));
+            i++;
+        }
+        System.out.print("\n");
+        data.reset();
+    }
+
+    public static void valueCheck(byte[] array1, byte[] array2) {
+        if (!Arrays.equals(array1, array2)) {
+            throw new RuntimeException("Array mismatch");
+        }
+    }
+
+    // Compares a range of bytes at specific offsets in each array
+    public static void valueCheck(byte[] array1, byte[] array2, int skip1,
+            int skip2, int length) {
+        ByteBuffer buf1 = ByteBuffer.wrap(array1);
+        ByteBuffer buf2 = ByteBuffer.wrap(array2);
+
+        // Skip past however many bytes are requested in both buffers
+        buf1.position(skip1);
+        buf2.position(skip2);
+
+        // Reset the limits on each buffer to account for the length
+        buf1.limit(buf1.position() + length);
+        buf2.limit(buf2.position() + length);
+
+        if (!buf1.equals(buf2)) {
+            throw new RuntimeException("Array range mismatch");
+        }
+    }
+
+    // Concatenate 1 or more arrays
+    public static byte[] gatherBuffers(byte[]... arrays) {
+        int totalLength = 0;
+        for (byte[] ar : arrays) {
+            totalLength += ar != null ? ar.length : 0;
+        }
+
+        byte[] resultBuf = new byte[totalLength];
+        int offset = 0;
+        for (byte[] ar : arrays) {
+            if (ar != null) {
+                System.arraycopy(ar, 0, resultBuf, offset, ar.length);
+                offset += ar.length;
+            }
+        }
+        return resultBuf;
+    }
+}
+
+