Mercurial > hg > openjdk > jdk8u > jdk
changeset 14140:896bbc5499ff
8245473: OCSP stapling support
Reviewed-by: mbalao
author | abakhtin |
---|---|
date | Tue, 25 Aug 2020 08:30:00 -0700 |
parents | b58fdaa80b5a |
children | 80cdee1da353 |
files | src/share/classes/sun/security/provider/certpath/OCSPNonceExtension.java src/share/classes/sun/security/ssl/SSLContextImpl.java src/share/classes/sun/security/ssl/SSLSessionImpl.java src/share/classes/sun/security/ssl/X509TrustManagerImpl.java src/share/classes/sun/security/validator/PKIXValidator.java src/share/classes/sun/security/validator/SimpleValidator.java src/share/classes/sun/security/validator/Validator.java |
diffstat | 7 files changed, 272 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/security/provider/certpath/OCSPNonceExtension.java Tue Aug 25 08:30:00 2020 -0700 @@ -0,0 +1,179 @@ +/* + * 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.util.Objects; +import java.security.SecureRandom; + +import sun.security.x509.Extension; +import sun.security.x509.PKIXExtensions; +import sun.security.util.Debug; +import sun.security.util.DerValue; + +/** + * 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 + */ +public final class OCSPNonceExtension extends Extension { + + /** + * Attribute name. + */ + private static final String EXTENSION_NAME = "OCSPNonce"; + private byte[] nonceData = null; + + /** + * Create an {@code OCSPNonceExtension} by providing the nonce length. + * The criticality is set to false, and the OID for the extension will + * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960. + * + * @param length the number of random bytes composing the nonce + * + * @throws IOException if any errors happen during encoding of the + * extension. + * @throws IllegalArgumentException if length is not a positive integer. + */ + public OCSPNonceExtension(int length) throws IOException { + this(false, length); + } + + /** + * Create an {@code OCSPNonceExtension} by providing the nonce length and + * criticality setting. The OID for the extension will + * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960. + * + * @param isCritical a boolean flag indicating whether the criticality bit + * is set for this extension + * @param length the number of random bytes composing the nonce + * + * @throws IOException if any errors happen during encoding of the + * extension. + * @throws IllegalArgumentException if length is not a positive integer. + */ + public OCSPNonceExtension(boolean isCritical, int length) + throws IOException { + this.extensionId = PKIXExtensions.OCSPNonce_Id; + this.critical = isCritical; + + if (length > 0) { + SecureRandom rng = new SecureRandom(); + this.nonceData = new byte[length]; + rng.nextBytes(nonceData); + this.extensionValue = new DerValue(DerValue.tag_OctetString, + nonceData).toByteArray(); + } else { + throw new IllegalArgumentException( + "Length must be a positive integer"); + } + } + + /** + * Create an {@code OCSPNonceExtension} by providing a nonce value. + * The criticality is set to false, and the OID for the extension will + * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960. + * + * @param incomingNonce The nonce data to be set for the extension. This + * must be a non-null array of at least one byte long. + * + * @throws IOException if any errors happen during encoding of the + * extension. + * @throws IllegalArgumentException if the incomingNonce length is not a + * positive integer. + * @throws NullPointerException if the incomingNonce is null. + */ + public OCSPNonceExtension(byte[] incomingNonce) throws IOException { + this(false, incomingNonce); + } + + /** + * Create an {@code OCSPNonceExtension} by providing a nonce value and + * criticality setting. The OID for the extension will + * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960. + * + * @param isCritical a boolean flag indicating whether the criticality bit + * is set for this extension + * @param incomingNonce The nonce data to be set for the extension. This + * must be a non-null array of at least one byte long. + * + * @throws IOException if any errors happen during encoding of the + * extension. + * @throws IllegalArgumentException if the incomingNonce length is not a + * positive integer. + * @throws NullPointerException if the incomingNonce is null. + */ + public OCSPNonceExtension(boolean isCritical, byte[] incomingNonce) + throws IOException { + this.extensionId = PKIXExtensions.OCSPNonce_Id; + this.critical = isCritical; + + Objects.requireNonNull(incomingNonce, "Nonce data must be non-null"); + if (incomingNonce.length > 0) { + this.nonceData = incomingNonce.clone(); + this.extensionValue = new DerValue(DerValue.tag_OctetString, + nonceData).toByteArray(); + } else { + throw new IllegalArgumentException( + "Nonce data must be at least 1 byte in length"); + } + } + + /** + * Return the nonce bytes themselves, without any DER encoding. + * + * @return A copy of the underlying nonce bytes + */ + public byte[] getNonceValue() { + return nonceData.clone(); + } + + /** + * Returns a printable representation of the {@code OCSPNonceExtension}. + * + * @return a string representation of the extension. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(EXTENSION_NAME).append(": "); + sb.append((nonceData == null) ? "" : Debug.toString(nonceData)); + sb.append("\n"); + return sb.toString(); + } + + /** + * Return the name of the extension as a {@code String} + * + * @return the name of the extension + */ + public String getName() { + return EXTENSION_NAME; + } +}
--- a/src/share/classes/sun/security/ssl/SSLContextImpl.java Tue Aug 25 09:23:47 2020 +0300 +++ b/src/share/classes/sun/security/ssl/SSLContextImpl.java Tue Aug 25 08:30:00 2020 -0700 @@ -59,7 +59,7 @@ private volatile HelloCookieManager.Builder helloCookieManagerBuilder; private final boolean clientEnableStapling = Utilities.getBooleanProperty( - "jdk.tls.client.enableStatusRequestExtension", true); + "jdk.tls.client.enableStatusRequestExtension", false); private final boolean serverEnableStapling = Utilities.getBooleanProperty( "jdk.tls.server.enableStatusRequestExtension", false); private static final Collection<CipherSuite> clientCustomizedCipherSuites =
--- a/src/share/classes/sun/security/ssl/SSLSessionImpl.java Tue Aug 25 09:23:47 2020 +0300 +++ b/src/share/classes/sun/security/ssl/SSLSessionImpl.java Tue Aug 25 08:30:00 2020 -0700 @@ -656,7 +656,6 @@ * 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();
--- a/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java Tue Aug 25 09:23:47 2020 +0300 +++ b/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java Tue Aug 25 08:30:00 2020 -0700 @@ -215,9 +215,10 @@ // Grab any stapled OCSP responses for use in validation List<byte[]> responseList = Collections.emptyList(); - if (!checkClientTrusted && isExtSession) { + if (!checkClientTrusted && isExtSession && + session instanceof SSLSessionImpl) { responseList = - ((ExtendedSSLSession)session).getStatusResponses(); + ((SSLSessionImpl)session).getStatusResponses(); } trustedChain = validate(v, chain, responseList, constraints, checkClientTrusted ? null : authType); @@ -269,9 +270,10 @@ // Grab any stapled OCSP responses for use in validation List<byte[]> responseList = Collections.emptyList(); - if (!checkClientTrusted && isExtSession) { + if (!checkClientTrusted && isExtSession && + session instanceof SSLSessionImpl) { responseList = - ((ExtendedSSLSession)session).getStatusResponses(); + ((SSLSessionImpl)session).getStatusResponses(); } trustedChain = validate(v, chain, responseList, constraints, checkClientTrusted ? null : authType);
--- a/src/share/classes/sun/security/validator/PKIXValidator.java Tue Aug 25 09:23:47 2020 +0300 +++ b/src/share/classes/sun/security/validator/PKIXValidator.java Tue Aug 25 08:30:00 2020 -0700 @@ -215,6 +215,7 @@ @Override X509Certificate[] engineValidate(X509Certificate[] chain, Collection<X509Certificate> otherCerts, + List<byte[]> responseList, AlgorithmConstraints constraints, Object parameter) throws CertificateException { if ((chain == null) || (chain.length == 0)) { @@ -241,6 +242,11 @@ new AlgorithmChecker(constraints, null, variant)); } + // attach it to the PKIXBuilderParameters. + if (!responseList.isEmpty()) { + addResponses(pkixParameters, chain, responseList); + } + if (TRY_VALIDATOR) { // check that chain is in correct order and check if chain contains // trust anchor @@ -450,4 +456,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/src/share/classes/sun/security/validator/SimpleValidator.java Tue Aug 25 09:23:47 2020 +0300 +++ b/src/share/classes/sun/security/validator/SimpleValidator.java Tue Aug 25 08:30:00 2020 -0700 @@ -123,6 +123,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/src/share/classes/sun/security/validator/Validator.java Tue Aug 25 09:23:47 2020 +0300 +++ b/src/share/classes/sun/security/validator/Validator.java Tue Aug 25 08:30:00 2020 -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;