Add unified NextProtoNego extension support to SslContext
Motivation: - OpenSslEngine and JDK SSLEngine (+ Jetty NPN) have different APIs to support NextProtoNego extension. - It is impossible to configure NPN with SslContext when the provider type is JDK. Modification: - Implement NextProtoNego extension by overriding the behavior of SSLSession.getProtocol() for both OpenSSLEngine and JDK SSLEngine. - SSLEngine.getProtocol() returns a string delimited by a colon (':') where the first component is the transport protosol (e.g. TLSv1.2) and the second component is the name of the application protocol - Remove the direct reference of Jetty NPN classes from the examples - Add SslContext.newApplicationProtocolSelector Result: - A user can now use both JDK SSLEngine and OpenSslEngine for NPN-based protocols such as HTTP2 and SPDY
This commit is contained in:
parent
d4f2488eac
commit
861ed1e7ad
@ -27,8 +27,8 @@ import io.netty.handler.codec.http.HttpHeaders;
|
|||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
import io.netty.handler.ssl.SslProvider;
|
|
||||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -60,7 +60,13 @@ public class SpdyClient {
|
|||||||
private EventLoopGroup workerGroup;
|
private EventLoopGroup workerGroup;
|
||||||
|
|
||||||
public SpdyClient(String host, int port) throws SSLException {
|
public SpdyClient(String host, int port) throws SSLException {
|
||||||
sslCtx = SslContext.newClientContext(SslProvider.JDK, InsecureTrustManagerFactory.INSTANCE);
|
sslCtx = SslContext.newClientContext(
|
||||||
|
null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||||
|
SslContext.newApplicationProtocolSelector(
|
||||||
|
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||||
|
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||||
|
0, 0);
|
||||||
|
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
httpResponseHandler = new HttpResponseClientHandler();
|
httpResponseHandler = new HttpResponseClientHandler();
|
||||||
|
@ -23,10 +23,6 @@ import io.netty.handler.codec.spdy.SpdyHttpDecoder;
|
|||||||
import io.netty.handler.codec.spdy.SpdyHttpEncoder;
|
import io.netty.handler.codec.spdy.SpdyHttpEncoder;
|
||||||
import io.netty.handler.codec.spdy.SpdySessionHandler;
|
import io.netty.handler.codec.spdy.SpdySessionHandler;
|
||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
import io.netty.handler.ssl.SslHandler;
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
|
|
||||||
import static io.netty.handler.codec.spdy.SpdyVersion.*;
|
import static io.netty.handler.codec.spdy.SpdyVersion.*;
|
||||||
import static io.netty.util.internal.logging.InternalLogLevel.*;
|
import static io.netty.util.internal.logging.InternalLogLevel.*;
|
||||||
@ -45,14 +41,8 @@ public class SpdyClientInitializer extends ChannelInitializer<SocketChannel> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
|
|
||||||
SSLEngine engine = sslHandler.engine();
|
|
||||||
NextProtoNego.put(engine, new SpdyClientProvider());
|
|
||||||
NextProtoNego.debug = true;
|
|
||||||
|
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
||||||
pipeline.addLast("ssl", sslHandler);
|
|
||||||
pipeline.addLast("spdyFrameCodec", new SpdyFrameCodec(SPDY_3_1));
|
pipeline.addLast("spdyFrameCodec", new SpdyFrameCodec(SPDY_3_1));
|
||||||
pipeline.addLast("spdyFrameLogger", new SpdyFrameLogger(INFO));
|
pipeline.addLast("spdyFrameLogger", new SpdyFrameLogger(INFO));
|
||||||
pipeline.addLast("spdySessionHandler", new SpdySessionHandler(SPDY_3_1, false));
|
pipeline.addLast("spdySessionHandler", new SpdySessionHandler(SPDY_3_1, false));
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014 The Netty Project
|
|
||||||
*
|
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at:
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package io.netty.example.spdy.client;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Jetty project provides an implementation of the Transport Layer Security (TLS) extension for Next Protocol
|
|
||||||
* Negotiation (NPN) for OpenJDK 7 or greater. NPN allows the application layer to negotiate which protocol to use
|
|
||||||
* over the secure connection.
|
|
||||||
* <p>
|
|
||||||
* This NPN service provider negotiates using SPDY.
|
|
||||||
* <p>
|
|
||||||
* To enable NPN support, start the JVM with: {@code java -Xbootclasspath/p:<path_to_npn_boot_jar> ...}. The
|
|
||||||
* "path_to_npn_boot_jar" is the path on the file system for the NPN Boot Jar file which can be downloaded from Maven
|
|
||||||
* at coordinates org.mortbay.jetty.npn:npn-boot. Different versions applies to different OpenJDK versions.
|
|
||||||
*
|
|
||||||
* @see <a href="http://www.eclipse.org/jetty/documentation/current/npn-chapter.html">Jetty documentation</a>
|
|
||||||
*/
|
|
||||||
public class SpdyClientProvider implements ClientProvider {
|
|
||||||
|
|
||||||
private String selectedProtocol;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String selectProtocol(List<String> protocols) {
|
|
||||||
if (protocols.contains(SPDY_3_1.protocolName())) {
|
|
||||||
return SPDY_3_1.protocolName();
|
|
||||||
}
|
|
||||||
return selectedProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unsupported() {
|
|
||||||
selectedProtocol = HTTP_1_1.protocolName();
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,6 @@ package io.netty.example.spdy.server;
|
|||||||
|
|
||||||
import io.netty.channel.ChannelInboundHandler;
|
import io.netty.channel.ChannelInboundHandler;
|
||||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser;
|
import io.netty.handler.codec.spdy.SpdyOrHttpChooser;
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -41,8 +40,8 @@ public class SpdyOrHttpHandler extends SpdyOrHttpChooser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SelectedProtocol getProtocol(SSLEngine engine) {
|
protected SelectedProtocol getProtocol(SSLEngine engine) {
|
||||||
SpdyServerProvider provider = (SpdyServerProvider) NextProtoNego.get(engine);
|
String[] protocol = engine.getSession().getProtocol().split(":");
|
||||||
SelectedProtocol selectedProtocol = provider.getSelectedProtocol();
|
SelectedProtocol selectedProtocol = SelectedProtocol.protocol(protocol[1]);
|
||||||
|
|
||||||
logger.info("Selected Protocol is " + selectedProtocol);
|
logger.info("Selected Protocol is " + selectedProtocol);
|
||||||
return selectedProtocol;
|
return selectedProtocol;
|
||||||
|
@ -21,10 +21,12 @@ import io.netty.channel.ChannelOption;
|
|||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
import io.netty.handler.ssl.SslProvider;
|
|
||||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SPDY Server that responds to a GET request with a Hello World.
|
* A SPDY Server that responds to a GET request with a Hello World.
|
||||||
* <p>
|
* <p>
|
||||||
@ -72,7 +74,6 @@ public class SpdyServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
checkForNpnSupport();
|
|
||||||
int port;
|
int port;
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
port = Integer.parseInt(args[0]);
|
port = Integer.parseInt(args[0]);
|
||||||
@ -86,21 +87,13 @@ public class SpdyServer {
|
|||||||
|
|
||||||
// Configure SSL.
|
// Configure SSL.
|
||||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||||
SslContext sslCtx = SslContext.newServerContext(SslProvider.JDK, ssc.certificate(), ssc.privateKey());
|
SslContext sslCtx = SslContext.newServerContext(
|
||||||
|
ssc.certificate(), ssc.privateKey(), null, null,
|
||||||
|
Arrays.asList(
|
||||||
|
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||||
|
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||||
|
0, 0);
|
||||||
|
|
||||||
new SpdyServer(sslCtx, port).run();
|
new SpdyServer(sslCtx, port).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkForNpnSupport() {
|
|
||||||
try {
|
|
||||||
Class.forName("sun.security.ssl.NextProtoNegoExtension");
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
System.err.println();
|
|
||||||
System.err.println("Could not locate Next Protocol Negotiation (NPN) implementation.");
|
|
||||||
System.err.println("The NPN jar should have been made available when building the examples with maven.");
|
|
||||||
System.err.println("Please check that your JDK is among those supported by Jetty-NPN:");
|
|
||||||
System.err.println("http://wiki.eclipse.org/Jetty/Feature/NPN#Versions");
|
|
||||||
System.err.println();
|
|
||||||
throw new IllegalStateException("Could not locate NPN implementation. See console err for details.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,6 @@ import io.netty.channel.ChannelInitializer;
|
|||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
import io.netty.handler.ssl.SslHandler;
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the Netty pipeline
|
* Sets up the Netty pipeline
|
||||||
@ -38,15 +34,7 @@ public class SpdyServerInitializer extends ChannelInitializer<SocketChannel> {
|
|||||||
@Override
|
@Override
|
||||||
public void initChannel(SocketChannel ch) throws Exception {
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
ChannelPipeline p = ch.pipeline();
|
ChannelPipeline p = ch.pipeline();
|
||||||
|
p.addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
||||||
SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
|
|
||||||
SSLEngine engine = sslHandler.engine();
|
|
||||||
p.addLast("ssl", sslHandler);
|
|
||||||
|
|
||||||
// Setup NextProtoNego with our server provider
|
|
||||||
NextProtoNego.put(engine, new SpdyServerProvider());
|
|
||||||
NextProtoNego.debug = true;
|
|
||||||
|
|
||||||
// Negotiates with the browser if SPDY or HTTP is going to be used
|
// Negotiates with the browser if SPDY or HTTP is going to be used
|
||||||
p.addLast("handler", new SpdyOrHttpHandler());
|
p.addLast("handler", new SpdyOrHttpHandler());
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014 The Netty Project
|
|
||||||
*
|
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at:
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package io.netty.example.spdy.server;
|
|
||||||
|
|
||||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser;
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Jetty project provides an implementation of the Transport Layer Security (TLS) extension for Next
|
|
||||||
* Protocol Negotiation (NPN) for OpenJDK 7 or greater. NPN allows the application layer to negotiate which
|
|
||||||
* protocol to use over the secure connection.
|
|
||||||
* <p>
|
|
||||||
* This NPN service provider negotiates using SPDY.
|
|
||||||
* <p>
|
|
||||||
* To enable NPN support, start the JVM with: {@code java -Xbootclasspath/p:<path_to_npn_boot_jar> ...}. The
|
|
||||||
* "path_to_npn_boot_jar" is the path on the file system for the NPN Boot Jar file which can be downloaded from
|
|
||||||
* Maven at coordinates org.mortbay.jetty.npn:npn-boot. Different versions applies to different OpenJDK versions.
|
|
||||||
*
|
|
||||||
* @see <a href="http://www.eclipse.org/jetty/documentation/current/npn-chapter.html">Jetty documentation</a>
|
|
||||||
*/
|
|
||||||
public class SpdyServerProvider implements ServerProvider {
|
|
||||||
|
|
||||||
private String selectedProtocol;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unsupported() {
|
|
||||||
// if unsupported, default to http/1.1
|
|
||||||
selectedProtocol = "http/1.1";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> protocols() {
|
|
||||||
return Arrays.asList("spdy/3.1", "http/1.1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void protocolSelected(String protocol) {
|
|
||||||
selectedProtocol = protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpdyOrHttpChooser.SelectedProtocol getSelectedProtocol() {
|
|
||||||
if (selectedProtocol == null) {
|
|
||||||
return SpdyOrHttpChooser.SelectedProtocol.UNKNOWN;
|
|
||||||
}
|
|
||||||
return SpdyOrHttpChooser.SelectedProtocol.protocol(selectedProtocol);
|
|
||||||
}
|
|
||||||
}
|
|
@ -55,6 +55,17 @@
|
|||||||
<artifactId>bcpkix-jdk15on</artifactId>
|
<artifactId>bcpkix-jdk15on</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.npn</groupId>
|
||||||
|
<artifactId>npn-api</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mortbay.jetty.npn</groupId>
|
||||||
|
<artifactId>npn-boot</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.ByteBufInputStream;
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
import javax.net.ssl.SSLSessionContext;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
@ -38,6 +39,7 @@ import java.util.List;
|
|||||||
public final class JdkSslClientContext extends JdkSslContext {
|
public final class JdkSslClientContext extends JdkSslContext {
|
||||||
|
|
||||||
private final SSLContext ctx;
|
private final SSLContext ctx;
|
||||||
|
private final ApplicationProtocolSelector nextProtocolSelector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -105,10 +107,12 @@ public final class JdkSslClientContext extends JdkSslContext {
|
|||||||
|
|
||||||
super(ciphers);
|
super(ciphers);
|
||||||
|
|
||||||
if (nextProtocolSelector != null) {
|
if (nextProtocolSelector != null && !JettyNpnSslEngine.isAvailable()) {
|
||||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocolSelector);
|
throw new SSLException("NPN/ALPN unsupported: " + nextProtocolSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.nextProtocolSelector = nextProtocolSelector;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (certChainFile == null) {
|
if (certChainFile == null) {
|
||||||
ctx = SSLContext.getInstance(PROTOCOL);
|
ctx = SSLContext.getInstance(PROTOCOL);
|
||||||
@ -166,7 +170,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApplicationProtocolSelector nextProtocolSelector() {
|
public ApplicationProtocolSelector nextProtocolSelector() {
|
||||||
return null;
|
return nextProtocolSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -178,4 +182,13 @@ public final class JdkSslClientContext extends JdkSslContext {
|
|||||||
public SSLContext context() {
|
public SSLContext context() {
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SSLEngine wrapEngine(SSLEngine engine) {
|
||||||
|
if (nextProtocolSelector == null) {
|
||||||
|
return engine;
|
||||||
|
} else {
|
||||||
|
return new JettyNpnSslEngine(engine, nextProtocolSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ public abstract class JdkSslContext extends SslContext {
|
|||||||
engine.setEnabledCipherSuites(cipherSuites);
|
engine.setEnabledCipherSuites(cipherSuites);
|
||||||
engine.setEnabledProtocols(PROTOCOLS);
|
engine.setEnabledProtocols(PROTOCOLS);
|
||||||
engine.setUseClientMode(isClient());
|
engine.setUseClientMode(isClient());
|
||||||
return engine;
|
return wrapEngine(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -162,9 +162,11 @@ public abstract class JdkSslContext extends SslContext {
|
|||||||
engine.setEnabledCipherSuites(cipherSuites);
|
engine.setEnabledCipherSuites(cipherSuites);
|
||||||
engine.setEnabledProtocols(PROTOCOLS);
|
engine.setEnabledProtocols(PROTOCOLS);
|
||||||
engine.setUseClientMode(isClient());
|
engine.setUseClientMode(isClient());
|
||||||
return engine;
|
return wrapEngine(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract SSLEngine wrapEngine(SSLEngine engine);
|
||||||
|
|
||||||
private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
|
private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
|
||||||
if (ciphers == null) {
|
if (ciphers == null) {
|
||||||
return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
|
return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
|
||||||
|
@ -21,6 +21,7 @@ import io.netty.buffer.ByteBufInputStream;
|
|||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
import javax.net.ssl.SSLSessionContext;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -42,6 +43,7 @@ import java.util.List;
|
|||||||
public final class JdkSslServerContext extends JdkSslContext {
|
public final class JdkSslServerContext extends JdkSslContext {
|
||||||
|
|
||||||
private final SSLContext ctx;
|
private final SSLContext ctx;
|
||||||
|
private final List<String> nextProtocols;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -100,7 +102,21 @@ public final class JdkSslServerContext extends JdkSslContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
||||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
if (!JettyNpnSslEngine.isAvailable()) {
|
||||||
|
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
for (String p: nextProtocols) {
|
||||||
|
if (p == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextProtocols = Collections.unmodifiableList(list);
|
||||||
|
} else {
|
||||||
|
this.nextProtocols = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
|
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
|
||||||
@ -173,11 +189,20 @@ public final class JdkSslServerContext extends JdkSslContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> nextProtocols() {
|
public List<String> nextProtocols() {
|
||||||
return Collections.emptyList();
|
return nextProtocols;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLContext context() {
|
public SSLContext context() {
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SSLEngine wrapEngine(SSLEngine engine) {
|
||||||
|
if (nextProtocols.isEmpty()) {
|
||||||
|
return engine;
|
||||||
|
} else {
|
||||||
|
return new JettyNpnSslEngine(engine, nextProtocols);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,286 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
|
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
||||||
|
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLEngineResult;
|
||||||
|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.SSLParameters;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class JettyNpnSslEngine extends SSLEngine {
|
||||||
|
|
||||||
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(JettyNpnSslEngine.class);
|
||||||
|
|
||||||
|
private static boolean available;
|
||||||
|
|
||||||
|
static boolean isAvailable() {
|
||||||
|
updateAvailability();
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateAvailability() {
|
||||||
|
if (available) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Try to get the bootstrap class loader.
|
||||||
|
ClassLoader bootloader = ClassLoader.getSystemClassLoader().getParent();
|
||||||
|
if (bootloader == null) {
|
||||||
|
// If failed, use the system class loader,
|
||||||
|
// although it's not perfect to tell if NPN extension has been loaded.
|
||||||
|
bootloader = ClassLoader.getSystemClassLoader();
|
||||||
|
}
|
||||||
|
Class.forName("sun.security.ssl.NextProtoNegoExtension", true, bootloader);
|
||||||
|
available = true;
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// npn-boot was not loaded.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final SSLEngine engine;
|
||||||
|
private final JettyNpnSslSession session;
|
||||||
|
|
||||||
|
JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols) {
|
||||||
|
assert !nextProtocols.isEmpty();
|
||||||
|
|
||||||
|
this.engine = engine;
|
||||||
|
session = new JettyNpnSslSession(engine);
|
||||||
|
|
||||||
|
NextProtoNego.put(engine, new ServerProvider() {
|
||||||
|
@Override
|
||||||
|
public void unsupported() {
|
||||||
|
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> protocols() {
|
||||||
|
return nextProtocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void protocolSelected(String protocol) {
|
||||||
|
getSession().setApplicationProtocol(protocol);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
JettyNpnSslEngine(SSLEngine engine, final ApplicationProtocolSelector nextProtocolSelector) {
|
||||||
|
this.engine = engine;
|
||||||
|
session = new JettyNpnSslSession(engine);
|
||||||
|
|
||||||
|
NextProtoNego.put(engine, new ClientProvider() {
|
||||||
|
@Override
|
||||||
|
public boolean supports() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unsupported() {
|
||||||
|
session.setApplicationProtocol(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String selectProtocol(List<String> protocols) {
|
||||||
|
String p = null;
|
||||||
|
try {
|
||||||
|
p = nextProtocolSelector.selectProtocol(protocols);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to select the next protocol:", e);
|
||||||
|
}
|
||||||
|
session.setApplicationProtocol(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JettyNpnSslSession getSession() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeInbound() throws SSLException {
|
||||||
|
NextProtoNego.remove(engine);
|
||||||
|
engine.closeInbound();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeOutbound() {
|
||||||
|
NextProtoNego.remove(engine);
|
||||||
|
engine.closeOutbound();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPeerHost() {
|
||||||
|
return engine.getPeerHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPeerPort() {
|
||||||
|
return engine.getPeerPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLEngineResult wrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException {
|
||||||
|
return engine.wrap(byteBuffer, byteBuffer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, ByteBuffer byteBuffer) throws SSLException {
|
||||||
|
return engine.wrap(byteBuffers, byteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i2, ByteBuffer byteBuffer) throws SSLException {
|
||||||
|
return engine.wrap(byteBuffers, i, i2, byteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException {
|
||||||
|
return engine.unwrap(byteBuffer, byteBuffer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers) throws SSLException {
|
||||||
|
return engine.unwrap(byteBuffer, byteBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i2) throws SSLException {
|
||||||
|
return engine.unwrap(byteBuffer, byteBuffers, i, i2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Runnable getDelegatedTask() {
|
||||||
|
return engine.getDelegatedTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInboundDone() {
|
||||||
|
return engine.isInboundDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOutboundDone() {
|
||||||
|
return engine.isOutboundDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getSupportedCipherSuites() {
|
||||||
|
return engine.getSupportedCipherSuites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getEnabledCipherSuites() {
|
||||||
|
return engine.getEnabledCipherSuites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabledCipherSuites(String[] strings) {
|
||||||
|
engine.setEnabledCipherSuites(strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getSupportedProtocols() {
|
||||||
|
return engine.getSupportedProtocols();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getEnabledProtocols() {
|
||||||
|
return engine.getEnabledProtocols();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabledProtocols(String[] strings) {
|
||||||
|
engine.setEnabledProtocols(strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLSession getHandshakeSession() {
|
||||||
|
return engine.getHandshakeSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beginHandshake() throws SSLException {
|
||||||
|
engine.beginHandshake();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandshakeStatus getHandshakeStatus() {
|
||||||
|
return engine.getHandshakeStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUseClientMode(boolean b) {
|
||||||
|
engine.setUseClientMode(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getUseClientMode() {
|
||||||
|
return engine.getUseClientMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNeedClientAuth(boolean b) {
|
||||||
|
engine.setNeedClientAuth(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getNeedClientAuth() {
|
||||||
|
return engine.getNeedClientAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWantClientAuth(boolean b) {
|
||||||
|
engine.setWantClientAuth(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getWantClientAuth() {
|
||||||
|
return engine.getWantClientAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnableSessionCreation(boolean b) {
|
||||||
|
engine.setEnableSessionCreation(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getEnableSessionCreation() {
|
||||||
|
return engine.getEnableSessionCreation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLParameters getSSLParameters() {
|
||||||
|
return engine.getSSLParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSSLParameters(SSLParameters sslParameters) {
|
||||||
|
engine.setSSLParameters(sslParameters);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSessionContext;
|
||||||
|
import javax.security.cert.X509Certificate;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
|
||||||
|
final class JettyNpnSslSession implements SSLSession {
|
||||||
|
|
||||||
|
private final SSLEngine engine;
|
||||||
|
private volatile String applicationProtocol;
|
||||||
|
|
||||||
|
JettyNpnSslSession(SSLEngine engine) {
|
||||||
|
this.engine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setApplicationProtocol(String applicationProtocol) {
|
||||||
|
this.applicationProtocol = applicationProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProtocol() {
|
||||||
|
final String protocol = unwrap().getProtocol();
|
||||||
|
final String applicationProtocol = this.applicationProtocol;
|
||||||
|
|
||||||
|
if (applicationProtocol == null) {
|
||||||
|
if (protocol != null) {
|
||||||
|
return protocol.replace(':', '_');
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder buf = new StringBuilder(32);
|
||||||
|
if (protocol != null) {
|
||||||
|
buf.append(protocol.replace(':', '_'));
|
||||||
|
buf.append(':');
|
||||||
|
} else {
|
||||||
|
buf.append("null:");
|
||||||
|
}
|
||||||
|
buf.append(applicationProtocol);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLSession unwrap() {
|
||||||
|
return engine.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getId() {
|
||||||
|
return unwrap().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLSessionContext getSessionContext() {
|
||||||
|
return unwrap().getSessionContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCreationTime() {
|
||||||
|
return unwrap().getCreationTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLastAccessedTime() {
|
||||||
|
return unwrap().getLastAccessedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
unwrap().invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return unwrap().isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putValue(String s, Object o) {
|
||||||
|
unwrap().putValue(s, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue(String s) {
|
||||||
|
return unwrap().getValue(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeValue(String s) {
|
||||||
|
unwrap().removeValue(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getValueNames() {
|
||||||
|
return unwrap().getValueNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
|
||||||
|
return unwrap().getPeerCertificates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Certificate[] getLocalCertificates() {
|
||||||
|
return unwrap().getLocalCertificates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
|
||||||
|
return unwrap().getPeerCertificateChain();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
|
||||||
|
return unwrap().getPeerPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getLocalPrincipal() {
|
||||||
|
return unwrap().getLocalPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCipherSuite() {
|
||||||
|
return unwrap().getCipherSuite();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPeerHost() {
|
||||||
|
return unwrap().getPeerHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPeerPort() {
|
||||||
|
return unwrap().getPeerPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPacketBufferSize() {
|
||||||
|
return unwrap().getPacketBufferSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getApplicationBufferSize() {
|
||||||
|
return unwrap().getApplicationBufferSize();
|
||||||
|
}
|
||||||
|
}
|
@ -85,7 +85,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
private volatile int destroyed;
|
private volatile int destroyed;
|
||||||
|
|
||||||
private String cipher;
|
private String cipher;
|
||||||
private String protocol;
|
private volatile String applicationProtocol;
|
||||||
|
|
||||||
// SSL Engine status variables
|
// SSL Engine status variables
|
||||||
private boolean isInboundDone;
|
private boolean isInboundDone;
|
||||||
@ -95,6 +95,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
private int lastPrimingReadResult;
|
private int lastPrimingReadResult;
|
||||||
|
|
||||||
private final ByteBufAllocator alloc;
|
private final ByteBufAllocator alloc;
|
||||||
|
private final String fallbackApplicationProtocol;
|
||||||
private SSLSession session;
|
private SSLSession session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +104,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
* @param sslCtx an OpenSSL {@code SSL_CTX} object
|
* @param sslCtx an OpenSSL {@code SSL_CTX} object
|
||||||
* @param alloc the {@link ByteBufAllocator} that will be used by this engine
|
* @param alloc the {@link ByteBufAllocator} that will be used by this engine
|
||||||
*/
|
*/
|
||||||
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc) {
|
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
|
||||||
OpenSsl.ensureAvailability();
|
OpenSsl.ensureAvailability();
|
||||||
if (sslCtx == 0) {
|
if (sslCtx == 0) {
|
||||||
throw new NullPointerException("sslContext");
|
throw new NullPointerException("sslContext");
|
||||||
@ -115,6 +116,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
this.alloc = alloc;
|
this.alloc = alloc;
|
||||||
ssl = SSL.newSSL(sslCtx, true);
|
ssl = SSL.newSSL(sslCtx, true);
|
||||||
networkBIO = SSL.makeNetworkBIO(ssl);
|
networkBIO = SSL.makeNetworkBIO(ssl);
|
||||||
|
this.fallbackApplicationProtocol = fallbackApplicationProtocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,7 +710,13 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getProtocol() {
|
public String getProtocol() {
|
||||||
return protocol;
|
// TODO: Figure out how to get the current protocol.
|
||||||
|
String applicationProtocol = OpenSslEngine.this.applicationProtocol;
|
||||||
|
if (applicationProtocol == null) {
|
||||||
|
return "unknown";
|
||||||
|
} else {
|
||||||
|
return "unknown:" + applicationProtocol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -796,7 +804,11 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
if (SSL.isInInit(ssl) == 0) {
|
if (SSL.isInInit(ssl) == 0) {
|
||||||
handshakeFinished = true;
|
handshakeFinished = true;
|
||||||
cipher = SSL.getCipherForSSL(ssl);
|
cipher = SSL.getCipherForSSL(ssl);
|
||||||
protocol = SSL.getNextProtoNegotiated(ssl);
|
String applicationProtocol = SSL.getNextProtoNegotiated(ssl);
|
||||||
|
if (applicationProtocol == null) {
|
||||||
|
applicationProtocol = fallbackApplicationProtocol;
|
||||||
|
}
|
||||||
|
this.applicationProtocol = applicationProtocol;
|
||||||
return FINISHED;
|
return FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +310,11 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||||
return new OpenSslEngine(ctx, alloc);
|
if (unmodifiableNextProtocols.isEmpty()) {
|
||||||
|
return new OpenSslEngine(ctx, alloc, null);
|
||||||
|
} else {
|
||||||
|
return new OpenSslEngine(ctx, alloc, unmodifiableNextProtocols.get(unmodifiableNextProtocols.size() - 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,6 +26,7 @@ import javax.net.ssl.SSLException;
|
|||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -373,6 +374,83 @@ public abstract class SslContext {
|
|||||||
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout);
|
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a simple client-side {@link ApplicationProtocolSelector} that selects the most preferred protocol
|
||||||
|
* among the application protocols sent by the server. If there is no match, it chooses the least preferred one.
|
||||||
|
*
|
||||||
|
* @param nextProtocols the list of the supported client-side application protocols, in the order of preference
|
||||||
|
* @return the new {@link ApplicationProtocolSelector}.
|
||||||
|
* {@code null} if the specified {@code nextProtocols} does not contain any elements.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static ApplicationProtocolSelector newApplicationProtocolSelector(String... nextProtocols) {
|
||||||
|
if (nextProtocols == null) {
|
||||||
|
throw new NullPointerException("nextProtocols");
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> list = new ArrayList<String>();
|
||||||
|
for (String p: nextProtocols) {
|
||||||
|
if (p == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newApplicationProtocolSelector(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ApplicationProtocolSelector newApplicationProtocolSelector(final List<String> list) {
|
||||||
|
return new ApplicationProtocolSelector() {
|
||||||
|
@Override
|
||||||
|
public String selectProtocol(List<String> protocols) throws Exception {
|
||||||
|
for (String p: list) {
|
||||||
|
if (protocols.contains(p)) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list.get(list.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ApplicationProtocolSelector(" + list + ')';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a simple client-side {@link ApplicationProtocolSelector} that selects the most preferred protocol
|
||||||
|
* among the application protocols sent by the server. If there is no match, it chooses the least preferred one.
|
||||||
|
*
|
||||||
|
* @param nextProtocols the list of the supported client-side application protocols, in the order of preference
|
||||||
|
* @return the new {@link ApplicationProtocolSelector}.
|
||||||
|
* {@code null} if the specified {@code nextProtocols} does not contain any elements.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static ApplicationProtocolSelector newApplicationProtocolSelector(Iterable<String> nextProtocols) {
|
||||||
|
if (nextProtocols == null) {
|
||||||
|
throw new NullPointerException("nextProtocols");
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> list = new ArrayList<String>();
|
||||||
|
for (String p: nextProtocols) {
|
||||||
|
if (p == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newApplicationProtocolSelector(list);
|
||||||
|
}
|
||||||
|
|
||||||
SslContext() { }
|
SslContext() { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
3
pom.xml
3
pom.xml
@ -668,6 +668,9 @@
|
|||||||
<ignore>sun.security.x509.X500Name</ignore>
|
<ignore>sun.security.x509.X500Name</ignore>
|
||||||
<ignore>sun.security.x509.X509CertInfo</ignore>
|
<ignore>sun.security.x509.X509CertInfo</ignore>
|
||||||
<ignore>sun.security.x509.X509CertImpl</ignore>
|
<ignore>sun.security.x509.X509CertImpl</ignore>
|
||||||
|
|
||||||
|
<!-- SSLSession implelementation -->
|
||||||
|
<ignore>javax.net.ssl.SSLEngine</ignore>
|
||||||
</ignores>
|
</ignores>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
|
Loading…
Reference in New Issue
Block a user