--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Fri May 11 14:55:56 2018 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2344 +0,0 @@
-/*
- * Copyright (c) 1996, 2017, 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.*;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.security.*;
-import java.security.cert.*;
-import java.security.interfaces.*;
-import java.security.spec.ECParameterSpec;
-import java.math.BigInteger;
-import java.util.function.BiFunction;
-
-import javax.crypto.SecretKey;
-import javax.net.ssl.*;
-
-import sun.security.action.GetLongAction;
-import sun.security.util.KeyUtil;
-import sun.security.util.LegacyAlgorithmConstraints;
-import sun.security.action.GetPropertyAction;
-import sun.security.ssl.HandshakeMessage.*;
-import sun.security.ssl.CipherSuite.*;
-import sun.security.ssl.SignatureAndHashAlgorithm.*;
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
-
-/**
- * ServerHandshaker does the protocol handshaking from the point
- * of view of a server. It is driven asychronously by handshake messages
- * as delivered by the parent Handshaker class, and also uses
- * common functionality (e.g. key generation) that is provided there.
- *
- * @author David Brownell
- */
-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 PrivateKey privateKey;
-
- private Object serviceCreds;
-
- // flag to check for clientCertificateVerify message
- private boolean needClientVerify = false;
-
- /*
- * For exportable ciphersuites using non-exportable key sizes, we use
- * ephemeral RSA keys. We could also do anonymous RSA in the same way
- * but there are no such ciphersuites currently defined.
- */
- private PrivateKey tempPrivateKey;
- private PublicKey tempPublicKey;
-
- /*
- * For anonymous and ephemeral Diffie-Hellman key exchange, we use
- * ephemeral Diffie-Hellman keys.
- */
- private DHCrypt dh;
-
- // Helper for ECDH based key exchanges
- private ECDHCrypt ecdh;
-
- // version request by the client in its ClientHello
- // we remember it for the RSA premaster secret version check
- private ProtocolVersion clientRequestedVersion;
-
- // client supported elliptic curves
- private SupportedGroupsExtension requestedGroups;
-
- // the preferable signature algorithm used by ServerKeyExchange message
- SignatureAndHashAlgorithm preferableSignatureAlgorithm;
-
- // Flag to use smart ephemeral DH key which size matches the corresponding
- // authentication key
- private static final boolean useSmartEphemeralDHKeys;
-
- // Flag to use legacy ephemeral DH key which size is 512 bits for
- // exportable cipher suites, and 768 bits for others
- private static final boolean useLegacyEphemeralDHKeys;
-
- // The customized ephemeral DH key size for non-exportable cipher suites.
- private static final int customizedDHKeySize;
-
- // legacy algorithm constraints
- private static final AlgorithmConstraints legacyAlgorithmConstraints =
- new LegacyAlgorithmConstraints(
- LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS,
- new SSLAlgorithmDecomposer());
-
- private long statusRespTimeout;
-
- static {
- String property = GetPropertyAction
- .privilegedGetProperty("jdk.tls.ephemeralDHKeySize");
- if (property == null || property.length() == 0) {
- useLegacyEphemeralDHKeys = false;
- useSmartEphemeralDHKeys = false;
- customizedDHKeySize = -1;
- } else if ("matched".equals(property)) {
- useLegacyEphemeralDHKeys = false;
- useSmartEphemeralDHKeys = true;
- customizedDHKeySize = -1;
- } else if ("legacy".equals(property)) {
- useLegacyEphemeralDHKeys = true;
- useSmartEphemeralDHKeys = false;
- customizedDHKeySize = -1;
- } else {
- useLegacyEphemeralDHKeys = false;
- useSmartEphemeralDHKeys = false;
-
- try {
- // DH parameter generation can be extremely slow, best to
- // use one of the supported pre-computed DH parameters
- // (see DHCrypt class).
- customizedDHKeySize = Integer.parseUnsignedInt(property);
- if (customizedDHKeySize < 1024 || customizedDHKeySize > 8192 ||
- (customizedDHKeySize & 0x3f) != 0) {
- throw new IllegalArgumentException(
- "Unsupported customized DH key size: " +
- customizedDHKeySize + ". " +
- "The key size must be multiple of 64, " +
- "and can only range from 1024 to 8192 (inclusive)");
- }
- } catch (NumberFormatException nfe) {
- throw new IllegalArgumentException(
- "Invalid system property jdk.tls.ephemeralDHKeySize");
- }
- }
- }
-
- /*
- * Constructor ... use the keys found in the auth context.
- */
- ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
- ProtocolList enabledProtocols, ClientAuthType clientAuth,
- ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
- boolean secureRenegotiation,
- byte[] clientVerifyData, byte[] serverVerifyData) {
-
- super(socket, context, enabledProtocols,
- (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
- 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;
- }
-
- /*
- * Constructor ... use the keys found in the auth context.
- */
- ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
- ProtocolList enabledProtocols, ClientAuthType clientAuth,
- ProtocolVersion activeProtocolVersion,
- boolean isInitialHandshake, boolean secureRenegotiation,
- byte[] clientVerifyData, byte[] serverVerifyData,
- boolean isDTLS) {
-
- super(engine, context, enabledProtocols,
- (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
- 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;
- }
-
- /*
- * As long as handshaking has not started, we can change
- * whether client authentication is required. Otherwise,
- * we will need to wait for the next handshake.
- */
- void setClientAuth(ClientAuthType clientAuth) {
- doClientAuth = clientAuth;
- }
-
- /*
- * This routine handles all the server side handshake messages, one at
- * a time. Given the message type (and in some cases the pending cipher
- * spec) it parses the type-specific message. Then it calls a function
- * that handles that specific message.
- *
- * It updates the state machine as each message is processed, and writes
- * responses as needed using the connection in the constructor.
- */
- @Override
- void processMessage(byte type, int message_len)
- throws IOException {
-
- // check the handshake state
- handshakeState.check(type);
-
- switch (type) {
- case HandshakeMessage.ht_client_hello:
- ClientHello ch = new ClientHello(input, message_len, isDTLS);
- handshakeState.update(ch, resumingSession);
-
- /*
- * send it off for processing.
- */
- this.clientHello(ch);
- break;
-
- case HandshakeMessage.ht_certificate:
- if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
- fatalSE(Alerts.alert_unexpected_message,
- "client sent unsolicited cert chain");
- // NOTREACHED
- }
- CertificateMsg certificateMsg = new CertificateMsg(input);
- handshakeState.update(certificateMsg, resumingSession);
- this.clientCertificate(certificateMsg);
- break;
-
- case HandshakeMessage.ht_client_key_exchange:
- SecretKey preMasterSecret;
- switch (keyExchange) {
- case K_RSA:
- case K_RSA_EXPORT:
- /*
- * The client's pre-master secret is decrypted using
- * either the server's normal private RSA key, or the
- * temporary one used for non-export or signing-only
- * certificates/keys.
- */
- RSAClientKeyExchange pms = new RSAClientKeyExchange(
- protocolVersion, clientRequestedVersion,
- sslContext.getSecureRandom(), input,
- message_len, privateKey);
- handshakeState.update(pms, resumingSession);
- preMasterSecret = this.clientKeyExchange(pms);
- break;
- case K_DHE_RSA:
- case K_DHE_DSS:
- case K_DH_ANON:
- /*
- * The pre-master secret is derived using the normal
- * Diffie-Hellman calculation. Note that the main
- * protocol difference in these five flavors is in how
- * the ServerKeyExchange message was constructed!
- */
- DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
- handshakeState.update(dhcke, resumingSession);
- preMasterSecret = this.clientKeyExchange(dhcke);
- break;
- case K_ECDH_RSA:
- case K_ECDH_ECDSA:
- case K_ECDHE_RSA:
- case K_ECDHE_ECDSA:
- case K_ECDH_ANON:
- ECDHClientKeyExchange ecdhcke =
- new ECDHClientKeyExchange(input);
- handshakeState.update(ecdhcke, resumingSession);
- preMasterSecret = this.clientKeyExchange(ecdhcke);
- break;
- default:
- ClientKeyExchangeService p =
- ClientKeyExchangeService.find(keyExchange.name);
- if (p == null) {
- throw new SSLProtocolException
- ("Unrecognized key exchange: " + keyExchange);
- }
- byte[] encodedTicket = input.getBytes16();
- input.getBytes16();
- byte[] secret = input.getBytes16();
- ClientKeyExchange cke = p.createServerExchange(protocolVersion,
- clientRequestedVersion,
- sslContext.getSecureRandom(),
- encodedTicket,
- secret,
- this.getAccSE(), serviceCreds);
- handshakeState.update(cke, resumingSession);
- preMasterSecret = this.clientKeyExchange(cke);
- break;
- }
-
- //
- // All keys are calculated from the premaster secret
- // and the exchanged nonces in the same way.
- //
- calculateKeys(preMasterSecret, clientRequestedVersion);
- break;
-
- case HandshakeMessage.ht_certificate_verify:
- CertificateVerify cvm =
- new CertificateVerify(input,
- getLocalSupportedSignAlgs(), protocolVersion);
- handshakeState.update(cvm, resumingSession);
- this.clientCertificateVerify(cvm);
-
- break;
-
- case HandshakeMessage.ht_finished:
- Finished cfm =
- new Finished(protocolVersion, input, cipherSuite);
- handshakeState.update(cfm, resumingSession);
- this.clientFinished(cfm);
-
- break;
-
- default:
- throw new SSLProtocolException(
- "Illegal server handshake msg, " + type);
- }
-
- }
-
-
- /*
- * ClientHello presents the server with a bunch of options, to which the
- * server replies with a ServerHello listing the ones which this session
- * will use. If needed, it also writes its Certificate plus in some cases
- * a ServerKeyExchange message. It may also write a CertificateRequest,
- * to elicit a client certificate.
- *
- * All these messages are terminated by a ServerHelloDone message. In
- * most cases, all this can be sent in a single Record.
- */
- private void clientHello(ClientHello mesg) throws IOException {
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- // Reject client initiated renegotiation?
- //
- // If server side should reject client-initiated renegotiation,
- // send an alert_handshake_failure fatal alert, not a no_renegotiation
- // warning alert (no_renegotiation must be a warning: RFC 2246).
- // no_renegotiation might seem more natural at first, but warnings
- // are not appropriate because the sending party does not know how
- // the receiving party will behave. This state must be treated as
- // a fatal server condition.
- //
- // This will not have any impact on server initiated renegotiation.
- if (rejectClientInitiatedRenego && !isInitialHandshake &&
- !serverHelloRequested) {
- fatalSE(Alerts.alert_handshake_failure,
- "Client initiated renegotiation is not allowed");
- }
-
- // check the server name indication if required
- ServerNameExtension clientHelloSNIExt = (ServerNameExtension)
- mesg.extensions.get(ExtensionType.EXT_SERVER_NAME);
- if (!sniMatchers.isEmpty()) {
- // we do not reject client without SNI extension
- if (clientHelloSNIExt != null &&
- !clientHelloSNIExt.isMatched(sniMatchers)) {
- fatalSE(Alerts.alert_unrecognized_name,
- "Unrecognized server name indication");
- }
- }
-
- // Does the message include security renegotiation indication?
- boolean renegotiationIndicated = false;
-
- // check the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- CipherSuiteList cipherSuites = mesg.getCipherSuites();
- if (cipherSuites.contains(CipherSuite.C_SCSV)) {
- renegotiationIndicated = true;
- if (isInitialHandshake) {
- secureRenegotiation = true;
- } else {
- // abort the handshake with a fatal handshake_failure alert
- if (secureRenegotiation) {
- fatalSE(Alerts.alert_handshake_failure,
- "The SCSV is present in a secure renegotiation");
- } else {
- fatalSE(Alerts.alert_handshake_failure,
- "The SCSV is present in a insecure renegotiation");
- }
- }
- }
-
- // check the "renegotiation_info" extension
- RenegotiationInfoExtension clientHelloRI = (RenegotiationInfoExtension)
- mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO);
- if (clientHelloRI != null) {
- renegotiationIndicated = true;
- if (isInitialHandshake) {
- // verify the length of the "renegotiated_connection" field
- if (!clientHelloRI.isEmpty()) {
- // abort the handshake with a fatal handshake_failure alert
- fatalSE(Alerts.alert_handshake_failure,
- "The renegotiation_info field is not empty");
- }
-
- secureRenegotiation = true;
- } else {
- if (!secureRenegotiation) {
- // unexpected RI extension for insecure renegotiation,
- // abort the handshake with a fatal handshake_failure alert
- fatalSE(Alerts.alert_handshake_failure,
- "The renegotiation_info is present in a insecure " +
- "renegotiation");
- }
-
- // verify the client_verify_data value
- if (!MessageDigest.isEqual(clientVerifyData,
- clientHelloRI.getRenegotiatedConnection())) {
- fatalSE(Alerts.alert_handshake_failure,
- "Incorrect verify data in ClientHello " +
- "renegotiation_info message");
- }
- }
- } else if (!isInitialHandshake && secureRenegotiation) {
- // if the connection's "secure_renegotiation" flag is set to TRUE
- // and the "renegotiation_info" extension is not present, abort
- // the handshake.
- fatalSE(Alerts.alert_handshake_failure,
- "Inconsistent secure renegotiation indication");
- }
-
- // if there is no security renegotiation indication or the previous
- // handshake is insecure.
- if (!renegotiationIndicated || !secureRenegotiation) {
- if (isInitialHandshake) {
- if (!allowLegacyHelloMessages) {
- // abort the handshake with a fatal handshake_failure alert
- fatalSE(Alerts.alert_handshake_failure,
- "Failed to negotiate the use of secure renegotiation");
- }
-
- // continue with legacy ClientHello
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Warning: No renegotiation " +
- "indication in ClientHello, allow legacy ClientHello");
- }
- } else if (!allowUnsafeRenegotiation) {
- // abort the handshake
- if (activeProtocolVersion.useTLS10PlusSpec()) {
- // respond with a no_renegotiation warning
- warningSE(Alerts.alert_no_renegotiation);
-
- // invalidate the handshake so that the caller can
- // dispose this object.
- invalidated = true;
-
- // If there is still unread block in the handshake
- // input stream, it would be truncated with the disposal
- // and the next handshake message will become incomplete.
- //
- // However, according to SSL/TLS specifications, no more
- // handshake message could immediately follow ClientHello
- // or HelloRequest. But in case of any improper messages,
- // we'd better check to ensure there is no remaining bytes
- // in the handshake input stream.
- if (input.available() > 0) {
- fatalSE(Alerts.alert_unexpected_message,
- "ClientHello followed by an unexpected " +
- "handshake message");
- }
-
- return;
- } else {
- // For SSLv3, send the handshake_failure fatal error.
- // Note that SSLv3 does not define a no_renegotiation
- // alert like TLSv1. However we cannot ignore the message
- // simply, otherwise the other side was waiting for a
- // response that would never come.
- fatalSE(Alerts.alert_handshake_failure,
- "Renegotiation is not allowed");
- }
- } else { // !isInitialHandshake && allowUnsafeRenegotiation
- // continue with unsafe renegotiation.
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println(
- "Warning: continue with insecure renegotiation");
- }
- }
- }
-
- // check the "max_fragment_length" extension
- MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
- mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
- if ((maxFragLenExt != null) && (maximumPacketSize != 0)) {
- // Not yet consider the impact of IV/MAC/padding.
- int estimatedMaxFragSize = maximumPacketSize;
- if (isDTLS) {
- estimatedMaxFragSize -= DTLSRecord.headerSize;
- } else {
- estimatedMaxFragSize -= SSLRecord.headerSize;
- }
-
- if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) {
- // For better interoperability, abort the maximum fragment
- // length negotiation, rather than terminate the connection
- // with a fatal alert.
- maxFragLenExt = null;
-
- // fatalSE(Alerts.alert_illegal_parameter,
- // "Not an allowed max_fragment_length value");
- }
- }
-
- // check out the "extended_master_secret" extension
- if (useExtendedMasterSecret) {
- ExtendedMasterSecretExtension extendedMasterSecretExtension =
- (ExtendedMasterSecretExtension)mesg.extensions.get(
- ExtensionType.EXT_EXTENDED_MASTER_SECRET);
- if (extendedMasterSecretExtension != null) {
- requestedToUseEMS = true;
- } else if (mesg.protocolVersion.useTLS10PlusSpec()) {
- if (!allowLegacyMasterSecret) {
- // For full handshake, if the server receives a ClientHello
- // without the extension, it SHOULD abort the handshake if
- // it does not wish to interoperate with legacy clients.
- //
- // As if extended master extension is required for full
- // handshake, it MUST be used in abbreviated handshake too.
- fatalSE(Alerts.alert_handshake_failure,
- "Extended Master Secret extension is required");
- }
- }
- }
-
- // check the ALPN extension
- ALPNExtension clientHelloALPN = (ALPNExtension)
- mesg.extensions.get(ExtensionType.EXT_ALPN);
-
- // Use the application protocol callback when provided.
- // Otherwise use the local list of application protocols.
- boolean hasAPCallback =
- ((engine != null && appProtocolSelectorSSLEngine != null) ||
- (conn != null && appProtocolSelectorSSLSocket != null));
-
- if (!hasAPCallback) {
- if ((clientHelloALPN != null) && (localApl.length > 0)) {
-
- // Intersect the requested and the locally supported,
- // and save for later.
- String negotiatedValue = null;
- List<String> protocols = clientHelloALPN.getPeerAPs();
-
- // Use server preference order
- for (String ap : localApl) {
- if (protocols.contains(ap)) {
- negotiatedValue = ap;
- break;
- }
- }
-
- if (negotiatedValue == null) {
- fatalSE(Alerts.alert_no_application_protocol,
- new SSLHandshakeException(
- "No matching ALPN values"));
- }
- applicationProtocol = negotiatedValue;
-
- } else {
- applicationProtocol = "";
- }
- } // Otherwise, applicationProtocol will be set by the callback.
-
- session = null; // forget about the current session
- //
- // Here we go down either of two paths: (a) the fast one, where
- // the client's asked to rejoin an existing session, and the server
- // permits this; (b) the other one, where a new session is created.
- //
- if (mesg.sessionId.length() != 0) {
- // client is trying to resume a session, let's see...
-
- SSLSessionImpl previous = ((SSLSessionContextImpl)sslContext
- .engineGetServerSessionContext())
- .get(mesg.sessionId.getId());
- //
- // Check if we can use the fast path, resuming a session. We
- // can do so iff we have a valid record for that session, and
- // the cipher suite for that session was on the list which the
- // client requested, and if we're not forgetting any needed
- // authentication on the part of the client.
- //
- if (previous != null) {
- resumingSession = previous.isRejoinable();
-
- if (resumingSession) {
- ProtocolVersion oldVersion = previous.getProtocolVersion();
- // cannot resume session with different version
- if (oldVersion != mesg.protocolVersion) {
- resumingSession = false;
- }
- }
-
- if (resumingSession && useExtendedMasterSecret) {
- if (requestedToUseEMS &&
- !previous.getUseExtendedMasterSecret()) {
- // For abbreviated handshake request, If the original
- // session did not use the "extended_master_secret"
- // extension but the new ClientHello contains the
- // extension, then the server MUST NOT perform the
- // abbreviated handshake. Instead, it SHOULD continue
- // with a full handshake.
- resumingSession = false;
- } else if (!requestedToUseEMS &&
- previous.getUseExtendedMasterSecret()) {
- // For abbreviated handshake request, if the original
- // session used the "extended_master_secret" extension
- // but the new ClientHello does not contain it, the
- // server MUST abort the abbreviated handshake.
- fatalSE(Alerts.alert_handshake_failure,
- "Missing Extended Master Secret extension " +
- "on session resumption");
- } else if (!requestedToUseEMS &&
- !previous.getUseExtendedMasterSecret()) {
- // For abbreviated handshake request, if neither the
- // original session nor the new ClientHello uses the
- // extension, the server SHOULD abort the handshake.
- if (!allowLegacyResumption) {
- fatalSE(Alerts.alert_handshake_failure,
- "Missing Extended Master Secret extension " +
- "on session resumption");
- } else { // Otherwise, continue with a full handshake.
- resumingSession = false;
- }
- }
- }
-
- // cannot resume session with different server name indication
- if (resumingSession) {
- List<SNIServerName> oldServerNames =
- previous.getRequestedServerNames();
- if (clientHelloSNIExt != null) {
- if (!clientHelloSNIExt.isIdentical(oldServerNames)) {
- resumingSession = false;
- }
- } else if (!oldServerNames.isEmpty()) {
- resumingSession = false;
- }
-
- if (!resumingSession &&
- debug != null && Debug.isOn("handshake")) {
- System.out.println(
- "The requested server name indication " +
- "is not identical to the previous one");
- }
- }
-
- if (resumingSession &&
- (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
- try {
- previous.getPeerPrincipal();
- } catch (SSLPeerUnverifiedException e) {
- resumingSession = false;
- }
- }
-
- // validate subject identity
- if (resumingSession) {
- CipherSuite suite = previous.getSuite();
- ClientKeyExchangeService p =
- ClientKeyExchangeService.find(suite.keyExchange.name);
- if (p != null) {
- Principal localPrincipal = previous.getLocalPrincipal();
-
- if (p.isRelated(
- false, getAccSE(), localPrincipal)) {
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject can" +
- " provide creds for princ");
- } else {
- resumingSession = false;
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject cannot" +
- " provide creds for princ");
- }
- }
- }
-
- if (resumingSession) {
- CipherSuite suite = previous.getSuite();
- // verify that the ciphersuite from the cached session
- // is in the list of client requested ciphersuites and
- // we have it enabled
- if ((isNegotiable(suite) == false) ||
- (mesg.getCipherSuites().contains(suite) == false)) {
- resumingSession = false;
- } else {
- // everything looks ok, set the ciphersuite
- // this should be done last when we are sure we
- // will resume
- setCipherSuite(suite);
- }
- }
-
- if (resumingSession) {
- session = previous;
- if (debug != null &&
- (Debug.isOn("handshake") || Debug.isOn("session"))) {
- System.out.println("%% Resuming " + session);
- }
- }
- }
- } // else client did not try to resume
-
- // cookie exchange
- if (isDTLS && !resumingSession) {
- HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
- if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
- (!hcMgr.isValid(mesg))) {
-
- //
- // Perform cookie exchange for DTLS handshaking if no cookie
- // or the cookie is invalid in the ClientHello message.
- //
- HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
-
- if (debug != null && Debug.isOn("handshake")) {
- m0.print(System.out);
- }
-
- m0.write(output);
- handshakeState.update(m0, resumingSession);
- output.flush();
-
- return;
- }
- }
-
- /*
- * FIRST, construct the ServerHello using the options and priorities
- * from the ClientHello. Update the (pending) cipher spec as we do
- * so, and save the client's version to protect against rollback
- * attacks.
- *
- * There are a bunch of minor tasks here, and one major one: deciding
- * if the short or the full handshake sequence will be used.
- */
- ServerHello m1 = new ServerHello();
-
- clientRequestedVersion = mesg.protocolVersion;
-
- // select a proper protocol version.
- ProtocolVersion selectedVersion =
- selectProtocolVersion(clientRequestedVersion);
- if (selectedVersion == null ||
- selectedVersion.v == ProtocolVersion.SSL20Hello.v) {
- fatalSE(Alerts.alert_handshake_failure,
- "Client requested protocol " + clientRequestedVersion +
- " not enabled or not supported");
- }
-
- handshakeHash.protocolDetermined(selectedVersion);
- setVersion(selectedVersion);
-
- m1.protocolVersion = protocolVersion;
-
- //
- // random ... save client and server values for later use
- // in computing the master secret (from pre-master secret)
- // and thence the other crypto keys.
- //
- // NOTE: this use of three inputs to generating _each_ set
- // of ciphers slows things down, but it does increase the
- // security since each connection in the session can hold
- // its own authenticated (and strong) keys. One could make
- // creation of a session a rare thing...
- //
- clnt_random = mesg.clnt_random;
- svr_random = new RandomCookie(sslContext.getSecureRandom());
- m1.svr_random = svr_random;
-
- //
- // If client hasn't specified a session we can resume, start a
- // new one and choose its cipher suite and compression options.
- // Unless new session creation is disabled for this connection!
- //
- if (session == null) {
- if (!enableNewSession) {
- throw new SSLException("Client did not resume a session");
- }
-
- requestedGroups = (SupportedGroupsExtension)
- mesg.extensions.get(ExtensionType.EXT_SUPPORTED_GROUPS);
-
- // We only need to handle the "signature_algorithm" extension
- // for full handshakes and TLS 1.2 or later.
- if (protocolVersion.useTLS12PlusSpec()) {
- SignatureAlgorithmsExtension signAlgs =
- (SignatureAlgorithmsExtension)mesg.extensions.get(
- ExtensionType.EXT_SIGNATURE_ALGORITHMS);
- if (signAlgs != null) {
- Collection<SignatureAndHashAlgorithm> peerSignAlgs =
- signAlgs.getSignAlgorithms();
- if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
- throw new SSLHandshakeException(
- "No peer supported signature algorithms");
- }
-
- Collection<SignatureAndHashAlgorithm>
- supportedPeerSignAlgs =
- SignatureAndHashAlgorithm.getSupportedAlgorithms(
- algorithmConstraints, peerSignAlgs);
- if (supportedPeerSignAlgs.isEmpty()) {
- throw new SSLHandshakeException(
- "No signature and hash algorithm in common");
- }
-
- setPeerSupportedSignAlgs(supportedPeerSignAlgs);
- } // else, need to use peer implicit supported signature algs
- }
-
- session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL,
- getLocalSupportedSignAlgs(),
- sslContext.getSecureRandom(),
- getHostAddressSE(), getPortSE(),
- (requestedToUseEMS &&
- protocolVersion.useTLS10PlusSpec()));
-
- if (protocolVersion.useTLS12PlusSpec()) {
- if (peerSupportedSignAlgs != null) {
- session.setPeerSupportedSignatureAlgorithms(
- peerSupportedSignAlgs);
- } // else, we will set the implicit peer supported signature
- // algorithms in chooseCipherSuite()
- }
-
- // set the server name indication in the session
- List<SNIServerName> clientHelloSNI =
- Collections.<SNIServerName>emptyList();
- if (clientHelloSNIExt != null) {
- clientHelloSNI = clientHelloSNIExt.getServerNames();
- }
- session.setRequestedServerNames(clientHelloSNI);
-
- // set the handshake session
- setHandshakeSessionSE(session);
-
- // choose cipher suite and corresponding private key
- chooseCipherSuite(mesg);
-
- session.setSuite(cipherSuite);
- session.setLocalPrivateKey(privateKey);
-
- // chooseCompression(mesg);
-
- // set the negotiated maximum fragment in the session
- //
- // The protocol version and cipher suite have been negotiated
- // in previous processes.
- if (maxFragLenExt != null) {
- int maxFragLen = maxFragLenExt.getMaxFragLen();
-
- // More check of the requested "max_fragment_length" extension.
- if (maximumPacketSize != 0) {
- int estimatedMaxFragSize = cipherSuite.calculatePacketSize(
- maxFragLen, protocolVersion, isDTLS);
- if (estimatedMaxFragSize > maximumPacketSize) {
- // For better interoperability, abort the maximum
- // fragment length negotiation, rather than terminate
- // the connection with a fatal alert.
- maxFragLenExt = null;
-
- // fatalSE(Alerts.alert_illegal_parameter,
- // "Not an allowed max_fragment_length value");
- }
- }
-
- if (maxFragLenExt != null) {
- session.setNegotiatedMaxFragSize(maxFragLen);
- }
- }
-
- session.setMaximumPacketSize(maximumPacketSize);
- } else {
- // set the handshake session
- setHandshakeSessionSE(session);
- }
-
- if (protocolVersion.useTLS12PlusSpec()) {
- handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
- }
-
- m1.cipherSuite = cipherSuite;
- m1.sessionId = session.getSessionId();
- m1.compression_method = session.getCompression();
-
- if (secureRenegotiation) {
- // For ServerHellos that are initial handshakes, then the
- // "renegotiated_connection" field in "renegotiation_info"
- // extension is of zero length.
- //
- // For ServerHellos that are renegotiating, this field contains
- // the concatenation of client_verify_data and server_verify_data.
- //
- // Note that for initial handshakes, both the clientVerifyData
- // variable and serverVerifyData variable are of zero length.
- HelloExtension serverHelloRI = new RenegotiationInfoExtension(
- clientVerifyData, serverVerifyData);
- m1.extensions.add(serverHelloRI);
- }
-
- if (!sniMatchers.isEmpty() && clientHelloSNIExt != null) {
- // When resuming a session, the server MUST NOT include a
- // server_name extension in the server hello.
- if (!resumingSession) {
- ServerNameExtension serverHelloSNI = new ServerNameExtension();
- m1.extensions.add(serverHelloSNI);
- }
- }
-
- if ((maxFragLenExt != null) && !resumingSession) {
- // When resuming a session, the server MUST NOT include a
- // max_fragment_length extension in the server hello.
- //
- // Otherwise, use the same value as the requested extension.
- m1.extensions.add(maxFragLenExt);
- }
-
- if (session.getUseExtendedMasterSecret()) {
- m1.extensions.add(new ExtendedMasterSecretExtension());
- }
-
- StaplingParameters staplingParams = processStapling(mesg);
- if (staplingParams != null) {
- // 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 (staplingParams.statusRespExt ==
- ExtensionType.EXT_STATUS_REQUEST) {
- m1.extensions.add(new CertStatusReqExtension());
- } else if (staplingParams.statusRespExt ==
- ExtensionType.EXT_STATUS_REQUEST_V2) {
- m1.extensions.add(new CertStatusReqListV2Extension());
- }
- }
-
- // Prepare the ALPN response
- if (clientHelloALPN != null) {
- List<String> peerAPs = clientHelloALPN.getPeerAPs();
-
- // check for a callback function
- if (hasAPCallback) {
- if (conn != null) {
- applicationProtocol =
- appProtocolSelectorSSLSocket.apply(conn, peerAPs);
- } else {
- applicationProtocol =
- appProtocolSelectorSSLEngine.apply(engine, peerAPs);
- }
- }
-
- // check for no-match and that the selected name was also proposed
- // by the TLS peer
- if (applicationProtocol == null ||
- (!applicationProtocol.isEmpty() &&
- !peerAPs.contains(applicationProtocol))) {
-
- fatalSE(Alerts.alert_no_application_protocol,
- new SSLHandshakeException(
- "No matching ALPN values"));
-
- } else if (!applicationProtocol.isEmpty()) {
- m1.extensions.add(new ALPNExtension(applicationProtocol));
- }
- } else {
- // Nothing was negotiated, returned at end of the handshake
- applicationProtocol = "";
- }
-
- if (debug != null && Debug.isOn("handshake")) {
- m1.print(System.out);
- System.out.println("Cipher suite: " + session.getSuite());
- }
- m1.write(output);
- handshakeState.update(m1, resumingSession);
-
- //
- // If we are resuming a session, we finish writing handshake
- // messages right now and then finish.
- //
- if (resumingSession) {
- calculateConnectionKeys(session.getMasterSecret());
- sendChangeCipherAndFinish(false);
-
- // expecting the final ChangeCipherSpec and Finished messages
- expectingFinishFlightSE();
-
- return;
- }
-
-
- /*
- * SECOND, write the server Certificate(s) if we need to.
- *
- * NOTE: while an "anonymous RSA" mode is explicitly allowed by
- * the protocol, we can't support it since all of the SSL flavors
- * defined in the protocol spec are explicitly stated to require
- * using RSA certificates.
- */
- if (ClientKeyExchangeService.find(
- cipherSuite.keyExchange.name) != null) {
- // No external key exchange provider needs a cert now.
- } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
- if (certs == null) {
- throw new RuntimeException("no certificates");
- }
-
- CertificateMsg m2 = new CertificateMsg(certs);
-
- /*
- * Set local certs in the SSLSession, output
- * debug info, and then actually write to the client.
- */
- session.setLocalCertificates(certs);
- if (debug != null && Debug.isOn("handshake")) {
- m2.print(System.out);
- }
- m2.write(output);
- handshakeState.update(m2, resumingSession);
-
- // XXX has some side effects with OS TCP buffering,
- // leave it out for now
-
- // let client verify chain in the meantime...
- // output.flush();
- } else {
- if (certs != null) {
- throw new RuntimeException("anonymous keyexchange with certs");
- }
- }
-
- /**
- * 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 (staplingParams != null) {
- CertificateStatus csMsg = new CertificateStatus(
- staplingParams.statReqType, certs,
- staplingParams.responseMap);
- if (debug != null && Debug.isOn("handshake")) {
- csMsg.print(System.out);
- }
- csMsg.write(output);
- handshakeState.update(csMsg, resumingSession);
- }
-
- /*
- * THIRD, the ServerKeyExchange message ... iff it's needed.
- *
- * It's usually needed unless there's an encryption-capable
- * RSA cert, or a D-H cert. The notable exception is that
- * exportable ciphers used with big RSA keys need to downgrade
- * to use short RSA keys, even when the key/cert encrypts OK.
- */
-
- ServerKeyExchange m3;
- switch (keyExchange) {
- case K_RSA:
- // no server key exchange for RSA ciphersuites
- m3 = null;
- break;
- case K_RSA_EXPORT:
- if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) {
- try {
- m3 = new RSA_ServerKeyExchange(
- tempPublicKey, privateKey,
- clnt_random, svr_random,
- sslContext.getSecureRandom());
- privateKey = tempPrivateKey;
- } catch (GeneralSecurityException e) {
- m3 = null; // make compiler happy
- throw new SSLException(
- "Error generating RSA server key exchange", e);
- }
- } else {
- // RSA_EXPORT with short key, don't need ServerKeyExchange
- m3 = null;
- }
- break;
- case K_DHE_RSA:
- case K_DHE_DSS:
- try {
- m3 = new DH_ServerKeyExchange(dh,
- privateKey,
- clnt_random.random_bytes,
- svr_random.random_bytes,
- sslContext.getSecureRandom(),
- preferableSignatureAlgorithm,
- protocolVersion);
- } catch (GeneralSecurityException e) {
- m3 = null; // make compiler happy
- throw new SSLException(
- "Error generating DH server key exchange", e);
- }
- break;
- case K_DH_ANON:
- m3 = new DH_ServerKeyExchange(dh, protocolVersion);
- break;
- case K_ECDHE_RSA:
- case K_ECDHE_ECDSA:
- case K_ECDH_ANON:
- try {
- m3 = new ECDH_ServerKeyExchange(ecdh,
- privateKey,
- clnt_random.random_bytes,
- svr_random.random_bytes,
- sslContext.getSecureRandom(),
- preferableSignatureAlgorithm,
- protocolVersion);
- } catch (GeneralSecurityException e) {
- m3 = null; // make compiler happy
- throw new SSLException(
- "Error generating ECDH server key exchange", e);
- }
- break;
- case K_ECDH_RSA:
- case K_ECDH_ECDSA:
- // ServerKeyExchange not used for fixed ECDH
- m3 = null;
- break;
- default:
- ClientKeyExchangeService p =
- ClientKeyExchangeService.find(keyExchange.name);
- if (p != null) {
- // No external key exchange provider needs a cert now.
- m3 = null;
- break;
- }
- throw new RuntimeException("internal error: " + keyExchange);
- }
- if (m3 != null) {
- if (debug != null && Debug.isOn("handshake")) {
- m3.print(System.out);
- }
- m3.write(output);
- handshakeState.update(m3, resumingSession);
- }
-
- //
- // FOURTH, the CertificateRequest message. The details of
- // the message can be affected by the key exchange algorithm
- // in use. For example, certs with fixed Diffie-Hellman keys
- // are only useful with the DH_DSS and DH_RSA key exchange
- // algorithms.
- //
- // Needed only if server requires client to authenticate self.
- // Illegal for anonymous flavors, so we need to check that.
- //
- // No external key exchange provider needs a cert now.
- if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
- keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
- ClientKeyExchangeService.find(keyExchange.name) == null) {
-
- CertificateRequest m4;
- X509Certificate[] caCerts;
-
- Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
- if (protocolVersion.useTLS12PlusSpec()) {
- // We currently use all local upported signature and hash
- // algorithms. However, to minimize the computation cost
- // of requested hash algorithms, we may use a restricted
- // set of signature algorithms in the future.
- localSignAlgs = getLocalSupportedSignAlgs();
- if (localSignAlgs.isEmpty()) {
- throw new SSLHandshakeException(
- "No supported signature algorithm");
- }
-
- Set<String> localHashAlgs =
- SignatureAndHashAlgorithm.getHashAlgorithmNames(
- localSignAlgs);
- if (localHashAlgs.isEmpty()) {
- throw new SSLHandshakeException(
- "No supported signature algorithm");
- }
- }
-
- caCerts = sslContext.getX509TrustManager().getAcceptedIssuers();
- m4 = new CertificateRequest(caCerts, keyExchange,
- localSignAlgs, protocolVersion);
-
- if (debug != null && Debug.isOn("handshake")) {
- m4.print(System.out);
- }
- m4.write(output);
- handshakeState.update(m4, resumingSession);
- }
-
- /*
- * FIFTH, say ServerHelloDone.
- */
- ServerHelloDone m5 = new ServerHelloDone();
-
- if (debug != null && Debug.isOn("handshake")) {
- m5.print(System.out);
- }
- m5.write(output);
- handshakeState.update(m5, resumingSession);
-
- /*
- * Flush any buffered messages so the client will see them.
- * Ideally, all the messages above go in a single network level
- * message to the client. Without big Certificate chains, it's
- * going to be the common case.
- */
- output.flush();
- }
-
- /*
- * Choose cipher suite from among those supported by client. Sets
- * the cipherSuite and keyExchange variables.
- */
- private void chooseCipherSuite(ClientHello mesg) throws IOException {
- CipherSuiteList prefered;
- CipherSuiteList proposed;
- if (preferLocalCipherSuites) {
- prefered = getActiveCipherSuites();
- proposed = mesg.getCipherSuites();
- } else {
- prefered = mesg.getCipherSuites();
- proposed = getActiveCipherSuites();
- }
-
- List<CipherSuite> legacySuites = new ArrayList<>();
- for (CipherSuite suite : prefered.collection()) {
- if (isNegotiable(proposed, suite) == false) {
- continue;
- }
-
- if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
- if ((suite.keyExchange == K_DH_ANON) ||
- (suite.keyExchange == K_ECDH_ANON)) {
- continue;
- }
- }
-
- if (!legacyAlgorithmConstraints.permits(null, suite.name, null)) {
- legacySuites.add(suite);
- continue;
- }
-
- if (trySetCipherSuite(suite) == false) {
- continue;
- }
-
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Standard ciphersuite chosen: " + suite);
- }
- return;
- }
-
- for (CipherSuite suite : legacySuites) {
- if (trySetCipherSuite(suite)) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Legacy ciphersuite chosen: " + suite);
- }
- return;
- }
- }
-
- fatalSE(Alerts.alert_handshake_failure, "no cipher suites in common");
- }
-
- /**
- * Set the given CipherSuite, if possible. Return the result.
- * The call succeeds if the CipherSuite is available and we have
- * the necessary certificates to complete the handshake. We don't
- * check if the CipherSuite is actually enabled.
- *
- * If successful, this method also generates ephemeral keys if
- * required for this ciphersuite. This may take some time, so this
- * method should only be called if you really want to use the
- * CipherSuite.
- *
- * This method is called from chooseCipherSuite() in this class.
- */
- boolean trySetCipherSuite(CipherSuite suite) {
- /*
- * If we're resuming a session we know we can
- * support this key exchange algorithm and in fact
- * have already cached the result of it in
- * the session state.
- */
- if (resumingSession) {
- return true;
- }
-
- if (suite.isNegotiable() == false) {
- return false;
- }
-
- // must not negotiate the obsoleted weak cipher suites.
- if (protocolVersion.obsoletes(suite)) {
- return false;
- }
-
- // must not negotiate unsupported cipher suites.
- if (!protocolVersion.supports(suite)) {
- return false;
- }
-
- KeyExchange keyExchange = suite.keyExchange;
-
- // null out any existing references
- privateKey = null;
- certs = null;
- dh = null;
- tempPrivateKey = null;
- tempPublicKey = null;
-
- Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
- if (protocolVersion.useTLS12PlusSpec()) {
- if (peerSupportedSignAlgs != null) {
- supportedSignAlgs = peerSupportedSignAlgs;
- } else {
- SignatureAndHashAlgorithm algorithm = null;
-
- // we may optimize the performance
- switch (keyExchange) {
- // If the negotiated key exchange algorithm is one of
- // (RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA),
- // behave as if client had sent the value {sha1,rsa}.
- case K_RSA:
- case K_DHE_RSA:
- case K_DH_RSA:
- // case K_RSA_PSK:
- case K_ECDH_RSA:
- case K_ECDHE_RSA:
- algorithm = SignatureAndHashAlgorithm.valueOf(
- HashAlgorithm.SHA1.value,
- SignatureAlgorithm.RSA.value, 0);
- break;
- // If the negotiated key exchange algorithm is one of
- // (DHE_DSS, DH_DSS), behave as if the client had
- // sent the value {sha1,dsa}.
- case K_DHE_DSS:
- case K_DH_DSS:
- algorithm = SignatureAndHashAlgorithm.valueOf(
- HashAlgorithm.SHA1.value,
- SignatureAlgorithm.DSA.value, 0);
- break;
- // If the negotiated key exchange algorithm is one of
- // (ECDH_ECDSA, ECDHE_ECDSA), behave as if the client
- // had sent value {sha1,ecdsa}.
- case K_ECDH_ECDSA:
- case K_ECDHE_ECDSA:
- algorithm = SignatureAndHashAlgorithm.valueOf(
- HashAlgorithm.SHA1.value,
- SignatureAlgorithm.ECDSA.value, 0);
- break;
- default:
- // no peer supported signature algorithms
- }
-
- if (algorithm == null) {
- supportedSignAlgs =
- Collections.<SignatureAndHashAlgorithm>emptySet();
- } else {
- supportedSignAlgs =
- new ArrayList<SignatureAndHashAlgorithm>(1);
- supportedSignAlgs.add(algorithm);
-
- supportedSignAlgs =
- SignatureAndHashAlgorithm.getSupportedAlgorithms(
- algorithmConstraints, supportedSignAlgs);
-
- // May be no default activated signature algorithm, but
- // let the following process make the final decision.
- }
-
- // Sets the peer supported signature algorithm to use in KM
- // temporarily.
- session.setPeerSupportedSignatureAlgorithms(supportedSignAlgs);
- }
- }
-
- // The named group used for ECDHE and FFDHE.
- NamedGroup namedGroup = null;
- switch (keyExchange) {
- case K_RSA:
- // need RSA certs for authentication
- if (setupPrivateKeyAndChain("RSA") == false) {
- return false;
- }
- break;
- case K_RSA_EXPORT:
- // need RSA certs for authentication
- if (setupPrivateKeyAndChain("RSA") == false) {
- return false;
- }
-
- try {
- if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) {
- if (!setupEphemeralRSAKeys(suite.exportable)) {
- return false;
- }
- }
- } catch (RuntimeException e) {
- // could not determine keylength, ignore key
- return false;
- }
- break;
- case K_DHE_RSA:
- // Is ephemeral DH cipher suite usable for the connection?
- //
- // [RFC 7919] If a compatible TLS server receives a Supported
- // Groups extension from a client that includes any FFDHE group
- // (i.e., any codepoint between 256 and 511, inclusive, even if
- // unknown to the server), and if none of the client-proposed
- // FFDHE groups are known and acceptable to the server, then
- // the server MUST NOT select an FFDHE cipher suite. In this
- // case, the server SHOULD select an acceptable non-FFDHE cipher
- // suite from the client's offered list. If the extension is
- // present with FFDHE groups, none of the client's offered
- // groups are acceptable by the server, and none of the client's
- // proposed non-FFDHE cipher suites are acceptable to the server,
- // the server MUST end the connection with a fatal TLS alert
- // of type insufficient_security(71).
- //
- // Note: For compatibility, if an application is customized to
- // use legacy sizes (512 bits for exportable cipher suites and
- // 768 bits for others), or the cipher suite is exportable, the
- // FFDHE extension will not be used.
- if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
- (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
-
- namedGroup = requestedGroups.getPreferredGroup(
- algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
- if (namedGroup == null) {
- // no match found, cannot use this cipher suite.
- return false;
- }
- }
-
- // need RSA certs for authentication
- if (setupPrivateKeyAndChain("RSA") == false) {
- return false;
- }
-
- // get preferable peer signature algorithm for server key exchange
- if (protocolVersion.useTLS12PlusSpec()) {
- preferableSignatureAlgorithm =
- SignatureAndHashAlgorithm.getPreferableAlgorithm(
- supportedSignAlgs, "RSA", privateKey);
- if (preferableSignatureAlgorithm == null) {
- if ((debug != null) && Debug.isOn("handshake")) {
- System.out.println(
- "No signature and hash algorithm for cipher " +
- suite);
- }
- return false;
- }
- }
-
- setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey);
- break;
- case K_ECDHE_RSA:
- // Is ECDHE cipher suite usable for the connection?
- namedGroup = (requestedGroups != null) ?
- requestedGroups.getPreferredGroup(
- algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
- SupportedGroupsExtension.getPreferredECGroup(
- algorithmConstraints);
- if (namedGroup == null) {
- // no match found, cannot use this ciphersuite
- return false;
- }
-
- // need RSA certs for authentication
- if (setupPrivateKeyAndChain("RSA") == false) {
- return false;
- }
-
- // get preferable peer signature algorithm for server key exchange
- if (protocolVersion.useTLS12PlusSpec()) {
- preferableSignatureAlgorithm =
- SignatureAndHashAlgorithm.getPreferableAlgorithm(
- supportedSignAlgs, "RSA", privateKey);
- if (preferableSignatureAlgorithm == null) {
- if ((debug != null) && Debug.isOn("handshake")) {
- System.out.println(
- "No signature and hash algorithm for cipher " +
- suite);
- }
- return false;
- }
- }
-
- setupEphemeralECDHKeys(namedGroup);
- break;
- case K_DHE_DSS:
- // Is ephemeral DH cipher suite usable for the connection?
- //
- // See comment in K_DHE_RSA case.
- if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
- (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
-
- namedGroup = requestedGroups.getPreferredGroup(
- algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
- if (namedGroup == null) {
- // no match found, cannot use this cipher suite.
- return false;
- }
- }
-
- // get preferable peer signature algorithm for server key exchange
- if (protocolVersion.useTLS12PlusSpec()) {
- preferableSignatureAlgorithm =
- SignatureAndHashAlgorithm.getPreferableAlgorithm(
- supportedSignAlgs, "DSA");
- if (preferableSignatureAlgorithm == null) {
- if ((debug != null) && Debug.isOn("handshake")) {
- System.out.println(
- "No signature and hash algorithm for cipher " +
- suite);
- }
- return false;
- }
- }
-
- // need DSS certs for authentication
- if (setupPrivateKeyAndChain("DSA") == false) {
- return false;
- }
-
- setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey);
- break;
- case K_ECDHE_ECDSA:
- // Is ECDHE cipher suite usable for the connection?
- namedGroup = (requestedGroups != null) ?
- requestedGroups.getPreferredGroup(
- algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
- SupportedGroupsExtension.getPreferredECGroup(
- algorithmConstraints);
- if (namedGroup == null) {
- // no match found, cannot use this ciphersuite
- return false;
- }
-
- // get preferable peer signature algorithm for server key exchange
- if (protocolVersion.useTLS12PlusSpec()) {
- preferableSignatureAlgorithm =
- SignatureAndHashAlgorithm.getPreferableAlgorithm(
- supportedSignAlgs, "ECDSA");
- if (preferableSignatureAlgorithm == null) {
- if ((debug != null) && Debug.isOn("handshake")) {
- System.out.println(
- "No signature and hash algorithm for cipher " +
- suite);
- }
- return false;
- }
- }
-
- // need EC cert
- if (setupPrivateKeyAndChain("EC") == false) {
- return false;
- }
-
- setupEphemeralECDHKeys(namedGroup);
- break;
- case K_ECDH_RSA:
- // need EC cert
- if (setupPrivateKeyAndChain("EC") == false) {
- return false;
- }
- setupStaticECDHKeys();
- break;
- case K_ECDH_ECDSA:
- // need EC cert
- if (setupPrivateKeyAndChain("EC") == false) {
- return false;
- }
- setupStaticECDHKeys();
- break;
- case K_DH_ANON:
- // Is ephemeral DH cipher suite usable for the connection?
- //
- // See comment in K_DHE_RSA case.
- if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
- (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
- namedGroup = requestedGroups.getPreferredGroup(
- algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
- if (namedGroup == null) {
- // no match found, cannot use this cipher suite.
- return false;
- }
- }
-
- // no certs needed for anonymous
- setupEphemeralDHKeys(namedGroup, suite.exportable, null);
- break;
- case K_ECDH_ANON:
- // Is ECDHE cipher suite usable for the connection?
- namedGroup = (requestedGroups != null) ?
- requestedGroups.getPreferredGroup(
- algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
- SupportedGroupsExtension.getPreferredECGroup(
- algorithmConstraints);
- if (namedGroup == null) {
- // no match found, cannot use this ciphersuite
- return false;
- }
-
- // no certs needed for anonymous
- setupEphemeralECDHKeys(namedGroup);
- break;
- default:
- ClientKeyExchangeService p =
- ClientKeyExchangeService.find(keyExchange.name);
- if (p == null) {
- // internal error, unknown key exchange
- throw new RuntimeException(
- "Unrecognized cipherSuite: " + suite);
- }
- // need service creds
- if (serviceCreds == null) {
- AccessControlContext acc = getAccSE();
- serviceCreds = p.getServiceCreds(acc);
- if (serviceCreds != null) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Using serviceCreds");
- }
- }
- if (serviceCreds == null) {
- return false;
- }
- }
- break;
- }
- setCipherSuite(suite);
-
- // set the peer implicit supported signature algorithms
- if (protocolVersion.useTLS12PlusSpec()) {
- if (peerSupportedSignAlgs == null) {
- setPeerSupportedSignAlgs(supportedSignAlgs);
- // we had alreay update the session
- }
- }
- return true;
- }
-
- /*
- * Get some "ephemeral" RSA keys for this context. This means
- * generating them if it's not already been done.
- *
- * Note that we currently do not implement any ciphersuites that use
- * strong ephemeral RSA. (We do not support the EXPORT1024 ciphersuites
- * and standard RSA ciphersuites prohibit ephemeral mode for some reason)
- * This means that export is always true and 512 bit keys are generated.
- */
- private boolean setupEphemeralRSAKeys(boolean export) {
- KeyPair kp = sslContext.getEphemeralKeyManager().
- getRSAKeyPair(export, sslContext.getSecureRandom());
- if (kp == null) {
- return false;
- } else {
- tempPublicKey = kp.getPublic();
- tempPrivateKey = kp.getPrivate();
- return true;
- }
- }
-
- /*
- * Acquire some "ephemeral" Diffie-Hellman keys for this handshake.
- * We don't reuse these, for improved forward secrecy.
- */
- private void setupEphemeralDHKeys(
- NamedGroup namedGroup, boolean export, Key key) {
- // Are the client and server willing to negotiate FFDHE groups?
- if ((!useLegacyEphemeralDHKeys) && (!export) && (namedGroup != null)) {
- dh = new DHCrypt(namedGroup, sslContext.getSecureRandom());
-
- return;
- } // Otherwise, the client is not compatible with FFDHE extension.
-
- /*
- * 768 bits ephemeral DH private keys were used to be used in
- * ServerKeyExchange except that exportable ciphers max out at 512
- * bits modulus values. We still adhere to this behavior in legacy
- * mode (system property "jdk.tls.ephemeralDHKeySize" is defined
- * as "legacy").
- *
- * Old JDK (JDK 7 and previous) releases don't support DH keys bigger
- * than 1024 bits. We have to consider the compatibility requirement.
- * 1024 bits DH key is always used for non-exportable cipher suites
- * in default mode (system property "jdk.tls.ephemeralDHKeySize"
- * is not defined).
- *
- * However, if applications want more stronger strength, setting
- * system property "jdk.tls.ephemeralDHKeySize" to "matched"
- * is a workaround to use ephemeral DH key which size matches the
- * corresponding authentication key. For example, if the public key
- * size of an authentication certificate is 2048 bits, then the
- * ephemeral DH key size should be 2048 bits accordingly unless
- * the cipher suite is exportable. This key sizing scheme keeps
- * the cryptographic strength consistent between authentication
- * keys and key-exchange keys.
- *
- * Applications may also want to customize the ephemeral DH key size
- * to a fixed length for non-exportable cipher suites. This can be
- * approached by setting system property "jdk.tls.ephemeralDHKeySize"
- * to a valid positive integer between 1024 and 8192 bits, inclusive.
- *
- * Note that the minimum acceptable key size is 1024 bits except
- * exportable cipher suites or legacy mode.
- *
- * Note that per RFC 2246, the key size limit of DH is 512 bits for
- * exportable cipher suites. Because of the weakness, exportable
- * cipher suites are deprecated since TLS v1.1 and they are not
- * enabled by default in Oracle provider. The legacy behavior is
- * reserved and 512 bits DH key is always used for exportable
- * cipher suites.
- */
- int keySize = export ? 512 : 1024; // default mode
- if (!export) {
- if (useLegacyEphemeralDHKeys) { // legacy mode
- keySize = 768;
- } else if (useSmartEphemeralDHKeys) { // matched mode
- if (key != null) {
- int ks = KeyUtil.getKeySize(key);
-
- // DH parameter generation can be extremely slow, make
- // sure to use one of the supported pre-computed DH
- // parameters (see DHCrypt class).
- //
- // Old deployed applications may not be ready to support
- // DH key sizes bigger than 2048 bits. Please DON'T use
- // value other than 1024 and 2048 at present. May improve
- // the underlying providers and key size limit in the
- // future when the compatibility and interoperability
- // impact is limited.
- //
- // keySize = ks <= 1024 ? 1024 : (ks >= 2048 ? 2048 : ks);
- keySize = ks <= 1024 ? 1024 : 2048;
- } // Otherwise, anonymous cipher suites, 1024-bit is used.
- } else if (customizedDHKeySize > 0) { // customized mode
- keySize = customizedDHKeySize;
- }
- }
-
- dh = new DHCrypt(keySize, sslContext.getSecureRandom());
- }
-
- /**
- * Setup the ephemeral ECDH parameters.
- */
- private void setupEphemeralECDHKeys(NamedGroup namedGroup) {
- ecdh = new ECDHCrypt(namedGroup, sslContext.getSecureRandom());
- }
-
- private void setupStaticECDHKeys() {
- // don't need to check whether the curve is supported, already done
- // in setupPrivateKeyAndChain().
- ecdh = new ECDHCrypt(privateKey, certs[0].getPublicKey());
- }
-
- /**
- * Retrieve the server key and certificate for the specified algorithm
- * from the KeyManager and set the instance variables.
- *
- * @return true if successful, false if not available or invalid
- */
- private boolean setupPrivateKeyAndChain(String algorithm) {
- X509ExtendedKeyManager km = sslContext.getX509KeyManager();
- String alias;
- if (conn != null) {
- alias = km.chooseServerAlias(algorithm, null, conn);
- } else {
- alias = km.chooseEngineServerAlias(algorithm, null, engine);
- }
- if (alias == null) {
- return false;
- }
- PrivateKey tempPrivateKey = km.getPrivateKey(alias);
- if (tempPrivateKey == null) {
- return false;
- }
- X509Certificate[] tempCerts = km.getCertificateChain(alias);
- if ((tempCerts == null) || (tempCerts.length == 0)) {
- return false;
- }
- String keyAlgorithm = algorithm.split("_")[0];
- PublicKey publicKey = tempCerts[0].getPublicKey();
- if ((tempPrivateKey.getAlgorithm().equals(keyAlgorithm) == false)
- || (publicKey.getAlgorithm().equals(keyAlgorithm) == false)) {
- return false;
- }
- // For ECC certs, check whether we support the EC domain parameters.
- // If the client sent a SupportedEllipticCurves ClientHello extension,
- // check against that too.
- if (keyAlgorithm.equals("EC")) {
- if (publicKey instanceof ECPublicKey == false) {
- return false;
- }
- ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
- NamedGroup namedGroup = NamedGroup.valueOf(params);
- if ((namedGroup == null) ||
- (!SupportedGroupsExtension.supports(namedGroup)) ||
- ((requestedGroups != null) &&
- !requestedGroups.contains(namedGroup.id))) {
- return false;
- }
- }
- this.privateKey = tempPrivateKey;
- this.certs = tempCerts;
- return true;
- }
-
- /*
- * Returns premaster secret for external key exchange services.
- */
- private SecretKey clientKeyExchange(ClientKeyExchange mesg)
- throws IOException {
-
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- // Record the principals involved in exchange
- session.setPeerPrincipal(mesg.getPeerPrincipal());
- session.setLocalPrincipal(mesg.getLocalPrincipal());
-
- return mesg.clientKeyExchange();
- }
-
- /*
- * Diffie Hellman key exchange is used when the server presented
- * D-H parameters in its certificate (signed using RSA or DSS/DSA),
- * or else the server presented no certificate but sent D-H params
- * in a ServerKeyExchange message. Use of D-H is specified by the
- * cipher suite chosen.
- *
- * The message optionally contains the client's D-H public key (if
- * it wasn't not sent in a client certificate). As always with D-H,
- * if a client and a server have each other's D-H public keys and
- * they use common algorithm parameters, they have a shared key
- * that's derived via the D-H calculation. That key becomes the
- * pre-master secret.
- */
- private SecretKey clientKeyExchange(DHClientKeyExchange mesg)
- throws IOException {
-
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- BigInteger publicKeyValue = mesg.getClientPublicKey();
-
- // check algorithm constraints
- dh.checkConstraints(algorithmConstraints, publicKeyValue);
-
- return dh.getAgreedSecret(publicKeyValue, false);
- }
-
- private SecretKey clientKeyExchange(ECDHClientKeyExchange mesg)
- throws IOException {
-
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- byte[] publicPoint = mesg.getEncodedPoint();
-
- // check algorithm constraints
- ecdh.checkConstraints(algorithmConstraints, publicPoint);
-
- return ecdh.getAgreedSecret(publicPoint);
- }
-
- /*
- * Client wrote a message to verify the certificate it sent earlier.
- *
- * Note that this certificate isn't involved in key exchange. Client
- * authentication messages are included in the checksums used to
- * validate the handshake (e.g. Finished messages). Other than that,
- * the _exact_ identity of the client is less fundamental to protocol
- * security than its role in selecting keys via the pre-master secret.
- */
- private void clientCertificateVerify(CertificateVerify mesg)
- throws IOException {
-
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- if (protocolVersion.useTLS12PlusSpec()) {
- SignatureAndHashAlgorithm signAlg =
- mesg.getPreferableSignatureAlgorithm();
- if (signAlg == null) {
- throw new SSLHandshakeException(
- "Illegal CertificateVerify message");
- }
-
- String hashAlg =
- SignatureAndHashAlgorithm.getHashAlgorithmName(signAlg);
- if (hashAlg == null || hashAlg.length() == 0) {
- throw new SSLHandshakeException(
- "No supported hash algorithm");
- }
- }
-
- try {
- PublicKey publicKey =
- session.getPeerCertificates()[0].getPublicKey();
-
- boolean valid = mesg.verify(protocolVersion, handshakeHash,
- publicKey, session.getMasterSecret());
- if (valid == false) {
- fatalSE(Alerts.alert_bad_certificate,
- "certificate verify message signature error");
- }
- } catch (GeneralSecurityException e) {
- fatalSE(Alerts.alert_bad_certificate,
- "certificate verify format error", e);
- }
-
- // reset the flag for clientCertificateVerify message
- needClientVerify = false;
- }
-
-
- /*
- * Client writes "finished" at the end of its handshake, after cipher
- * spec is changed. We verify it and then send ours.
- *
- * When we're resuming a session, we'll have already sent our own
- * Finished message so just the verification is needed.
- */
- private void clientFinished(Finished mesg) throws IOException {
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- /*
- * Verify if client did send the certificate when client
- * authentication was required, otherwise server should not proceed
- */
- if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
- // get X500Principal of the end-entity certificate for X509-based
- // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc
- session.getPeerPrincipal();
- }
-
- /*
- * Verify if client did send clientCertificateVerify message following
- * the client Certificate, otherwise server should not proceed
- */
- if (needClientVerify) {
- fatalSE(Alerts.alert_handshake_failure,
- "client did not send certificate verify message");
- }
-
- /*
- * Verify the client's message with the "before" digest of messages,
- * and forget about continuing to use that digest.
- */
- boolean verified = mesg.verify(handshakeHash, Finished.CLIENT,
- session.getMasterSecret());
-
- if (!verified) {
- fatalSE(Alerts.alert_handshake_failure,
- "client 'finished' message doesn't verify");
- // NOTREACHED
- }
-
- /*
- * save client verify data for secure renegotiation
- */
- if (secureRenegotiation) {
- clientVerifyData = mesg.getVerifyData();
- }
-
- /*
- * OK, it verified. If we're doing the full handshake, add that
- * "Finished" message to the hash of handshake messages, then send
- * the change_cipher_spec and Finished message.
- */
- if (!resumingSession) {
- sendChangeCipherAndFinish(true);
- } else {
- handshakeFinished = true;
- }
-
- /*
- * Update the session cache only after the handshake completed, else
- * we're open to an attack against a partially completed handshake.
- */
- session.setLastAccessedTime(System.currentTimeMillis());
- if (!resumingSession && session.isRejoinable()) {
- ((SSLSessionContextImpl)sslContext.engineGetServerSessionContext())
- .put(session);
- if (debug != null && Debug.isOn("session")) {
- System.out.println(
- "%% Cached server session: " + session);
- }
- } else if (!resumingSession &&
- debug != null && Debug.isOn("session")) {
- System.out.println(
- "%% Didn't cache non-resumable server session: "
- + session);
- }
- }
-
- /*
- * Compute finished message with the "server" digest (and then forget
- * about that digest, it can't be used again).
- */
- private void sendChangeCipherAndFinish(boolean finishedTag)
- throws IOException {
-
- // Reload if this message has been reserved.
- handshakeHash.reload();
-
- Finished mesg = new Finished(protocolVersion, handshakeHash,
- Finished.SERVER, session.getMasterSecret(), cipherSuite);
-
- /*
- * Send the change_cipher_spec record; then our Finished handshake
- * message will be the last handshake message. Flush, and now we
- * are ready for application data!!
- */
- sendChangeCipherSpec(mesg, finishedTag);
-
- /*
- * save server verify data for secure renegotiation
- */
- if (secureRenegotiation) {
- serverVerifyData = mesg.getVerifyData();
- }
- }
-
-
- /*
- * Returns a HelloRequest message to kickstart renegotiations
- */
- @Override
- HandshakeMessage getKickstartMessage() {
- return new HelloRequest();
- }
-
-
- /*
- * Fault detected during handshake.
- */
- @Override
- void handshakeAlert(byte description) throws SSLProtocolException {
-
- String message = Alerts.alertDescription(description);
-
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("SSL -- handshake alert: "
- + message);
- }
-
- /*
- * It's ok to get a no_certificate alert from a client of which
- * we *requested* authentication information.
- * However, if we *required* it, then this is not acceptable.
- *
- * Anyone calling getPeerCertificates() on the
- * session will get an SSLPeerUnverifiedException.
- */
- if ((description == Alerts.alert_no_certificate) &&
- (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
- return;
- }
-
- throw new SSLProtocolException("handshake alert: " + message);
- }
-
- /*
- * RSA key exchange is normally used. The client encrypts a "pre-master
- * secret" with the server's public key, from the Certificate (or else
- * ServerKeyExchange) message that was sent to it by the server. That's
- * decrypted using the private key before we get here.
- */
- private SecretKey clientKeyExchange(RSAClientKeyExchange mesg)
- throws IOException {
-
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
- return mesg.preMaster;
- }
-
- /*
- * Verify the certificate sent by the client. We'll only get one if we
- * sent a CertificateRequest to request client authentication. If we
- * are in TLS mode, the client may send a message with no certificates
- * to indicate it does not have an appropriate chain. (In SSLv3 mode,
- * it would send a no certificate alert).
- */
- private void clientCertificate(CertificateMsg mesg) throws IOException {
- if (debug != null && Debug.isOn("handshake")) {
- mesg.print(System.out);
- }
-
- X509Certificate[] peerCerts = mesg.getCertificateChain();
-
- if (peerCerts.length == 0) {
- /*
- * If the client authentication is only *REQUESTED* (e.g.
- * not *REQUIRED*, this is an acceptable condition.)
- */
- if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
- return;
- } else {
- fatalSE(Alerts.alert_bad_certificate,
- "null cert chain");
- }
- }
-
- // ask the trust manager to verify the chain
- X509TrustManager tm = sslContext.getX509TrustManager();
-
- try {
- // find out the types of client authentication used
- PublicKey key = peerCerts[0].getPublicKey();
- String keyAlgorithm = key.getAlgorithm();
- String authType;
- if (keyAlgorithm.equals("RSA")) {
- authType = "RSA";
- } else if (keyAlgorithm.equals("DSA")) {
- authType = "DSA";
- } else if (keyAlgorithm.equals("EC")) {
- authType = "EC";
- } else {
- // unknown public key type
- authType = "UNKNOWN";
- }
-
- if (tm instanceof X509ExtendedTrustManager) {
- if (conn != null) {
- ((X509ExtendedTrustManager)tm).checkClientTrusted(
- peerCerts.clone(),
- authType,
- conn);
- } else {
- ((X509ExtendedTrustManager)tm).checkClientTrusted(
- peerCerts.clone(),
- authType,
- 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);
- }
- // set the flag for clientCertificateVerify message
- needClientVerify = true;
-
- session.setPeerCertificates(peerCerts);
- }
-
- private StaplingParameters processStapling(ClientHello mesg) {
- StaplingParameters params = null;
- ExtensionType ext = null;
- StatusRequestType type = null;
- StatusRequest req = null;
- Map<X509Certificate, byte[]> responses;
-
- // If this feature has not been enabled, then no more processing
- // is necessary. Also we will only staple if we're doing a full
- // handshake.
- if (!sslContext.isStaplingEnabled(false) || resumingSession) {
- return null;
- }
-
- // 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);
-
- // 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. Also since we don't support ResponderId
- // selection yet, only accept a request if the ResponderId field
- // is empty.
- if (statReqExtV2 != null) { // RFC 6961 stapling
- ext = ExtensionType.EXT_STATUS_REQUEST_V2;
- List<CertStatusReqItemV2> reqItems =
- statReqExtV2.getRequestItems();
- int ocspIdx = -1;
- int ocspMultiIdx = -1;
- for (int pos = 0; (pos < reqItems.size() &&
- (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) {
- CertStatusReqItemV2 item = reqItems.get(pos);
- StatusRequestType curType = item.getType();
- if (ocspIdx < 0 && curType == StatusRequestType.OCSP) {
- OCSPStatusRequest ocspReq =
- (OCSPStatusRequest)item.getRequest();
- if (ocspReq.getResponderIds().isEmpty()) {
- ocspIdx = pos;
- }
- } else if (ocspMultiIdx < 0 &&
- curType == StatusRequestType.OCSP_MULTI) {
- // If the type is OCSP, then the request
- // is guaranteed to be OCSPStatusRequest
- OCSPStatusRequest ocspReq =
- (OCSPStatusRequest)item.getRequest();
- if (ocspReq.getResponderIds().isEmpty()) {
- ocspMultiIdx = pos;
- }
- }
- }
- if (ocspMultiIdx >= 0) {
- type = reqItems.get(ocspMultiIdx).getType();
- req = reqItems.get(ocspMultiIdx).getRequest();
- } else if (ocspIdx >= 0) {
- type = reqItems.get(ocspIdx).getType();
- req = reqItems.get(ocspIdx).getRequest();
- } else {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Warning: No suitable request " +
- "found in the status_request_v2 extension.");
- }
- }
- }
-
- // Only attempt to process a status_request extension if:
- // * The status_request extension is set AND
- // * either the status_request_v2 extension is not present OR
- // * none of the underlying OCSPStatusRequest structures is suitable
- // for stapling.
- // If either of the latter two bullet items is true the ext, type and
- // req variables should all be null. If any are null we will try
- // processing an asserted status_request.
- if ((statReqExt != null) &&
- (ext == null || type == null || req == null)) {
- ext = ExtensionType.EXT_STATUS_REQUEST;
- type = statReqExt.getType();
- if (type == StatusRequestType.OCSP) {
- // If the type is OCSP, then the request is guaranteed
- // to be OCSPStatusRequest
- OCSPStatusRequest ocspReq =
- (OCSPStatusRequest)statReqExt.getRequest();
- if (ocspReq.getResponderIds().isEmpty()) {
- req = ocspReq;
- } else {
- if (debug != null && Debug.isOn("handshake")) {
- req = null;
- System.out.println("Warning: No suitable request " +
- "found in the status_request extension.");
- }
- }
- }
- }
-
- // If, after walking through the extensions we were unable to
- // find a suitable StatusRequest, then stapling is disabled.
- // The ext, type and req variables must have been set to continue.
- if (type == null || req == null || ext == null) {
- return null;
- }
-
- // Get the OCSP responses from the StatusResponseManager
- StatusResponseManager statRespMgr =
- sslContext.getStatusResponseManager();
- if (statRespMgr != null) {
- responses = statRespMgr.get(type, req, certs, statusRespTimeout,
- TimeUnit.MILLISECONDS);
- if (!responses.isEmpty()) {
- // If this RFC 6066-style stapling (SSL cert only) then the
- // response cannot be zero length
- if (type == StatusRequestType.OCSP) {
- byte[] respDER = responses.get(certs[0]);
- if (respDER == null || respDER.length <= 0) {
- return null;
- }
- }
- params = new StaplingParameters(ext, type, req, responses);
- }
- } else {
- // This should not happen, but if lazy initialization of the
- // StatusResponseManager doesn't occur we should turn off stapling.
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Warning: lazy initialization " +
- "of the StatusResponseManager failed. " +
- "Stapling has been disabled.");
- }
- }
-
- return params;
- }
-
- /**
- * Inner class used to hold stapling parameters needed by the handshaker
- * when stapling is active.
- */
- private class StaplingParameters {
- private final ExtensionType statusRespExt;
- private final StatusRequestType statReqType;
- private final StatusRequest statReqData;
- private final Map<X509Certificate, byte[]> responseMap;
-
- StaplingParameters(ExtensionType ext, StatusRequestType type,
- StatusRequest req, Map<X509Certificate, byte[]> responses) {
- statusRespExt = ext;
- statReqType = type;
- statReqData = req;
- responseMap = responses;
- }
- }
-}