8170282: Enable ALPN parameters to be supplied during the TLS handshake

Reviewed-by: wetmore, xuelei
This commit is contained in:
Vinnie Ryan 2016-12-16 14:32:51 +00:00
parent 98cc34711b
commit bd0a13fa21
10 changed files with 579 additions and 81 deletions

View File

@ -27,6 +27,8 @@ package javax.net.ssl;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.util.List;
import java.util.function.BiFunction;
/**
@ -1332,4 +1334,89 @@ public abstract class SSLEngine {
public String getHandshakeApplicationProtocol() {
throw new UnsupportedOperationException();
}
/**
* Registers a callback function that selects an application protocol
* value for a SSL/TLS/DTLS handshake.
* The function overrides any values set using
* {@link SSLParameters#setApplicationProtocols
* SSLParameters.setApplicationProtocols} and it supports the following
* type parameters:
* <blockquote>
* <dl>
* <dt> {@code SSLEngine}
* <dd> The function's first argument allows the current {@code SSLEngine}
* to be inspected, including the handshake session and configuration
* settings.
* <dt> {@code List<String>}
* <dd> The function's second argument lists the application protocol names
* advertised by the TLS peer.
* <dt> {@code String}
* <dd> The function's result is an application protocol name, or null to
* indicate that none of the advertised names are acceptable.
* If the return value is null (no value chosen) or is a value that
* was not advertised by the peer, the underlying protocol will
* determine what action to take. (For example, ALPN will send a
* "no_application_protocol" alert and terminate the connection.)
* </dl>
* </blockquote>
*
* For example, the following call registers a callback function that
* examines the TLS handshake parameters and selects an application protocol
* name:
* <pre>{@code
* serverEngine.setHandshakeApplicationProtocolSelector(
* (serverEngine, clientProtocols) -> {
* SSLSession session = serverEngine.getHandshakeSession();
* return chooseApplicationProtocol(
* serverEngine,
* clientProtocols,
* session.getProtocol(),
* session.getCipherSuite());
* });
* }</pre>
*
* @apiNote
* This method should be called by TLS server applications before the TLS
* handshake begins. Also, this {@code SSLEngine} should be configured with
* parameters that are compatible with the application protocol selected by
* the callback function. For example, enabling a poor choice of cipher
* suites could result in no suitable application protocol.
* See {@link SSLParameters}.
*
* @implSpec
* The implementation in this class throws
* {@code UnsupportedOperationException} and performs no other action.
*
* @param selector the callback function, or null to disable the callback
* functionality.
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @since 9
*/
public void setHandshakeApplicationProtocolSelector(
BiFunction<SSLEngine, List<String>, String> selector) {
throw new UnsupportedOperationException();
}
/**
* Retrieves the callback function that selects an application protocol
* value during a SSL/TLS/DTLS handshake.
* See {@link #setHandshakeApplicationProtocolSelector
* setHandshakeApplicationProtocolSelector}
* for the function's type parameters.
*
* @implSpec
* The implementation in this class throws
* {@code UnsupportedOperationException} and performs no other action.
*
* @return the callback function, or null if none has been set.
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @since 9
*/
public BiFunction<SSLEngine, List<String>, String>
getHandshakeApplicationProtocolSelector() {
throw new UnsupportedOperationException();
}
}

View File

@ -28,6 +28,8 @@ package javax.net.ssl;
import java.io.IOException;
import java.net.*;
import java.util.List;
import java.util.function.BiFunction;
/**
* This class extends <code>Socket</code>s and provides secure
@ -742,4 +744,89 @@ public abstract class SSLSocket extends Socket
public String getHandshakeApplicationProtocol() {
throw new UnsupportedOperationException();
}
/**
* Registers a callback function that selects an application protocol
* value for a SSL/TLS/DTLS handshake.
* The function overrides any values set using
* {@link SSLParameters#setApplicationProtocols
* SSLParameters.setApplicationProtocols} and it supports the following
* type parameters:
* <blockquote>
* <dl>
* <dt> {@code SSLSocket}
* <dd> The function's first argument allows the current {@code SSLSocket}
* to be inspected, including the handshake session and configuration
* settings.
* <dt> {@code List<String>}
* <dd> The function's second argument lists the application protocol names
* advertised by the TLS peer.
* <dt> {@code String}
* <dd> The function's result is an application protocol name, or null to
* indicate that none of the advertised names are acceptable.
* If the return value is null (no value chosen) or is a value that
* was not advertised by the peer, the underlying protocol will
* determine what action to take. (For example, ALPN will send a
* "no_application_protocol" alert and terminate the connection.)
* </dl>
* </blockquote>
*
* For example, the following call registers a callback function that
* examines the TLS handshake parameters and selects an application protocol
* name:
* <pre>{@code
* serverSocket.setHandshakeApplicationProtocolSelector(
* (serverSocket, clientProtocols) -> {
* SSLSession session = serverSocket.getHandshakeSession();
* return chooseApplicationProtocol(
* serverSocket,
* clientProtocols,
* session.getProtocol(),
* session.getCipherSuite());
* });
* }</pre>
*
* @apiNote
* This method should be called by TLS server applications before the TLS
* handshake begins. Also, this {@code SSLSocket} should be configured with
* parameters that are compatible with the application protocol selected by
* the callback function. For example, enabling a poor choice of cipher
* suites could result in no suitable application protocol.
* See {@link SSLParameters}.
*
* @implSpec
* The implementation in this class throws
* {@code UnsupportedOperationException} and performs no other action.
*
* @param selector the callback function, or null to de-register.
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @since 9
*/
public void setHandshakeApplicationProtocolSelector(
BiFunction<SSLSocket, List<String>, String> selector) {
throw new UnsupportedOperationException();
}
/**
* Retrieves the callback function that selects an application protocol
* value during a SSL/TLS/DTLS handshake.
* See {@link #setHandshakeApplicationProtocolSelector
* setHandshakeApplicationProtocolSelector}
* for the function's type parameters.
*
* @implSpec
* The implementation in this class throws
* {@code UnsupportedOperationException} and performs no other action.
*
* @return the callback function, or null if none has been set.
* @throws UnsupportedOperationException if the underlying provider
* does not implement the operation.
* @since 9
*/
public BiFunction<SSLSocket, List<String>, String>
getHandshakeApplicationProtocolSelector() {
throw new UnsupportedOperationException();
}
}

View File

@ -36,6 +36,7 @@ import java.security.AlgorithmConstraints;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.util.function.BiFunction;
import javax.crypto.*;
import javax.crypto.spec.*;
@ -122,6 +123,14 @@ abstract class Handshaker {
// Negotiated ALPN value
String applicationProtocol = null;
// Application protocol callback function (for SSLEngine)
BiFunction<SSLEngine,List<String>,String>
appProtocolSelectorSSLEngine = null;
// Application protocol callback function (for SSLSocket)
BiFunction<SSLSocket,List<String>,String>
appProtocolSelectorSSLSocket = null;
// The maximum expected network packet size for SSL/TLS/DTLS records.
int maximumPacketSize = 0;
@ -500,6 +509,22 @@ abstract class Handshaker {
return applicationProtocol;
}
/**
* Sets the Application Protocol selector function for SSLEngine.
*/
void setApplicationProtocolSelectorSSLEngine(
BiFunction<SSLEngine,List<String>,String> selector) {
this.appProtocolSelectorSSLEngine = selector;
}
/**
* Sets the Application Protocol selector function for SSLSocket.
*/
void setApplicationProtocolSelectorSSLSocket(
BiFunction<SSLSocket,List<String>,String> selector) {
this.appProtocolSelectorSSLSocket = selector;
}
/**
* Sets the cipher suites preference.
*/

View File

@ -27,8 +27,9 @@ package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import java.security.*;
import java.util.*;
import java.util.function.BiFunction;
import javax.crypto.BadPaddingException;
@ -206,6 +207,10 @@ public final class SSLEngineImpl extends SSLEngine {
// The value under negotiation will be obtained from handshaker.
String applicationProtocol = null;
// Callback function that selects the application protocol value during
// the SSL/TLS handshake.
BiFunction<SSLEngine, List<String>, String> applicationProtocolSelector;
// Have we been told whether we're client or server?
private boolean serverModeSet = false;
private boolean roleIsServer;
@ -442,6 +447,8 @@ public final class SSLEngineImpl extends SSLEngine {
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
handshaker.setApplicationProtocols(applicationProtocols);
handshaker.setApplicationProtocolSelectorSSLEngine(
applicationProtocolSelector);
outputRecord.initHandshaker();
}
@ -2264,6 +2271,21 @@ public final class SSLEngineImpl extends SSLEngine {
return null;
}
@Override
public synchronized void setHandshakeApplicationProtocolSelector(
BiFunction<SSLEngine, List<String>, String> selector) {
applicationProtocolSelector = selector;
if ((handshaker != null) && !handshaker.activated()) {
handshaker.setApplicationProtocolSelectorSSLEngine(selector);
}
}
@Override
public synchronized BiFunction<SSLEngine, List<String>, String>
getHandshakeApplicationProtocolSelector() {
return this.applicationProtocolSelector;
}
/**
* Returns a printable representation of this end of the connection.
*/

View File

@ -37,6 +37,7 @@ import java.security.AlgorithmConstraints;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
@ -223,6 +224,10 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl {
// The value under negotiation will be obtained from handshaker.
String applicationProtocol = null;
// Callback function that selects the application protocol value during
// the SSL/TLS handshake.
BiFunction<SSLSocket, List<String>, String> applicationProtocolSelector;
/*
* READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
* IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
@ -1370,6 +1375,8 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl {
handshaker.setEnabledCipherSuites(enabledCipherSuites);
handshaker.setEnableSessionCreation(enableSessionCreation);
handshaker.setApplicationProtocols(applicationProtocols);
handshaker.setApplicationProtocolSelectorSSLSocket(
applicationProtocolSelector);
}
/**
@ -2658,6 +2665,21 @@ public final class SSLSocketImpl extends BaseSSLSocketImpl {
return null;
}
@Override
public synchronized void setHandshakeApplicationProtocolSelector(
BiFunction<SSLSocket, List<String>, String> selector) {
applicationProtocolSelector = selector;
if ((handshaker != null) && !handshaker.activated()) {
handshaker.setApplicationProtocolSelectorSSLSocket(selector);
}
}
@Override
public synchronized BiFunction<SSLSocket, List<String>, String>
getHandshakeApplicationProtocolSelector() {
return this.applicationProtocolSelector;
}
//
// We allocate a separate thread to deliver handshake completion
// events. This ensures that the notifications don't block the

View File

@ -34,6 +34,7 @@ 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.*;
@ -532,31 +533,39 @@ final class ServerHandshaker extends Handshaker {
ALPNExtension clientHelloALPN = (ALPNExtension)
mesg.extensions.get(ExtensionType.EXT_ALPN);
if ((clientHelloALPN != null) && (localApl.length > 0)) {
// 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));
// Intersect the requested and the locally supported,
// and save for later.
String negotiatedValue = null;
List<String> protocols = clientHelloALPN.getPeerAPs();
if (!hasAPCallback) {
if ((clientHelloALPN != null) && (localApl.length > 0)) {
// Use server preference order
for (String ap : localApl) {
if (protocols.contains(ap)) {
negotiatedValue = ap;
break;
// 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;
if (negotiatedValue == null) {
fatalSE(Alerts.alert_no_application_protocol,
new SSLHandshakeException(
"No matching ALPN values"));
}
applicationProtocol = negotiatedValue;
} else {
applicationProtocol = "";
}
} else {
applicationProtocol = "";
}
} // Otherwise, applicationProtocol will be set by the callback.
session = null; // forget about the current session
//
@ -892,8 +901,36 @@ final class ServerHandshaker extends Handshaker {
}
// Prepare the ALPN response
if (applicationProtocol != null && !applicationProtocol.isEmpty()) {
m1.extensions.add(new ALPNExtension(applicationProtocol));
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")) {

View File

@ -34,15 +34,17 @@ public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager {
static final String ERROR = "ERROR";
X509ExtendedKeyManager akm;
String expectedAP;
boolean doCheck = true;
MyX509ExtendedKeyManager(X509ExtendedKeyManager akm) {
this.akm = akm;
}
public MyX509ExtendedKeyManager(
X509ExtendedKeyManager akm, String expectedAP) {
X509ExtendedKeyManager akm, String expectedAP, boolean doCheck) {
this.akm = akm;
this.expectedAP = expectedAP;
this.doCheck = doCheck;
}
@ -104,6 +106,12 @@ public class MyX509ExtendedKeyManager extends X509ExtendedKeyManager {
private void checkALPN(String ap) {
if (!doCheck) {
System.out.println("Skipping KeyManager checks " +
"because a callback has been registered");
return;
}
if (ERROR.equals(expectedAP)) {
throw new RuntimeException("Should not reach here");
}

View File

@ -26,23 +26,53 @@
/*
* @test
* @bug 8051498 8145849
* @bug 8051498 8145849 8170282
* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
* @compile MyX509ExtendedKeyManager.java
* @run main/othervm SSLEngineAlpnTest h2 h2 h2
* @run main/othervm SSLEngineAlpnTest h2 h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest h2,http/1.1 h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest http/1.1,h2 h2,http/1.1 http/1.1
* @run main/othervm SSLEngineAlpnTest h4,h3,h2 h1,h2 h2
* @run main/othervm SSLEngineAlpnTest EMPTY h2,http/1.1 NONE
* @run main/othervm SSLEngineAlpnTest h2 EMPTY NONE
* @run main/othervm SSLEngineAlpnTest H2 h2 ERROR
* @run main/othervm SSLEngineAlpnTest h2 http/1.1 ERROR
*
* @run main/othervm SSLEngineAlpnTest h2 UNUSED h2 h2
* @run main/othervm SSLEngineAlpnTest h2 UNUSED h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
* @run main/othervm SSLEngineAlpnTest h4,h3,h2 UNUSED h1,h2 h2
* @run main/othervm SSLEngineAlpnTest EMPTY UNUSED h2,http/1.1 NONE
* @run main/othervm SSLEngineAlpnTest h2 UNUSED EMPTY NONE
* @run main/othervm SSLEngineAlpnTest H2 UNUSED h2 ERROR
* @run main/othervm SSLEngineAlpnTest h2 UNUSED http/1.1 ERROR
*
* @run main/othervm SSLEngineAlpnTest UNUSED h2 h2 h2
* @run main/othervm SSLEngineAlpnTest UNUSED h2 h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest UNUSED h2 http/1.1,h2 h2
* @run main/othervm SSLEngineAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
* @run main/othervm SSLEngineAlpnTest UNUSED EMPTY h2,http/1.1 NONE
* @run main/othervm SSLEngineAlpnTest UNUSED h2 EMPTY NONE
* @run main/othervm SSLEngineAlpnTest UNUSED H2 h2 ERROR
* @run main/othervm SSLEngineAlpnTest UNUSED h2 http/1.1 ERROR
*
* @run main/othervm SSLEngineAlpnTest h2 h2 h2 h2
* @run main/othervm SSLEngineAlpnTest H2 h2 h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
* @run main/othervm SSLEngineAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
* @run main/othervm SSLEngineAlpnTest EMPTY h2 h2 h2
* @run main/othervm SSLEngineAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
* @run main/othervm SSLEngineAlpnTest h2,http/1.1 h2 EMPTY NONE
* @run main/othervm SSLEngineAlpnTest UNUSED UNUSED http/1.1,h2 NONE
* @run main/othervm SSLEngineAlpnTest h2 h2 http/1.1 ERROR
* @run main/othervm SSLEngineAlpnTest h2,http/1.1 H2 http/1.1 ERROR
*/
/**
* A simple SSLEngine-based client/server that demonstrates the proposed API
* changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
*
* Usage:
* java SSLEngineAlpnTest <server-APs> <callback-AP> <client-APs> <result>
*
* where:
* EMPTY indicates that ALPN is disabled
* UNUSED indicates that no ALPN values are supplied (server-side only)
* ERROR indicates that an exception is expected
* NONE indicates that no ALPN is expected
*
* This example is based on our standard SSLEngineTemplate.
*
* The immediate consumer of ALPN will be HTTP/2 (RFC 7540), aka H2. The H2 IETF
@ -98,6 +128,7 @@ import javax.net.ssl.SSLEngineResult.*;
import java.io.*;
import java.security.*;
import java.nio.*;
import java.util.Arrays;
public class SSLEngineAlpnTest {
@ -117,6 +148,9 @@ public class SSLEngineAlpnTest {
*/
private static final boolean debug = false;
private static boolean hasServerAPs; // whether server APs are present
private static boolean hasCallback; // whether a callback is present
private final SSLContext sslc;
private SSLEngine clientEngine; // client Engine
@ -157,17 +191,21 @@ public class SSLEngineAlpnTest {
if (debug) {
System.setProperty("javax.net.debug", "all");
}
System.out.println("Test args: " + Arrays.toString(args));
// Validate parameters
if (args.length != 3) {
if (args.length != 4) {
throw new Exception("Invalid number of test parameters");
}
SSLEngineAlpnTest test = new SSLEngineAlpnTest(args[2]);
hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
hasCallback = !args[1].equals("UNUSED"); // is callback being used?
SSLEngineAlpnTest test = new SSLEngineAlpnTest(args[3]);
try {
test.runTest(convert(args[0]), convert(args[1]), args[2]);
test.runTest(convert(args[0]), args[1], convert(args[2]), args[3]);
} catch (SSLHandshakeException she) {
if (args[2].equals("ERROR")) {
if (args[3].equals("ERROR")) {
System.out.println("Caught the expected exception: " + she);
} else {
throw she;
@ -199,7 +237,8 @@ public class SSLEngineAlpnTest {
}
kms = new KeyManager[] { new MyX509ExtendedKeyManager(
(X509ExtendedKeyManager) kms[0], expectedAP) };
(X509ExtendedKeyManager) kms[0], expectedAP,
!hasCallback && hasServerAPs) };
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
@ -215,12 +254,15 @@ public class SSLEngineAlpnTest {
* Convert a comma-separated list into an array of strings.
*/
private static String[] convert(String list) {
String[] strings = null;
if (list.equals("UNUSED")) {
return null;
}
if (list.equals("EMPTY")) {
return new String[0];
}
String[] strings;
if (list.indexOf(',') > 0) {
strings = list.split(",");
} else {
@ -247,12 +289,12 @@ public class SSLEngineAlpnTest {
* One could easily separate these phases into separate
* sections of code.
*/
private void runTest(String[] serverAPs, String[] clientAPs,
String expectedAP) throws Exception {
private void runTest(String[] serverAPs, String callbackAP,
String[] clientAPs, String expectedAP) throws Exception {
boolean dataDone = false;
createSSLEngines(serverAPs, clientAPs);
createSSLEngines(serverAPs, callbackAP, clientAPs);
createBuffers();
SSLEngineResult clientResult; // results from client's last operation
@ -364,8 +406,8 @@ public class SSLEngineAlpnTest {
* Using the SSLContext created during object creation,
* create/configure the SSLEngines we'll use for this test.
*/
private void createSSLEngines(String[] serverAPs, String[] clientAPs)
throws Exception {
private void createSSLEngines(String[] serverAPs, String callbackAP,
String[] clientAPs) throws Exception {
/*
* Configure the serverEngine to act as a server in the SSL/TLS
* handshake. Also, require SSL client authentication.
@ -385,18 +427,42 @@ public class SSLEngineAlpnTest {
*/
String[] suites = sslp.getCipherSuites();
sslp.setCipherSuites(suites);
sslp.setApplicationProtocols(serverAPs);
if (serverAPs != null) {
sslp.setApplicationProtocols(serverAPs);
}
sslp.setUseCipherSuitesOrder(true); // Set server side order
serverEngine.setSSLParameters(sslp);
// check that no callback has been registered
if (serverEngine.getHandshakeApplicationProtocolSelector() != null) {
throw new Exception("getHandshakeApplicationProtocolSelector() " +
"should return null");
}
if (hasCallback) {
serverEngine.setHandshakeApplicationProtocolSelector(
(sslEngine, clientProtocols) -> {
return callbackAP.equals("EMPTY") ? "" : callbackAP;
});
// check that the callback can be retrieved
if (serverEngine.getHandshakeApplicationProtocolSelector()
== null) {
throw new Exception("getHandshakeApplicationProtocolSelector()"
+ " should return non-null");
}
}
/*
* Similar to above, but using client mode instead.
*/
clientEngine = sslc.createSSLEngine("client", 80);
clientEngine.setUseClientMode(true);
sslp = clientEngine.getSSLParameters();
sslp.setApplicationProtocols(clientAPs);
if (clientAPs != null) {
sslp.setApplicationProtocols(clientAPs);
}
clientEngine.setSSLParameters(sslp);
if ((clientEngine.getHandshakeApplicationProtocol() != null) ||

View File

@ -26,22 +26,61 @@
/*
* @test
* @bug 8051498 8145849 8158978
* @bug 8051498 8145849 8158978 8170282
* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
* @compile MyX509ExtendedKeyManager.java
* @run main/othervm SSLServerSocketAlpnTest h2 h2 h2
* @run main/othervm SSLServerSocketAlpnTest h2 h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 h2,http/1.1 http/1.1
* @run main/othervm SSLServerSocketAlpnTest h4,h3,h2 h1,h2 h2
* @run main/othervm SSLServerSocketAlpnTest EMPTY h2,http/1.1 NONE
* @run main/othervm SSLServerSocketAlpnTest h2 EMPTY NONE
* @run main/othervm SSLServerSocketAlpnTest H2 h2 ERROR
* @run main/othervm SSLServerSocketAlpnTest h2 http/1.1 ERROR
*
* @run main/othervm SSLServerSocketAlpnTest h2 UNUSED h2 h2
* @run main/othervm SSLServerSocketAlpnTest h2 UNUSED h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
* @run main/othervm SSLServerSocketAlpnTest h4,h3,h2 UNUSED h1,h2 h2
* @run main/othervm SSLServerSocketAlpnTest EMPTY UNUSED h2,http/1.1 NONE
* @run main/othervm SSLServerSocketAlpnTest h2 UNUSED EMPTY NONE
* @run main/othervm SSLServerSocketAlpnTest H2 UNUSED h2 ERROR
* @run main/othervm SSLServerSocketAlpnTest h2 UNUSED http/1.1 ERROR
*
* @run main/othervm SSLServerSocketAlpnTest UNUSED h2 h2 h2
* @run main/othervm SSLServerSocketAlpnTest UNUSED h2 h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest UNUSED h2 http/1.1,h2 h2
* @run main/othervm SSLServerSocketAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
* @run main/othervm SSLServerSocketAlpnTest UNUSED EMPTY h2,http/1.1 NONE
* @run main/othervm SSLServerSocketAlpnTest UNUSED h2 EMPTY NONE
* @run main/othervm SSLServerSocketAlpnTest UNUSED H2 h2 ERROR
* @run main/othervm SSLServerSocketAlpnTest UNUSED h2 http/1.1 ERROR
*
* @run main/othervm SSLServerSocketAlpnTest h2 h2 h2 h2
* @run main/othervm SSLServerSocketAlpnTest H2 h2 h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
* @run main/othervm SSLServerSocketAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
* @run main/othervm SSLServerSocketAlpnTest EMPTY h2 h2 h2
* @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
* @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 h2 EMPTY NONE
* @run main/othervm SSLServerSocketAlpnTest UNUSED UNUSED http/1.1,h2 NONE
* @run main/othervm SSLServerSocketAlpnTest h2 h2 http/1.1 ERROR
* @run main/othervm SSLServerSocketAlpnTest h2,http/1.1 H2 http/1.1 ERROR
*
* @author Brad Wetmore
*/
/**
* A simple SSLSocket-based client/server that demonstrates the proposed API
* changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
*
* Usage:
* java SSLServerSocketAlpnTest
* <server-APs> <callback-AP> <client-APs> <result>
*
* where:
* EMPTY indicates that ALPN is disabled
* UNUSED indicates that no ALPN values are supplied (server-side only)
* ERROR indicates that an exception is expected
* NONE indicates that no ALPN is expected
*
* This example is based on our standard SSLSocketTemplate.
*/
import java.io.*;
import java.security.KeyStore;
import java.util.Arrays;
import javax.net.ssl.*;
@ -73,6 +112,9 @@ public class SSLServerSocketAlpnTest {
static String trustFilename = System.getProperty("test.src", ".") + "/"
+ pathToStores + "/" + trustStoreFile;
private static boolean hasServerAPs; // whether server APs are present
private static boolean hasCallback; // whether a callback is present
/*
* SSLContext
*/
@ -89,6 +131,7 @@ public class SSLServerSocketAlpnTest {
static boolean debug = false;
static String[] serverAPs;
static String callbackAP;
static String[] clientAPs;
static String expectedAP;
@ -129,7 +172,9 @@ public class SSLServerSocketAlpnTest {
sslp.setUseCipherSuitesOrder(true); // Set server side order
// Set the ALPN selection.
sslp.setApplicationProtocols(serverAPs);
if (serverAPs != null) {
sslp.setApplicationProtocols(serverAPs);
}
sslServerSocket.setSSLParameters(sslp);
serverPort = sslServerSocket.getLocalPort();
@ -146,6 +191,25 @@ public class SSLServerSocketAlpnTest {
+ "return null before the handshake starts");
}
// check that no callback has been registered
if (sslSocket.getHandshakeApplicationProtocolSelector() != null) {
throw new Exception("getHandshakeApplicationProtocolSelector() " +
"should return null");
}
if (hasCallback) {
sslSocket.setHandshakeApplicationProtocolSelector(
(serverSocket, clientProtocols) -> {
return callbackAP.equals("EMPTY") ? "" : callbackAP;
});
// check that the callback can be retrieved
if (sslSocket.getHandshakeApplicationProtocolSelector() == null) {
throw new Exception("getHandshakeApplicationProtocolSelector()"
+ " should return non-null");
}
}
sslSocket.startHandshake();
if (sslSocket.getHandshakeApplicationProtocol() != null) {
@ -276,14 +340,19 @@ public class SSLServerSocketAlpnTest {
if (debug) {
System.setProperty("javax.net.debug", "all");
}
System.out.println("Test args: " + Arrays.toString(args));
// Validate parameters
if (args.length != 3) {
if (args.length != 4) {
throw new Exception("Invalid number of test parameters");
}
serverAPs = convert(args[0]);
clientAPs = convert(args[1]);
expectedAP = args[2];
callbackAP = args[1];
clientAPs = convert(args[2]);
expectedAP = args[3];
hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
hasCallback = !callbackAP.equals("UNUSED"); // is callback being used?
/*
* Start the tests.
@ -291,7 +360,7 @@ public class SSLServerSocketAlpnTest {
try {
new SSLServerSocketAlpnTest();
} catch (SSLHandshakeException she) {
if (args[2].equals("ERROR")) {
if (args[3].equals("ERROR")) {
System.out.println("Caught the expected exception: " + she);
} else {
throw she;
@ -322,7 +391,8 @@ public class SSLServerSocketAlpnTest {
}
kms = new KeyManager[] { new MyX509ExtendedKeyManager(
(X509ExtendedKeyManager) kms[0], expectedAP) };
(X509ExtendedKeyManager) kms[0], expectedAP,
!hasCallback && hasServerAPs) };
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustKS);
@ -338,12 +408,15 @@ public class SSLServerSocketAlpnTest {
* Convert a comma-separated list into an array of strings.
*/
private static String[] convert(String list) {
String[] strings;
if (list.equals("UNUSED")) {
return null;
}
if (list.equals("EMPTY")) {
return new String[0];
}
String[] strings;
if (list.indexOf(',') > 0) {
strings = list.split(",");
} else {

View File

@ -26,22 +26,60 @@
/*
* @test
* @bug 8051498 8145849
* @bug 8051498 8145849 8170282
* @summary JEP 244: TLS Application-Layer Protocol Negotiation Extension
* @compile MyX509ExtendedKeyManager.java
* @run main/othervm SSLSocketAlpnTest h2 h2 h2
* @run main/othervm SSLSocketAlpnTest h2 h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest h2,http/1.1 h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest http/1.1,h2 h2,http/1.1 http/1.1
* @run main/othervm SSLSocketAlpnTest h4,h3,h2 h1,h2 h2
* @run main/othervm SSLSocketAlpnTest EMPTY h2,http/1.1 NONE
* @run main/othervm SSLSocketAlpnTest h2 EMPTY NONE
* @run main/othervm SSLSocketAlpnTest H2 h2 ERROR
* @run main/othervm SSLSocketAlpnTest h2 http/1.1 ERROR
*
* @run main/othervm SSLSocketAlpnTest h2 UNUSED h2 h2
* @run main/othervm SSLSocketAlpnTest h2 UNUSED h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest h2,http/1.1 UNUSED h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest http/1.1,h2 UNUSED h2,http/1.1 http/1.1
* @run main/othervm SSLSocketAlpnTest h4,h3,h2 UNUSED h1,h2 h2
* @run main/othervm SSLSocketAlpnTest EMPTY UNUSED h2,http/1.1 NONE
* @run main/othervm SSLSocketAlpnTest h2 UNUSED EMPTY NONE
* @run main/othervm SSLSocketAlpnTest H2 UNUSED h2 ERROR
* @run main/othervm SSLSocketAlpnTest h2 UNUSED http/1.1 ERROR
*
* @run main/othervm SSLSocketAlpnTest UNUSED h2 h2 h2
* @run main/othervm SSLSocketAlpnTest UNUSED h2 h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest UNUSED h2 http/1.1,h2 h2
* @run main/othervm SSLSocketAlpnTest UNUSED http/1.1 h2,http/1.1 http/1.1
* @run main/othervm SSLSocketAlpnTest UNUSED EMPTY h2,http/1.1 NONE
* @run main/othervm SSLSocketAlpnTest UNUSED h2 EMPTY NONE
* @run main/othervm SSLSocketAlpnTest UNUSED H2 h2 ERROR
* @run main/othervm SSLSocketAlpnTest UNUSED h2 http/1.1 ERROR
*
* @run main/othervm SSLSocketAlpnTest h2 h2 h2 h2
* @run main/othervm SSLSocketAlpnTest H2 h2 h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest h2,http/1.1 http/1.1 h2,http/1.1 http/1.1
* @run main/othervm SSLSocketAlpnTest http/1.1,h2 h2 h2,http/1.1 h2
* @run main/othervm SSLSocketAlpnTest EMPTY h2 h2 h2
* @run main/othervm SSLSocketAlpnTest h2,http/1.1 EMPTY http/1.1 NONE
* @run main/othervm SSLSocketAlpnTest h2,http/1.1 h2 EMPTY NONE
* @run main/othervm SSLSocketAlpnTest UNUSED UNUSED http/1.1,h2 NONE
* @run main/othervm SSLSocketAlpnTest h2 h2 http/1.1 ERROR
* @run main/othervm SSLSocketAlpnTest h2,http/1.1 H2 http/1.1 ERROR
*
* @author Brad Wetmore
*/
/**
* A simple SSLSocket-based client/server that demonstrates the proposed API
* changes for JEP 244 in support of the TLS ALPN extension (RFC 7301).
*
* Usage:
* java SSLSocketAlpnTest <server-APs> <callback-AP> <client-APs> <result>
*
* where:
* EMPTY indicates that ALPN is disabled
* UNUSED indicates that no ALPN values are supplied (server-side only)
* ERROR indicates that an exception is expected
* NONE indicates that no ALPN is expected
*
* This example is based on our standard SSLSocketTemplate.
*/
import java.io.*;
import java.security.KeyStore;
import java.util.Arrays;
import javax.net.ssl.*;
@ -73,6 +111,9 @@ public class SSLSocketAlpnTest {
static String trustFilename = System.getProperty("test.src", ".") + "/"
+ pathToStores + "/" + trustStoreFile;
private static boolean hasServerAPs; // whether server APs are present
private static boolean hasCallback; // whether a callback is present
/*
* SSLContext
*/
@ -89,6 +130,7 @@ public class SSLSocketAlpnTest {
static boolean debug = false;
static String[] serverAPs;
static String callbackAP;
static String[] clientAPs;
static String expectedAP;
@ -136,7 +178,9 @@ public class SSLSocketAlpnTest {
sslp.setUseCipherSuitesOrder(true); // Set server side order
// Set the ALPN selection.
sslp.setApplicationProtocols(serverAPs);
if (serverAPs != null) {
sslp.setApplicationProtocols(serverAPs);
}
sslSocket.setSSLParameters(sslp);
if (sslSocket.getHandshakeApplicationProtocol() != null) {
@ -144,6 +188,24 @@ public class SSLSocketAlpnTest {
+ "return null before the handshake starts");
}
// check that no callback has been registered
if (sslSocket.getHandshakeApplicationProtocolSelector() != null) {
throw new Exception("getHandshakeApplicationProtocolSelector() " +
"should return null");
}
if (hasCallback) {
sslSocket.setHandshakeApplicationProtocolSelector(
(serverSocket, clientProtocols) -> {
return callbackAP.equals("EMPTY") ? "" : callbackAP;
});
// check that the callback can be retrieved
if (sslSocket.getHandshakeApplicationProtocolSelector() == null) {
throw new Exception("getHandshakeApplicationProtocolSelector()" + " should return non-null");
}
}
sslSocket.startHandshake();
if (sslSocket.getHandshakeApplicationProtocol() != null) {
@ -274,14 +336,19 @@ public class SSLSocketAlpnTest {
if (debug) {
System.setProperty("javax.net.debug", "all");
}
System.out.println("Test args: " + Arrays.toString(args));
// Validate parameters
if (args.length != 3) {
if (args.length != 4) {
throw new Exception("Invalid number of test parameters");
}
serverAPs = convert(args[0]);
clientAPs = convert(args[1]);
expectedAP = args[2];
callbackAP = args[1];
clientAPs = convert(args[2]);
expectedAP = args[3];
hasServerAPs = !args[0].equals("UNUSED"); // are server APs being used?
hasCallback = !callbackAP.equals("UNUSED"); // is callback being used?
/*
* Start the tests.
@ -289,7 +356,7 @@ public class SSLSocketAlpnTest {
try {
new SSLSocketAlpnTest();
} catch (SSLHandshakeException she) {
if (args[2].equals("ERROR")) {
if (args[3].equals("ERROR")) {
System.out.println("Caught the expected exception: " + she);
} else {
throw she;
@ -320,7 +387,8 @@ public class SSLSocketAlpnTest {
}
kms = new KeyManager[] { new MyX509ExtendedKeyManager(
(X509ExtendedKeyManager) kms[0], expectedAP) };
(X509ExtendedKeyManager) kms[0], expectedAP,
!hasCallback && hasServerAPs) };
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustKS);
@ -336,12 +404,15 @@ public class SSLSocketAlpnTest {
* Convert a comma-separated list into an array of strings.
*/
private static String[] convert(String list) {
String[] strings;
if (list.equals("UNUSED")) {
return null;
}
if (list.equals("EMPTY")) {
return new String[0];
}
String[] strings;
if (list.indexOf(',') > 0) {
strings = list.split(",");
} else {