Add an OpenSslEngine and the universal API for enabling SSL

Motivation:

Some users already use an SSLEngine implementation in finagle-native. It
wraps OpenSSL to get higher SSL performance.  However, to take advantage
of it, finagle-native must be compiled manually, and it means we cannot
pull it in as a dependency and thus we cannot test our SslHandler
against the OpenSSL-based SSLEngine.  For an instance, we had #2216.

Because the construction procedures of JDK SSLEngine and OpenSslEngine
are very different from each other, we also need to provide a universal
way to enable SSL in a Netty application.

Modifications:

- Pull netty-tcnative in as an optional dependency.
  http://netty.io/wiki/forked-tomcat-native.html
- Backport NativeLibraryLoader from 4.0
- Move OpenSSL-based SSLEngine implementation into our code base.
  - Copied from finagle-native; originally written by @jpinner et al.
  - Overall cleanup by @trustin.
- Run all SslHandler tests with both default SSLEngine and OpenSslEngine
- Add a unified API for creating an SSL context
  - SslContext allows you to create a new SSLEngine or a new SslHandler
    with your PKCS#8 key and X.509 certificate chain.
  - Add JdkSslContext and its subclasses
  - Add OpenSslServerContext
- Add ApplicationProtocolSelector to ensure the future support for NPN
  (NextProtoNego) and ALPN (Application Layer Protocol Negotiation) on
  the client-side.
- Add SimpleTrustManagerFactory to help a user write a
  TrustManagerFactory easily, which should be useful for those who need
  to write an alternative verification mechanism. For example, we can
  use it to implement an unsafe TrustManagerFactory that accepts
  self-signed certificates for testing purposes.
- Add InsecureTrustManagerFactory and FingerprintTrustManager for quick
  and dirty testing
- Add SelfSignedCertificate class which generates a self-signed X.509
  certificate very easily.
- Update all our examples to use SslContext.newClient/ServerContext()
- SslHandler now logs the chosen cipher suite when handshake is
  finished.

Result:

- Cleaner unified API for configuring an SSL client and an SSL server
  regardless of its internal implementation.
- When native libraries are available, OpenSSL-based SSLEngine
  implementation is selected automatically to take advantage of its
  performance benefit.
- Examples take advantage of this modification and thus are cleaner.
This commit is contained in:
Trustin Lee 2014-05-18 02:26:01 +09:00
parent 942db3aa23
commit a72230061d
56 changed files with 3953 additions and 1105 deletions

View File

@ -17,6 +17,7 @@
package io.netty.util.internal;
import java.nio.ByteBuffer;
import java.security.cert.X509Certificate;
public final class EmptyArrays {
@ -31,6 +32,7 @@ public final class EmptyArrays {
public static final String[] EMPTY_STRINGS = new String[0];
public static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
public static final ByteBuffer[] EMPTY_BYTE_BUFFERS = new ByteBuffer[0];
public static final X509Certificate[] EMPTY_X509_CERTIFICATES = new X509Certificate[0];
private EmptyArrays() { }
}

View File

@ -27,6 +27,8 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.net.URI;
@ -59,7 +61,13 @@ public class HttpSnoopClient {
return;
}
final SslContext sslCtx;
boolean ssl = "https".equalsIgnoreCase(scheme);
if (ssl) {
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
} else {
sslCtx = null;
}
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
@ -67,7 +75,7 @@ public class HttpSnoopClient {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new HttpSnoopClientInitializer(ssl));
.handler(new HttpSnoopClientInitializer(sslCtx));
// Make the connection attempt.
Channel ch = b.connect(host, port).sync().channel();

View File

@ -18,21 +18,18 @@ package io.netty.example.http.snoop;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
import io.netty.handler.ssl.SslContext;
public class HttpSnoopClientInitializer extends ChannelInitializer<SocketChannel> {
private final boolean ssl;
private final SslContext sslCtx;
public HttpSnoopClientInitializer(boolean ssl) {
this.ssl = ssl;
public HttpSnoopClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
@ -41,13 +38,10 @@ public class HttpSnoopClientInitializer extends ChannelInitializer<SocketChannel
ChannelPipeline p = ch.pipeline();
p.addLast("log", new LoggingHandler(LogLevel.INFO));
// Enable HTTPS if necessary.
if (ssl) {
SSLEngine engine =
SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
p.addLast("ssl", new SslHandler(engine));
// Enable HTTPS if necessary.
if (sslCtx != null) {
p.addLast("ssl", sslCtx.newHandler(ch.alloc()));
}
p.addLast("codec", new HttpClientCodec());

View File

@ -37,6 +37,8 @@ import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.ErrorDataEncoderException;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.BufferedWriter;
import java.io.File;
@ -97,7 +99,13 @@ public class HttpUploadClient {
return;
}
final SslContext sslCtx;
boolean ssl = "https".equalsIgnoreCase(scheme);
if (ssl) {
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
} else {
sslCtx = null;
}
URI uriFile;
try {
@ -125,7 +133,7 @@ public class HttpUploadClient {
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(new HttpUploadClientIntializer(ssl));
b.group(group).channel(NioSocketChannel.class).handler(new HttpUploadClientIntializer(sslCtx));
// Simple Get form: no factory used (not usable)
List<Entry<String, String>> headers = formGet(b, host, port, get, uriSimple);

View File

@ -18,19 +18,17 @@ package io.netty.example.http.upload;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.stream.ChunkedWriteHandler;
import javax.net.ssl.SSLEngine;
public class HttpUploadClientIntializer extends ChannelInitializer<SocketChannel> {
private final boolean ssl;
public HttpUploadClientIntializer(boolean ssl) {
this.ssl = ssl;
private final SslContext sslCtx;
public HttpUploadClientIntializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
@ -38,10 +36,8 @@ public class HttpUploadClientIntializer extends ChannelInitializer<SocketChannel
// Create a default pipeline implementation.
ChannelPipeline pipeline = ch.pipeline();
if (ssl) {
SSLEngine engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
pipeline.addLast("ssl", new SslHandler(engine));
if (sslCtx != null) {
pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc()));
}
pipeline.addLast("codec", new HttpClientCodec());

View File

@ -20,16 +20,20 @@ import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* A HTTP server showing how to use the HTTP multipart package for file uploads and decoding post data.
*/
public class HttpUploadServer {
private final SslContext sslCtx;
private final int port;
public static boolean isSSL;
public HttpUploadServer(int port) {
public HttpUploadServer(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -39,7 +43,7 @@ public class HttpUploadServer {
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new HttpUploadServerInitializer());
.childHandler(new HttpUploadServerInitializer(sslCtx));
Channel ch = b.bind(port).sync().channel();
System.out.println("HTTP Upload Server at port " + port + '.');
@ -62,6 +66,15 @@ public class HttpUploadServer {
if (args.length > 1) {
isSSL = true;
}
new HttpUploadServer(port).run();
// Configure SSL.
SslContext sslCtx;
if (isSSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
new HttpUploadServer(sslCtx, port).run();
}
}

View File

@ -18,24 +18,26 @@ package io.netty.example.http.upload;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
import io.netty.handler.ssl.SslContext;
public class HttpUploadServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public HttpUploadServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = ch.pipeline();
if (HttpUploadServer.isSSL) {
SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
pipeline.addLast("ssl", new SslHandler(engine));
if (sslCtx != null) {
pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc()));
}
pipeline.addLast("decoder", new HttpRequestDecoder());

View File

@ -14,7 +14,6 @@
*/
package io.netty.example.http2.client;
import static java.util.concurrent.TimeUnit.SECONDS;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
@ -28,23 +27,30 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http2.DefaultHttp2Headers;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import javax.net.ssl.SSLException;
import java.net.InetSocketAddress;
import java.util.concurrent.BlockingQueue;
import static java.util.concurrent.TimeUnit.*;
/**
* An HTTP2 client that allows you to send HTTP2 frames to a server. Inbound and outbound frames are
* logged.
*/
public class Http2Client {
private final SslContext sslCtx;
private final String host;
private final int port;
private final Http2ClientConnectionHandler http2ConnectionHandler;
private Channel channel;
private EventLoopGroup workerGroup;
public Http2Client(String host, int port) {
public Http2Client(String host, int port) throws SSLException {
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
this.host = host;
this.port = port;
http2ConnectionHandler = new Http2ClientConnectionHandler();
@ -63,7 +69,7 @@ public class Http2Client {
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.remoteAddress(new InetSocketAddress(host, port));
b.handler(new Http2ClientInitializer(http2ConnectionHandler));
b.handler(new Http2ClientInitializer(sslCtx, http2ConnectionHandler));
// Start the client.
channel = b.connect().syncUninterruptibly().channel();

View File

@ -17,29 +17,30 @@ package io.netty.example.http2.client;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.eclipse.jetty.npn.NextProtoNego;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.npn.NextProtoNego;
/**
* Configures the client pipeline to support HTTP/2 frames.
*/
public class Http2ClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
private final AbstractHttp2ConnectionHandler connectionHandler;
public Http2ClientInitializer(AbstractHttp2ConnectionHandler connectionHandler) {
public Http2ClientInitializer(SslContext sslCtx, AbstractHttp2ConnectionHandler connectionHandler) {
this.sslCtx = sslCtx;
this.connectionHandler = connectionHandler;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
SSLEngine engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
SSLEngine engine = sslHandler.engine();
NextProtoNego.put(engine, new Http2ClientProvider());
NextProtoNego.debug = true;

View File

@ -22,6 +22,9 @@ import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* A HTTP/2 Server that responds to requests with a Hello World.
@ -30,9 +33,11 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
*/
public class Http2Server {
private final SslContext sslCtx;
private final int port;
public Http2Server(int port) {
public Http2Server(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -44,7 +49,7 @@ public class Http2Server {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new Http2ServerInitializer());
.childHandler(new Http2ServerInitializer(sslCtx));
Channel ch = b.bind(port).sync().channel();
ch.closeFuture().sync();
@ -65,7 +70,11 @@ public class Http2Server {
System.out.println("HTTP2 server started at port " + port + '.');
new Http2Server(port).run();
// Configure SSL context.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(SslProvider.JDK, ssc.certificate(), ssc.privateKey());
new Http2Server(sslCtx, port).run();
}
public static void checkForNpnSupport() {

View File

@ -19,7 +19,7 @@ package io.netty.example.http2.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.eclipse.jetty.npn.NextProtoNego;
@ -29,12 +29,19 @@ import javax.net.ssl.SSLEngine;
* Sets up the Netty pipeline
*/
public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public Http2ServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
SslHandler handler = sslCtx.newHandler(ch.alloc());
SSLEngine engine = handler.engine();
p.addLast("ssl", new SslHandler(engine));
// Setup NextProtoNego with our server provider

View File

@ -21,6 +21,8 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* Serves two protocols (HTTP and Factorial) using only one port, enabling
@ -31,9 +33,11 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
*/
public class PortUnificationServer {
private final SslContext sslCtx;
private final int port;
public PortUnificationServer(int port) {
public PortUnificationServer(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -47,7 +51,7 @@ public class PortUnificationServer {
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new PortUnificationServerHandler());
ch.pipeline().addLast(new PortUnificationServerHandler(sslCtx));
}
});
@ -66,6 +70,11 @@ public class PortUnificationServer {
} else {
port = 8080;
}
new PortUnificationServer(port).run();
// Configure SSL.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
new PortUnificationServer(sslCtx, port).run();
}
}

View File

@ -22,16 +22,15 @@ import io.netty.example.factorial.BigIntegerDecoder;
import io.netty.example.factorial.FactorialServerHandler;
import io.netty.example.factorial.NumberEncoder;
import io.netty.example.http.snoop.HttpSnoopServerHandler;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
import java.util.List;
/**
@ -40,14 +39,16 @@ import java.util.List;
*/
public class PortUnificationServerHandler extends ByteToMessageDecoder {
private final SslContext sslCtx;
private final boolean detectSsl;
private final boolean detectGzip;
public PortUnificationServerHandler() {
this(true, true);
public PortUnificationServerHandler(SslContext sslCtx) {
this(sslCtx, true, true);
}
private PortUnificationServerHandler(boolean detectSsl, boolean detectGzip) {
private PortUnificationServerHandler(SslContext sslCtx, boolean detectSsl, boolean detectGzip) {
this.sslCtx = sslCtx;
this.detectSsl = detectSsl;
this.detectGzip = detectGzip;
}
@ -111,13 +112,8 @@ public class PortUnificationServerHandler extends ByteToMessageDecoder {
private void enableSsl(ChannelHandlerContext ctx) {
ChannelPipeline p = ctx.pipeline();
SSLEngine engine =
SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
p.addLast("ssl", new SslHandler(engine));
p.addLast("unificationA", new PortUnificationServerHandler(false, detectGzip));
p.addLast("ssl", sslCtx.newHandler(ctx.alloc()));
p.addLast("unificationA", new PortUnificationServerHandler(sslCtx, false, detectGzip));
p.remove(this);
}
@ -125,7 +121,7 @@ public class PortUnificationServerHandler extends ByteToMessageDecoder {
ChannelPipeline p = ctx.pipeline();
p.addLast("gzipdeflater", ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
p.addLast("gzipinflater", ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
p.addLast("unificationB", new PortUnificationServerHandler(detectSsl, false));
p.addLast("unificationB", new PortUnificationServerHandler(sslCtx, detectSsl, false));
p.remove(this);
}

View File

@ -22,6 +22,8 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.example.telnet.TelnetClient;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@ -31,10 +33,12 @@ import java.io.InputStreamReader;
*/
public class SecureChatClient {
private final SslContext sslCtx;
private final String host;
private final int port;
public SecureChatClient(String host, int port) {
public SecureChatClient(SslContext sslCtx, String host, int port) {
this.sslCtx = sslCtx;
this.host = host;
this.port = port;
}
@ -45,7 +49,7 @@ public class SecureChatClient {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new SecureChatClientInitializer());
.handler(new SecureChatClientInitializer(sslCtx));
// Start the connection attempt.
Channel ch = b.connect(host, port).sync().channel();
@ -93,6 +97,8 @@ public class SecureChatClient {
String host = args[0];
int port = Integer.parseInt(args[1]);
new SecureChatClient(host, port).run();
// Configure SSL.
SslContext sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
new SecureChatClient(sslCtx, host, port).run();
}
}

View File

@ -22,15 +22,19 @@ import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public SecureChatClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
@ -40,12 +44,7 @@ public class SecureChatClientInitializer extends ChannelInitializer<SocketChanne
// and accept any invalid certificates in the client side.
// You will need something more complicated to identify both
// and server in the real world.
SSLEngine engine =
SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc()));
// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(

View File

@ -1,313 +0,0 @@
/*
* Copyright 2012 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.securechat;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* A bogus key store which provides all the required information to
* create an example SSL connection.
*
* To generate a bogus key store:
* <pre>
* keytool -genkey -alias securechat -keysize 2048 -validity 36500
* -keyalg RSA -dname "CN=securechat"
* -keypass secret -storepass secret
* -keystore cert.jks
* </pre>
*/
public final class SecureChatKeyStore {
private static final short[] DATA = {
0xfe, 0xed, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
0x00, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x00, 0x00, 0x01, 0x1a, 0x9f, 0x57, 0xa5,
0x27, 0x00, 0x00, 0x01, 0x9a, 0x30, 0x82, 0x01,
0x96, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01,
0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05,
0x00, 0x04, 0x82, 0x01, 0x82, 0x48, 0x6d, 0xcf,
0x16, 0xb5, 0x50, 0x95, 0x36, 0xbf, 0x47, 0x27,
0x50, 0x58, 0x0d, 0xa2, 0x52, 0x7e, 0x25, 0xab,
0x14, 0x1a, 0x26, 0x5e, 0x2d, 0x8a, 0x23, 0x90,
0x60, 0x7f, 0x12, 0x20, 0x56, 0xd1, 0x43, 0xa2,
0x6b, 0x47, 0x5d, 0xed, 0x9d, 0xd4, 0xe5, 0x83,
0x28, 0x89, 0xc2, 0x16, 0x4c, 0x76, 0x06, 0xad,
0x8e, 0x8c, 0x29, 0x1a, 0x9b, 0x0f, 0xdd, 0x60,
0x4b, 0xb4, 0x62, 0x82, 0x9e, 0x4a, 0x63, 0x83,
0x2e, 0xd2, 0x43, 0x78, 0xc2, 0x32, 0x1f, 0x60,
0xa9, 0x8a, 0x7f, 0x0f, 0x7c, 0xa6, 0x1d, 0xe6,
0x92, 0x9e, 0x52, 0xc7, 0x7d, 0xbb, 0x35, 0x3b,
0xaa, 0x89, 0x73, 0x4c, 0xfb, 0x99, 0x54, 0x97,
0x99, 0x28, 0x6e, 0x66, 0x5b, 0xf7, 0x9b, 0x7e,
0x6d, 0x8a, 0x2f, 0xfa, 0xc3, 0x1e, 0x71, 0xb9,
0xbd, 0x8f, 0xc5, 0x63, 0x25, 0x31, 0x20, 0x02,
0xff, 0x02, 0xf0, 0xc9, 0x2c, 0xdd, 0x3a, 0x10,
0x30, 0xab, 0xe5, 0xad, 0x3d, 0x1a, 0x82, 0x77,
0x46, 0xed, 0x03, 0x38, 0xa4, 0x73, 0x6d, 0x36,
0x36, 0x33, 0x70, 0xb2, 0x63, 0x20, 0xca, 0x03,
0xbf, 0x5a, 0xf4, 0x7c, 0x35, 0xf0, 0x63, 0x1a,
0x12, 0x33, 0x12, 0x58, 0xd9, 0xa2, 0x63, 0x6b,
0x63, 0x82, 0x41, 0x65, 0x70, 0x37, 0x4b, 0x99,
0x04, 0x9f, 0xdd, 0x5e, 0x07, 0x01, 0x95, 0x9f,
0x36, 0xe8, 0xc3, 0x66, 0x2a, 0x21, 0x69, 0x68,
0x40, 0xe6, 0xbc, 0xbb, 0x85, 0x81, 0x21, 0x13,
0xe6, 0xa4, 0xcf, 0xd3, 0x67, 0xe3, 0xfd, 0x75,
0xf0, 0xdf, 0x83, 0xe0, 0xc5, 0x36, 0x09, 0xac,
0x1b, 0xd4, 0xf7, 0x2a, 0x23, 0x57, 0x1c, 0x5c,
0x0f, 0xf4, 0xcf, 0xa2, 0xcf, 0xf5, 0xbd, 0x9c,
0x69, 0x98, 0x78, 0x3a, 0x25, 0xe4, 0xfd, 0x85,
0x11, 0xcc, 0x7d, 0xef, 0xeb, 0x74, 0x60, 0xb1,
0xb7, 0xfb, 0x1f, 0x0e, 0x62, 0xff, 0xfe, 0x09,
0x0a, 0xc3, 0x80, 0x2f, 0x10, 0x49, 0x89, 0x78,
0xd2, 0x08, 0xfa, 0x89, 0x22, 0x45, 0x91, 0x21,
0xbc, 0x90, 0x3e, 0xad, 0xb3, 0x0a, 0xb4, 0x0e,
0x1c, 0xa1, 0x93, 0x92, 0xd8, 0x72, 0x07, 0x54,
0x60, 0xe7, 0x91, 0xfc, 0xd9, 0x3c, 0xe1, 0x6f,
0x08, 0xe4, 0x56, 0xf6, 0x0b, 0xb0, 0x3c, 0x39,
0x8a, 0x2d, 0x48, 0x44, 0x28, 0x13, 0xca, 0xe9,
0xf7, 0xa3, 0xb6, 0x8a, 0x5f, 0x31, 0xa9, 0x72,
0xf2, 0xde, 0x96, 0xf2, 0xb1, 0x53, 0xb1, 0x3e,
0x24, 0x57, 0xfd, 0x18, 0x45, 0x1f, 0xc5, 0x33,
0x1b, 0xa4, 0xe8, 0x21, 0xfa, 0x0e, 0xb2, 0xb9,
0xcb, 0xc7, 0x07, 0x41, 0xdd, 0x2f, 0xb6, 0x6a,
0x23, 0x18, 0xed, 0xc1, 0xef, 0xe2, 0x4b, 0xec,
0xc9, 0xba, 0xfb, 0x46, 0x43, 0x90, 0xd7, 0xb5,
0x68, 0x28, 0x31, 0x2b, 0x8d, 0xa8, 0x51, 0x63,
0xf7, 0x53, 0x99, 0x19, 0x68, 0x85, 0x66, 0x00,
0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35,
0x30, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x30, 0x82,
0x02, 0x36, 0x30, 0x82, 0x01, 0xe0, 0xa0, 0x03,
0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 0xf1,
0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
0x30, 0x81, 0xa0, 0x31, 0x0b, 0x30, 0x09, 0x06,
0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52,
0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67,
0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 0x30,
0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b,
0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d,
0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 0x68,
0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20,
0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31,
0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b,
0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75,
0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e,
0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65,
0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30,
0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34,
0x31, 0x33, 0x38, 0x5a, 0x18, 0x0f, 0x32, 0x31,
0x38, 0x37, 0x31, 0x31, 0x32, 0x34, 0x30, 0x35,
0x34, 0x31, 0x33, 0x38, 0x5a, 0x30, 0x81, 0xa0,
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d,
0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
0x55, 0x04, 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f,
0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69,
0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04,
0x0a, 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e,
0x65, 0x74, 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f,
0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x41,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x31, 0x30,
0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63,
0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74,
0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79,
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74,
0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41,
0x00, 0xc3, 0xe3, 0x5e, 0x41, 0xa7, 0x87, 0x11,
0x00, 0x42, 0x2a, 0xb0, 0x4b, 0xed, 0xb2, 0xe0,
0x23, 0xdb, 0xb1, 0x3d, 0x58, 0x97, 0x35, 0x60,
0x0b, 0x82, 0x59, 0xd3, 0x00, 0xea, 0xd4, 0x61,
0xb8, 0x79, 0x3f, 0xb6, 0x3c, 0x12, 0x05, 0x93,
0x2e, 0x9a, 0x59, 0x68, 0x14, 0x77, 0x3a, 0xc8,
0x50, 0x25, 0x57, 0xa4, 0x49, 0x18, 0x63, 0x41,
0xf0, 0x2d, 0x28, 0xec, 0x06, 0xfb, 0xb4, 0x9f,
0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00,
0x65, 0x6c, 0x30, 0x01, 0xc2, 0x8e, 0x3e, 0xcb,
0xb3, 0x77, 0x48, 0xe9, 0x66, 0x61, 0x9a, 0x40,
0x86, 0xaf, 0xf6, 0x03, 0xeb, 0xba, 0x6a, 0xf2,
0xfd, 0xe2, 0xaf, 0x36, 0x5e, 0x7b, 0xaa, 0x22,
0x04, 0xdd, 0x2c, 0x20, 0xc4, 0xfc, 0xdd, 0xd0,
0x82, 0x20, 0x1c, 0x3d, 0xd7, 0x9e, 0x5e, 0x5c,
0x92, 0x5a, 0x76, 0x71, 0x28, 0xf5, 0x07, 0x7d,
0xa2, 0x81, 0xba, 0x77, 0x9f, 0x2a, 0xd9, 0x44,
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x6d, 0x79,
0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x1a, 0x9f,
0x5b, 0x56, 0xa0, 0x00, 0x00, 0x01, 0x99, 0x30,
0x82, 0x01, 0x95, 0x30, 0x0e, 0x06, 0x0a, 0x2b,
0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01,
0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x81, 0x29,
0xa8, 0xb6, 0x08, 0x0c, 0x85, 0x75, 0x3e, 0xdd,
0xb5, 0xe5, 0x1a, 0x87, 0x68, 0xd1, 0x90, 0x4b,
0x29, 0x31, 0xee, 0x90, 0xbc, 0x9d, 0x73, 0xa0,
0x3f, 0xe9, 0x0b, 0xa4, 0xef, 0x30, 0x9b, 0x36,
0x9a, 0xb2, 0x54, 0x77, 0x81, 0x07, 0x4b, 0xaa,
0xa5, 0x77, 0x98, 0xe1, 0xeb, 0xb5, 0x7c, 0x4e,
0x48, 0xd5, 0x08, 0xfc, 0x2c, 0x36, 0xe2, 0x65,
0x03, 0xac, 0xe5, 0xf3, 0x96, 0xb7, 0xd0, 0xb5,
0x3b, 0x92, 0xe4, 0x14, 0x05, 0x7a, 0x6a, 0x92,
0x56, 0xfe, 0x4e, 0xab, 0xd3, 0x0e, 0x32, 0x04,
0x22, 0x22, 0x74, 0x47, 0x7d, 0xec, 0x21, 0x99,
0x30, 0x31, 0x64, 0x46, 0x64, 0x9b, 0xc7, 0x13,
0xbf, 0xbe, 0xd0, 0x31, 0x49, 0xe7, 0x3c, 0xbf,
0xba, 0xb1, 0x20, 0xf9, 0x42, 0xf4, 0xa9, 0xa9,
0xe5, 0x13, 0x65, 0x32, 0xbf, 0x7c, 0xcc, 0x91,
0xd3, 0xfd, 0x24, 0x47, 0x0b, 0xe5, 0x53, 0xad,
0x50, 0x30, 0x56, 0xd1, 0xfa, 0x9c, 0x37, 0xa8,
0xc1, 0xce, 0xf6, 0x0b, 0x18, 0xaa, 0x7c, 0xab,
0xbd, 0x1f, 0xdf, 0xe4, 0x80, 0xb8, 0xa7, 0xe0,
0xad, 0x7d, 0x50, 0x74, 0xf1, 0x98, 0x78, 0xbc,
0x58, 0xb9, 0xc2, 0x52, 0xbe, 0xd2, 0x5b, 0x81,
0x94, 0x83, 0x8f, 0xb9, 0x4c, 0xee, 0x01, 0x2b,
0x5e, 0xc9, 0x6e, 0x9b, 0xf5, 0x63, 0x69, 0xe4,
0xd8, 0x0b, 0x47, 0xd8, 0xfd, 0xd8, 0xe0, 0xed,
0xa8, 0x27, 0x03, 0x74, 0x1e, 0x5d, 0x32, 0xe6,
0x5c, 0x63, 0xc2, 0xfb, 0x3f, 0xee, 0xb4, 0x13,
0xc6, 0x0e, 0x6e, 0x74, 0xe0, 0x22, 0xac, 0xce,
0x79, 0xf9, 0x43, 0x68, 0xc1, 0x03, 0x74, 0x2b,
0xe1, 0x18, 0xf8, 0x7f, 0x76, 0x9a, 0xea, 0x82,
0x3f, 0xc2, 0xa6, 0xa7, 0x4c, 0xfe, 0xae, 0x29,
0x3b, 0xc1, 0x10, 0x7c, 0xd5, 0x77, 0x17, 0x79,
0x5f, 0xcb, 0xad, 0x1f, 0xd8, 0xa1, 0xfd, 0x90,
0xe1, 0x6b, 0xb2, 0xef, 0xb9, 0x41, 0x26, 0xa4,
0x0b, 0x4f, 0xc6, 0x83, 0x05, 0x6f, 0xf0, 0x64,
0x40, 0xe1, 0x44, 0xc4, 0xf9, 0x40, 0x2b, 0x3b,
0x40, 0xdb, 0xaf, 0x35, 0xa4, 0x9b, 0x9f, 0xc4,
0x74, 0x07, 0xe5, 0x18, 0x60, 0xc5, 0xfe, 0x15,
0x0e, 0x3a, 0x25, 0x2a, 0x11, 0xee, 0x78, 0x2f,
0xb8, 0xd1, 0x6e, 0x4e, 0x3c, 0x0a, 0xb5, 0xb9,
0x40, 0x86, 0x27, 0x6d, 0x8f, 0x53, 0xb7, 0x77,
0x36, 0xec, 0x5d, 0xed, 0x32, 0x40, 0x43, 0x82,
0xc3, 0x52, 0x58, 0xc4, 0x26, 0x39, 0xf3, 0xb3,
0xad, 0x58, 0xab, 0xb7, 0xf7, 0x8e, 0x0e, 0xba,
0x8e, 0x78, 0x9d, 0xbf, 0x58, 0x34, 0xbd, 0x77,
0x73, 0xa6, 0x50, 0x55, 0x00, 0x60, 0x26, 0xbf,
0x6d, 0xb4, 0x98, 0x8a, 0x18, 0x83, 0x89, 0xf8,
0xcd, 0x0d, 0x49, 0x06, 0xae, 0x51, 0x6e, 0xaf,
0xbd, 0xe2, 0x07, 0x13, 0xd8, 0x64, 0xcc, 0xbf,
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e,
0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x34, 0x30,
0x82, 0x02, 0x30, 0x30, 0x82, 0x01, 0xda, 0xa0,
0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59,
0xf2, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
0x00, 0x30, 0x81, 0x9d, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b,
0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e,
0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14,
0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61,
0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18,
0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54,
0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79,
0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04,
0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x31,
0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65,
0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74,
0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d,
0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65,
0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, 0x38, 0x30,
0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x35, 0x34,
0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37,
0x31, 0x31, 0x32, 0x33, 0x30, 0x35, 0x34, 0x35,
0x34, 0x30, 0x5a, 0x30, 0x81, 0x9d, 0x31, 0x0b,
0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79,
0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f,
0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04,
0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67,
0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a,
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74,
0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65,
0x63, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03,
0x55, 0x04, 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72,
0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75,
0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e,
0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65,
0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30,
0x48, 0x02, 0x41, 0x00, 0x95, 0xb3, 0x47, 0x17,
0x95, 0x0f, 0x57, 0xcf, 0x66, 0x72, 0x0a, 0x7e,
0x5b, 0x54, 0xea, 0x8c, 0x6f, 0x79, 0xde, 0x94,
0xac, 0x0b, 0x5a, 0xd4, 0xd6, 0x1b, 0x58, 0x12,
0x1a, 0x16, 0x3d, 0xfe, 0xdf, 0xa5, 0x2b, 0x86,
0xbc, 0x64, 0xd4, 0x80, 0x1e, 0x3f, 0xf9, 0xe2,
0x04, 0x03, 0x79, 0x9b, 0xc1, 0x5c, 0xf0, 0xf1,
0xf3, 0xf1, 0xe3, 0xbf, 0x3f, 0xc0, 0x1f, 0xdd,
0xdb, 0xc0, 0x5b, 0x21, 0x02, 0x03, 0x01, 0x00,
0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
0x03, 0x41, 0x00, 0x02, 0xd7, 0xdd, 0xbd, 0x0c,
0x8e, 0x21, 0x20, 0xef, 0x9e, 0x4f, 0x1f, 0xf5,
0x49, 0xf1, 0xae, 0x58, 0x9b, 0x94, 0x3a, 0x1f,
0x70, 0x33, 0xf0, 0x9b, 0xbb, 0xe9, 0xc0, 0xf3,
0x72, 0xcb, 0xde, 0xb6, 0x56, 0x72, 0xcc, 0x1c,
0xf0, 0xd6, 0x5a, 0x2a, 0xbc, 0xa1, 0x7e, 0x23,
0x83, 0xe9, 0xe7, 0xcf, 0x9e, 0xa5, 0xf9, 0xcc,
0xc2, 0x61, 0xf4, 0xdb, 0x40, 0x93, 0x1d, 0x63,
0x8a, 0x50, 0x4c, 0x11, 0x39, 0xb1, 0x91, 0xc1,
0xe6, 0x9d, 0xd9, 0x1a, 0x62, 0x1b, 0xb8, 0xd3,
0xd6, 0x9a, 0x6d, 0xb9, 0x8e, 0x15, 0x51 };
public static InputStream asInputStream() {
byte[] data = new byte[DATA.length];
for (int i = 0; i < data.length; i ++) {
data[i] = (byte) DATA[i];
}
return new ByteArrayInputStream(data);
}
public static char[] getCertificatePassword() {
return "secret".toCharArray();
}
public static char[] getKeyStorePassword() {
return "secret".toCharArray();
}
private SecureChatKeyStore() {
// Unused
}
}

View File

@ -20,15 +20,19 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.example.telnet.TelnetServer;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* Simple SSL chat server modified from {@link TelnetServer}.
*/
public class SecureChatServer {
private final SslContext sslCtx;
private final int port;
public SecureChatServer(int port) {
public SecureChatServer(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -39,7 +43,7 @@ public class SecureChatServer {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new SecureChatServerInitializer());
.childHandler(new SecureChatServerInitializer(sslCtx));
b.bind(port).sync().channel().closeFuture().sync();
} finally {
@ -55,6 +59,11 @@ public class SecureChatServer {
} else {
port = 8443;
}
new SecureChatServer(port).run();
// Configure SSL context.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
new SecureChatServer(sslCtx, port).run();
}
}

View File

@ -22,15 +22,19 @@ import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine;
import io.netty.handler.ssl.SslContext;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public SecureChatServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
@ -43,12 +47,7 @@ public class SecureChatServerInitializer extends ChannelInitializer<SocketChanne
//
// Read SecureChatSslContextFactory
// if you need client certificate authentication.
SSLEngine engine =
SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc()));
// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(

View File

@ -1,108 +0,0 @@
/*
* Copyright 2012 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.securechat;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.internal.SystemPropertyUtil;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.Security;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
/**
* Creates a bogus {@link SSLContext}. A client-side context created by this
* factory accepts any certificate even if it is invalid. A server-side context
* created by this factory sends a bogus certificate defined in {@link SecureChatKeyStore}.
* <p>
* You will have to create your context differently in a real world application.
*
* <h3>Client Certificate Authentication</h3>
*
* To enable client certificate authentication:
* <ul>
* <li>Enable client authentication on the server side by calling
* {@link SSLEngine#setNeedClientAuth(boolean)} before creating
* {@link SslHandler}.</li>
* <li>When initializing an {@link SSLContext} on the client side,
* specify the {@link KeyManager} that contains the client certificate as
* the first argument of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}.</li>
* <li>When initializing an {@link SSLContext} on the server side,
* specify the proper {@link TrustManager} as the second argument of
* {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}
* to validate the client certificate.</li>
* </ul>
*/
public final class SecureChatSslContextFactory {
private static final String PROTOCOL = "TLS";
private static final SSLContext SERVER_CONTEXT;
private static final SSLContext CLIENT_CONTEXT;
static {
String algorithm = SystemPropertyUtil.get("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
SSLContext serverContext;
SSLContext clientContext;
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(SecureChatKeyStore.asInputStream(),
SecureChatKeyStore.getKeyStorePassword());
// Set up key manager factory to use our key store
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, SecureChatKeyStore.getCertificatePassword());
// Initialize the SSLContext to work with our key managers.
serverContext = SSLContext.getInstance(PROTOCOL);
serverContext.init(kmf.getKeyManagers(), null, null);
} catch (Exception e) {
throw new Error(
"Failed to initialize the server-side SSLContext", e);
}
try {
clientContext = SSLContext.getInstance(PROTOCOL);
clientContext.init(null, SecureChatTrustManagerFactory.getTrustManagers(), null);
} catch (Exception e) {
throw new Error(
"Failed to initialize the client-side SSLContext", e);
}
SERVER_CONTEXT = serverContext;
CLIENT_CONTEXT = clientContext;
}
public static SSLContext getServerContext() {
return SERVER_CONTEXT;
}
public static SSLContext getClientContext() {
return CLIENT_CONTEXT;
}
private SecureChatSslContextFactory() {
// Unused
}
}

View File

@ -1,77 +0,0 @@
/*
* Copyright 2012 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.securechat;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509TrustManager;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
/**
* Bogus {@link TrustManagerFactorySpi} which accepts any certificate
* even if it is invalid.
*/
public class SecureChatTrustManagerFactory extends TrustManagerFactorySpi {
private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// Always trust - it is an example.
// You should do something in the real world.
// You will reach here only if you enabled client certificate auth,
// as described in SecureChatSslContextFactory.
System.err.println(
"UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN());
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// Always trust - it is an example.
// You should do something in the real world.
System.err.println(
"UNKNOWN SERVER CERTIFICATE: " + chain[0].getSubjectDN());
}
};
public static TrustManager[] getTrustManagers() {
return new TrustManager[] { DUMMY_TRUST_MANAGER };
}
@Override
protected TrustManager[] engineGetTrustManagers() {
return getTrustManagers();
}
@Override
protected void engineInit(KeyStore keystore) throws KeyStoreException {
// Unused
}
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
throws InvalidAlgorithmParameterException {
// Unused
}
}

View File

@ -27,7 +27,11 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import javax.net.ssl.SSLException;
import java.net.InetSocketAddress;
import java.util.concurrent.BlockingQueue;
@ -48,13 +52,15 @@ import static java.util.concurrent.TimeUnit.*;
*/
public class SpdyClient {
private final SslContext sslCtx;
private final String host;
private final int port;
private final HttpResponseClientHandler httpResponseHandler;
private Channel channel;
private EventLoopGroup workerGroup;
public SpdyClient(String host, int port) {
public SpdyClient(String host, int port) throws SSLException {
sslCtx = SslContext.newClientContext(SslProvider.JDK, InsecureTrustManagerFactory.INSTANCE);
this.host = host;
this.port = port;
httpResponseHandler = new HttpResponseClientHandler();
@ -73,7 +79,7 @@ public class SpdyClient {
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.remoteAddress(new InetSocketAddress(host, port));
b.handler(new SpdyClientInitializer(httpResponseHandler));
b.handler(new SpdyClientInitializer(sslCtx, httpResponseHandler));
// Start the client.
channel = b.connect().syncUninterruptibly().channel();

View File

@ -18,11 +18,11 @@ package io.netty.example.spdy.client;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.codec.spdy.SpdyFrameCodec;
import io.netty.handler.codec.spdy.SpdyHttpDecoder;
import io.netty.handler.codec.spdy.SpdyHttpEncoder;
import io.netty.handler.codec.spdy.SpdySessionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.eclipse.jetty.npn.NextProtoNego;
@ -33,9 +33,11 @@ import static io.netty.util.internal.logging.InternalLogLevel.*;
public class SpdyClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
private final HttpResponseClientHandler httpResponseHandler;
public SpdyClientInitializer(HttpResponseClientHandler httpResponseHandler) {
public SpdyClientInitializer(SslContext sslCtx, HttpResponseClientHandler httpResponseHandler) {
this.sslCtx = sslCtx;
this.httpResponseHandler = httpResponseHandler;
}
@ -43,14 +45,14 @@ public class SpdyClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) throws Exception {
SSLEngine engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
SSLEngine engine = sslHandler.engine();
NextProtoNego.put(engine, new SpdyClientProvider());
NextProtoNego.debug = true;
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("ssl", sslHandler);
pipeline.addLast("spdyFrameCodec", new SpdyFrameCodec(SPDY_3_1));
pipeline.addLast("spdyFrameLogger", new SpdyFrameLogger(INFO));
pipeline.addLast("spdySessionHandler", new SpdySessionHandler(SPDY_3_1, false));

View File

@ -21,6 +21,8 @@ import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
/**
* A SPDY Server that responds to a GET request with a Hello World.
@ -42,9 +44,11 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
*/
public class SpdyServer {
private final SslContext sslCtx;
private final int port;
public SpdyServer(int port) {
public SpdyServer(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -56,7 +60,7 @@ public class SpdyServer {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new SpdyServerInitializer());
.childHandler(new SpdyServerInitializer(sslCtx));
Channel ch = b.bind(port).sync().channel();
ch.closeFuture().sync();
@ -79,7 +83,10 @@ public class SpdyServer {
System.out.println("Open your SPDY enabled browser and navigate to https://localhost:" + port + '/');
System.out.println("If using Chrome browser, check your SPDY sessions at chrome://net-internals/#spdy");
new SpdyServer(port).run();
// Configure SSL.
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
new SpdyServer(sslCtx, port).run();
}
private static void checkForNpnSupport() {

View File

@ -18,7 +18,7 @@ package io.netty.example.spdy.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.example.securechat.SecureChatSslContextFactory;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import org.eclipse.jetty.npn.NextProtoNego;
@ -28,12 +28,19 @@ import javax.net.ssl.SSLEngine;
* Sets up the Netty pipeline
*/
public class SpdyServerInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public SpdyServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
SslHandler sslHandler = sslCtx.newHandler(ch.alloc());
SSLEngine engine = sslHandler.engine();
p.addLast("ssl", new SslHandler(engine));
// Setup NextProtoNego with our server provider

View File

@ -44,6 +44,12 @@
<artifactId>netty-codec</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-tcnative</artifactId>
<classifier>${os.detected.classifier}</classifier>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>

View File

@ -0,0 +1,34 @@
/*
* 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 java.util.List;
/**
* Selects an application layer protocol in TLS <a href="http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04">NPN
* (Next Protocol Negotiation)</a> or <a href="https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-05">ALPN
* (Application Layer Protocol Negotiation)</a>.
*/
public interface ApplicationProtocolSelector {
/**
* Invoked to select a protocol from the list of specified application layer protocols.
*
* @param protocols the list of application layer protocols sent by the server.
* The list is empty if the server supports neither NPN nor ALPM.
* @return the selected protocol. {@code null} if no protocol was selected.
*/
String selectProtocol(List<String> protocols) throws Exception;
}

View File

@ -0,0 +1,174 @@
/*
* 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.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;
/**
* A client-side {@link SslContext} which uses JDK's SSL/TLS implementation.
*/
public final class JdkSslClientContext extends JdkSslContext {
private final SSLContext ctx;
/**
* Creates a new instance.
*/
public JdkSslClientContext() throws SSLException {
this(null, null, null, null, 0, 0);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
*/
public JdkSslClientContext(File certChainFile) throws SSLException {
this(certChainFile, null);
}
/**
* Creates a new instance.
*
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*/
public JdkSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
this(null, trustManagerFactory);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*/
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
this(certChainFile, trustManagerFactory, null, null, 0, 0);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocolSelector the {@link ApplicationProtocolSelector} that chooses one of the application layer
* protocols returned by a TLS server.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*/
public JdkSslClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers);
if (nextProtocolSelector != null) {
throw new SSLException("NPN/ALPN unsupported: " + nextProtocolSelector);
}
try {
if (certChainFile == null) {
ctx = SSLContext.getInstance(PROTOCOL);
if (trustManagerFactory == null) {
ctx.init(null, null, null);
} else {
trustManagerFactory.init((KeyStore) null);
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
}
} else {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
for (ByteBuf buf: PemReader.readCertificates(certChainFile)) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(buf));
X500Principal principal = cert.getSubjectX500Principal();
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
}
// Set up trust manager factory to use our key store.
if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
}
trustManagerFactory.init(ks);
// Initialize the SSLContext to work with the trust managers.
ctx = SSLContext.getInstance(PROTOCOL);
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
}
SSLSessionContext sessCtx = ctx.getClientSessionContext();
if (sessionCacheSize > 0) {
sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
}
if (sessionTimeout > 0) {
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
}
} catch (Exception e) {
throw new SSLException("failed to initialize the server-side SSL context", e);
}
}
@Override
public boolean isClient() {
return true;
}
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
}
@Override
public List<String> nextProtocols() {
return Collections.emptyList();
}
@Override
public SSLContext context() {
return ctx;
}
}

View File

@ -0,0 +1,178 @@
/*
* 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.buffer.ByteBufAllocator;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSessionContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* An {@link SslContext} which uses JDK's SSL/TLS implementation.
*/
public abstract class JdkSslContext extends SslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class);
static final String PROTOCOL = "TLS";
static final String[] PROTOCOLS;
static final List<String> DEFAULT_CIPHERS;
static {
SSLContext context;
try {
context = SSLContext.getInstance(PROTOCOL);
context.init(null, null, null);
} catch (Exception e) {
throw new Error("failed to initialize the default SSL context", e);
}
SSLEngine engine = context.createSSLEngine();
// Choose the sensible default list of protocols.
String[] supportedProtocols = engine.getSupportedProtocols();
List<String> protocols = new ArrayList<String>();
addIfSupported(
supportedProtocols, protocols,
"TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3");
if (!protocols.isEmpty()) {
PROTOCOLS = protocols.toArray(new String[protocols.size()]);
} else {
PROTOCOLS = engine.getEnabledProtocols();
}
// Choose the sensible default list of cipher suites.
String[] supportedCiphers = engine.getSupportedCipherSuites();
List<String> ciphers = new ArrayList<String>();
addIfSupported(
supportedCiphers, ciphers,
// XXX: Make sure to sync this list with OpenSslEngineFactory.
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // since JDK 8
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256", // since JDK 8
"SSL_RSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_RC4_128_MD5",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"SSL_RSA_WITH_DES_CBC_SHA");
if (!ciphers.isEmpty()) {
DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
} else {
// Use the default from JDK as fallback.
DEFAULT_CIPHERS = Collections.unmodifiableList(Arrays.asList(engine.getEnabledCipherSuites()));
}
if (logger.isDebugEnabled()) {
logger.debug("Default protocols (JDK): {} ", Arrays.asList(PROTOCOLS));
logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
}
}
private static void addIfSupported(String[] supported, List<String> enabled, String... names) {
for (String n: names) {
for (String s: supported) {
if (n.equals(s)) {
enabled.add(s);
break;
}
}
}
}
private final String[] cipherSuites;
private final List<String> unmodifiableCipherSuites;
JdkSslContext(Iterable<String> ciphers) {
cipherSuites = toCipherSuiteArray(ciphers);
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
}
/**
* Returns the JDK {@link SSLContext} object held by this context.
*/
public abstract SSLContext context();
/**
* Returns the JDK {@link SSLSessionContext} object held by this context.
*/
public final SSLSessionContext sessionContext() {
if (isServer()) {
return context().getServerSessionContext();
} else {
return context().getClientSessionContext();
}
}
@Override
public final List<String> cipherSuites() {
return unmodifiableCipherSuites;
}
@Override
public final long sessionCacheSize() {
return sessionContext().getSessionCacheSize();
}
@Override
public final long sessionTimeout() {
return sessionContext().getSessionTimeout();
}
@Override
public final SSLEngine newEngine(ByteBufAllocator alloc) {
SSLEngine engine = context().createSSLEngine();
engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient());
return engine;
}
@Override
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
SSLEngine engine = context().createSSLEngine(peerHost, peerPort);
engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient());
return engine;
}
private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
if (ciphers == null) {
return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
} else {
List<String> newCiphers = new ArrayList<String>();
for (String c: ciphers) {
if (c == null) {
break;
}
newCiphers.add(c);
}
return newCiphers.toArray(new String[newCiphers.size()]);
}
}
}

View File

@ -0,0 +1,176 @@
/*
* 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.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import java.io.File;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A server-side {@link SslContext} which uses JDK's SSL/TLS implementation.
*/
public final class JdkSslServerContext extends JdkSslContext {
private final SSLContext ctx;
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
*/
public JdkSslServerContext(File certChainFile, File keyFile) throws SSLException {
this(certChainFile, keyFile, null);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
*/
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
this(certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*/
public JdkSslServerContext(
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers);
if (certChainFile == null) {
throw new NullPointerException("certChainFile");
}
if (keyFile == null) {
throw new NullPointerException("keyFile");
}
if (keyPassword == null) {
keyPassword = "";
}
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
}
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
KeyFactory rsaKF = KeyFactory.getInstance("RSA");
KeyFactory dsaKF = KeyFactory.getInstance("DSA");
ByteBuf encodedKeyBuf = PemReader.readPrivateKey(keyFile);
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
encodedKeyBuf.readBytes(encodedKey);
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
PrivateKey key;
try {
key = rsaKF.generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException ignore) {
key = dsaKF.generatePrivate(encodedKeySpec);
}
List<Certificate> certChain = new ArrayList<Certificate>();
for (ByteBuf buf: PemReader.readCertificates(certChainFile)) {
certChain.add(cf.generateCertificate(new ByteBufInputStream(buf)));
}
ks.setKeyEntry("key", key, keyPassword.toCharArray(), certChain.toArray(new Certificate[certChain.size()]));
// Set up key manager factory to use our key store
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, keyPassword.toCharArray());
// Initialize the SSLContext to work with our key managers.
ctx = SSLContext.getInstance(PROTOCOL);
ctx.init(kmf.getKeyManagers(), null, null);
SSLSessionContext sessCtx = ctx.getServerSessionContext();
if (sessionCacheSize > 0) {
sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
}
if (sessionTimeout > 0) {
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
}
} catch (Exception e) {
throw new SSLException("failed to initialize the server-side SSL context", e);
}
}
@Override
public boolean isClient() {
return false;
}
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
}
@Override
public List<String> nextProtocols() {
return Collections.emptyList();
}
@Override
public SSLContext context() {
return ctx;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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.NativeLibraryLoader;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.Library;
import org.apache.tomcat.jni.SSL;
/**
* Tells if <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
* are available.
*/
public final class OpenSsl {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class);
private static final Throwable UNAVAILABILITY_CAUSE;
static final String IGNORABLE_ERROR_PREFIX = "error:00000000:";
static {
Throwable cause = null;
try {
NativeLibraryLoader.load("netty-tcnative", SSL.class.getClassLoader());
Library.initialize("provided");
SSL.initialize(null);
} catch (Throwable t) {
cause = t;
logger.debug(
"Failed to load netty-tcnative; " +
OpenSslEngine.class.getSimpleName() + " will be unavailable.", t);
}
UNAVAILABILITY_CAUSE = cause;
}
/**
* Returns {@code true} if and only if
* <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
* are available.
*/
public static boolean isAvailable() {
return UNAVAILABILITY_CAUSE == null;
}
/**
* Ensure that <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and
* its OpenSSL support are available.
*
* @throws UnsatisfiedLinkError if unavailable
*/
public static void ensureAvailability() {
if (UNAVAILABILITY_CAUSE != null) {
throw (Error) new UnsatisfiedLinkError(
"failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
}
}
/**
* Returns the cause of unavailability of
* <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support.
*
* @return the cause if unavailable. {@code null} if available.
*/
public static Throwable unavailabilityCause() {
return UNAVAILABILITY_CAUSE;
}
private OpenSsl() { }
}

View File

@ -0,0 +1,867 @@
/*
* 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.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.Buffer;
import org.apache.tomcat.jni.SSL;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.security.cert.X509Certificate;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
import static javax.net.ssl.SSLEngineResult.Status.*;
/**
* Implements a {@link SSLEngine} using
* <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL BIO abstractions</a>.
*/
public final class OpenSslEngine extends SSLEngine {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslEngine.class);
private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
private static final X509Certificate[] EMPTY_X509_CERTIFICATES = new X509Certificate[0];
private static final SSLException ENGINE_CLOSED = new SSLException("engine closed");
private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException("renegotiation unsupported");
private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException("encrypted packet oversized");
static {
ENGINE_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
RENEGOTIATION_UNSUPPORTED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
ENCRYPTED_PACKET_OVERSIZED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}
private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024;
private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
// Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
// OpenSSL state
private long ssl;
private long networkBIO;
/**
* 0 - not accepted, 1 - accepted implicitly via wrap()/unwrap(), 2 - accepted explicitly via beginHandshake() call
*/
private int accepted;
private boolean handshakeFinished;
private boolean receivedShutdown;
@SuppressWarnings("UnusedDeclaration")
private volatile int destroyed;
private String cipher;
private String protocol;
// SSL Engine status variables
private boolean isInboundDone;
private boolean isOutboundDone;
private boolean engineClosed;
private int lastPrimingReadResult;
private final ByteBufAllocator alloc;
private SSLSession session;
/**
* Creates a new instance
*
* @param sslCtx an OpenSSL {@code SSL_CTX} object
* @param alloc the {@link ByteBufAllocator} that will be used by this engine
*/
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc) {
OpenSsl.ensureAvailability();
if (sslCtx == 0) {
throw new NullPointerException("sslContext");
}
if (alloc == null) {
throw new NullPointerException("alloc");
}
this.alloc = alloc;
ssl = SSL.newSSL(sslCtx, true);
networkBIO = SSL.makeNetworkBIO(ssl);
}
/**
* Destroys this engine.
*/
public synchronized void shutdown() {
if (DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
SSL.freeSSL(ssl);
SSL.freeBIO(networkBIO);
ssl = networkBIO = 0;
// internal errors can cause shutdown without marking the engine closed
isInboundDone = isOutboundDone = engineClosed = true;
}
}
/**
* Write plaintext data to the OpenSSL internal BIO
*
* Calling this function with src.remaining == 0 is undefined.
*/
private int writePlaintextData(final ByteBuffer src) {
final int pos = src.position();
final int limit = src.limit();
final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
final int sslWrote;
if (src.isDirect()) {
final long addr = Buffer.address(src) + pos;
sslWrote = SSL.writeToSSL(ssl, addr, len);
if (sslWrote > 0) {
src.position(pos + sslWrote);
return sslWrote;
}
} else {
ByteBuf buf = alloc.directBuffer(len);
try {
final long addr;
if (buf.hasMemoryAddress()) {
addr = buf.memoryAddress();
} else {
addr = Buffer.address(buf.nioBuffer());
}
src.limit(pos + len);
buf.setBytes(0, src);
src.limit(limit);
sslWrote = SSL.writeToSSL(ssl, addr, len);
if (sslWrote > 0) {
src.position(pos + sslWrote);
return sslWrote;
} else {
src.position(pos);
}
} finally {
buf.release();
}
}
throw new IllegalStateException("SSL.writeToSSL() returned a non-positive value: " + sslWrote);
}
/**
* Write encrypted data to the OpenSSL network BIO
*/
private int writeEncryptedData(final ByteBuffer src) {
final int pos = src.position();
final int len = src.remaining();
if (src.isDirect()) {
final long addr = Buffer.address(src) + pos;
final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
if (netWrote >= 0) {
src.position(pos + netWrote);
lastPrimingReadResult = SSL.readFromSSL(ssl, addr, 0); // priming read
return netWrote;
}
} else {
final ByteBuf buf = alloc.directBuffer(len);
try {
final long addr;
if (buf.hasMemoryAddress()) {
addr = buf.memoryAddress();
} else {
addr = Buffer.address(buf.nioBuffer());
}
buf.setBytes(0, src);
final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
if (netWrote >= 0) {
src.position(pos + netWrote);
lastPrimingReadResult = SSL.readFromSSL(ssl, addr, 0); // priming read
return netWrote;
} else {
src.position(pos);
}
} finally {
buf.release();
}
}
return 0;
}
/**
* Read plaintext data from the OpenSSL internal BIO
*/
private int readPlaintextData(final ByteBuffer dst) {
if (dst.isDirect()) {
final int pos = dst.position();
final long addr = Buffer.address(dst) + pos;
final int len = dst.limit() - pos;
final int sslRead = SSL.readFromSSL(ssl, addr, len);
if (sslRead > 0) {
dst.position(pos + sslRead);
return sslRead;
}
} else {
final int pos = dst.position();
final int limit = dst.limit();
final int len = Math.min(MAX_ENCRYPTED_PACKET_LENGTH, limit - pos);
final ByteBuf buf = alloc.directBuffer(len);
try {
final long addr;
if (buf.hasMemoryAddress()) {
addr = buf.memoryAddress();
} else {
addr = Buffer.address(buf.nioBuffer());
}
final int sslRead = SSL.readFromSSL(ssl, addr, len);
if (sslRead > 0) {
dst.limit(pos + sslRead);
buf.getBytes(0, dst);
dst.limit(limit);
return sslRead;
}
} finally {
buf.release();
}
}
return 0;
}
/**
* Read encrypted data from the OpenSSL network BIO
*/
private int readEncryptedData(final ByteBuffer dst, final int pending) {
if (dst.isDirect() && dst.remaining() >= pending) {
final int pos = dst.position();
final long addr = Buffer.address(dst) + pos;
final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
if (bioRead > 0) {
dst.position(pos + bioRead);
return bioRead;
}
} else {
final ByteBuf buf = alloc.directBuffer(pending);
try {
final long addr;
if (buf.hasMemoryAddress()) {
addr = buf.memoryAddress();
} else {
addr = Buffer.address(buf.nioBuffer());
}
final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
if (bioRead > 0) {
int oldLimit = dst.limit();
dst.limit(dst.position() + bioRead);
buf.getBytes(0, dst);
dst.limit(oldLimit);
return bioRead;
}
} finally {
buf.release();
}
}
return 0;
}
@Override
public synchronized SSLEngineResult wrap(
final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
// Check to make sure the engine has not been closed
if (destroyed != 0) {
return new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
}
// Throw required runtime exceptions
if (srcs == null) {
throw new NullPointerException("srcs");
}
if (dst == null) {
throw new NullPointerException("dst");
}
if (offset >= srcs.length || offset + length > srcs.length) {
throw new IndexOutOfBoundsException(
"offset: " + offset + ", length: " + length +
" (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))");
}
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
// Prepare OpenSSL to work in server mode and receive handshake
if (accepted == 0) {
beginHandshakeImplicitly();
}
// In handshake or close_notify stages, check if call to wrap was made
// without regard to the handshake status.
SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_UNWRAP) {
return new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, 0, 0);
}
int bytesProduced = 0;
int pendingNet;
// Check for pending data in the network BIO
pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
if (pendingNet > 0) {
// Do we have enough room in dst to write encrypted data?
int capacity = dst.remaining();
if (capacity < pendingNet) {
return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus, 0, bytesProduced);
}
// Write the pending data from the network BIO into the dst buffer
try {
bytesProduced += readEncryptedData(dst, pendingNet);
} catch (Exception e) {
throw new SSLException(e);
}
// If isOuboundDone is set, then the data from the network BIO
// was the close_notify message -- we are not required to wait
// for the receipt the peer's close_notify message -- shutdown.
if (isOutboundDone) {
shutdown();
}
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
}
// There was no pending data in the network BIO -- encrypt any application data
int bytesConsumed = 0;
for (int i = offset; i < length; ++ i) {
final ByteBuffer src = srcs[i];
while (src.hasRemaining()) {
// Write plaintext application data to the SSL engine
try {
bytesConsumed += writePlaintextData(src);
} catch (Exception e) {
throw new SSLException(e);
}
// Check to see if the engine wrote data into the network BIO
pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
if (pendingNet > 0) {
// Do we have enough room in dst to write encrypted data?
int capacity = dst.remaining();
if (capacity < pendingNet) {
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
}
// Write the pending data from the network BIO into the dst buffer
try {
bytesProduced += readEncryptedData(dst, pendingNet);
} catch (Exception e) {
throw new SSLException(e);
}
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
}
}
}
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
}
@Override
public synchronized SSLEngineResult unwrap(
final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException {
// Check to make sure the engine has not been closed
if (destroyed != 0) {
return new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
}
// Throw requried runtime exceptions
if (src == null) {
throw new NullPointerException("src");
}
if (dsts == null) {
throw new NullPointerException("dsts");
}
if (offset >= dsts.length || offset + length > dsts.length) {
throw new IndexOutOfBoundsException(
"offset: " + offset + ", length: " + length +
" (expected: offset <= offset + length <= dsts.length (" + dsts.length + "))");
}
int capacity = 0;
final int endOffset = offset + length;
for (int i = offset; i < endOffset; i ++) {
ByteBuffer dst = dsts[i];
if (dst == null) {
throw new IllegalArgumentException();
}
if (dst.isReadOnly()) {
throw new ReadOnlyBufferException();
}
capacity += dst.remaining();
}
// Prepare OpenSSL to work in server mode and receive handshake
if (accepted == 0) {
beginHandshakeImplicitly();
}
// In handshake or close_notify stages, check if call to unwrap was made
// without regard to the handshake status.
SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_WRAP) {
return new SSLEngineResult(getEngineStatus(), NEED_WRAP, 0, 0);
}
// protect against protocol overflow attack vector
if (src.remaining() > MAX_ENCRYPTED_PACKET_LENGTH) {
isInboundDone = true;
isOutboundDone = true;
engineClosed = true;
shutdown();
throw ENCRYPTED_PACKET_OVERSIZED;
}
// Write encrypted data to network BIO
int bytesConsumed = 0;
lastPrimingReadResult = 0;
try {
bytesConsumed += writeEncryptedData(src);
} catch (Exception e) {
throw new SSLException(e);
}
// Check for OpenSSL errors caused by the priming read
String error = SSL.getLastError();
if (error != null && !error.startsWith(OpenSsl.IGNORABLE_ERROR_PREFIX)) {
if (logger.isInfoEnabled()) {
logger.info(
"SSL_read failed: primingReadResult: " + lastPrimingReadResult +
"; OpenSSL error: '" + error + '\'');
}
// There was an internal error -- shutdown
shutdown();
throw new SSLException(error);
}
// There won't be any application data until we're done handshaking
int pendingApp = SSL.isInInit(ssl) == 0 ? SSL.pendingReadableBytesInSSL(ssl) : 0;
// Do we have enough room in dsts to write decrypted data?
if (capacity < pendingApp) {
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, 0);
}
// Write decrypted data to dsts buffers
int bytesProduced = 0;
int idx = offset;
while (idx < endOffset) {
ByteBuffer dst = dsts[idx];
if (!dst.hasRemaining()) {
idx ++;
continue;
}
if (pendingApp <= 0) {
break;
}
int bytesRead;
try {
bytesRead = readPlaintextData(dst);
} catch (Exception e) {
throw new SSLException(e);
}
if (bytesRead == 0) {
break;
}
bytesProduced += bytesRead;
pendingApp -= bytesRead;
if (!dst.hasRemaining()) {
idx ++;
}
}
// Check to see if we received a close_notify message from the peer
if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) {
receivedShutdown = true;
closeOutbound();
closeInbound();
}
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
}
@Override
public Runnable getDelegatedTask() {
// Currently, we do not delegate SSL computation tasks
// TODO: in the future, possibly create tasks to do encrypt / decrypt async
return null;
}
@Override
public synchronized void closeInbound() throws SSLException {
if (isInboundDone) {
return;
}
isInboundDone = true;
engineClosed = true;
if (accepted != 0) {
if (!receivedShutdown) {
shutdown();
throw new SSLException(
"Inbound closed before receiving peer's close_notify: possible truncation attack?");
}
} else {
// engine closing before initial handshake
shutdown();
}
}
@Override
public synchronized boolean isInboundDone() {
return isInboundDone || engineClosed;
}
@Override
public synchronized void closeOutbound() {
if (isOutboundDone) {
return;
}
isOutboundDone = true;
engineClosed = true;
if (accepted != 0 && destroyed == 0) {
int mode = SSL.getShutdown(ssl);
if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
SSL.shutdownSSL(ssl);
}
} else {
// engine closing before initial handshake
shutdown();
}
}
@Override
public synchronized boolean isOutboundDone() {
return isOutboundDone;
}
@Override
public String[] getSupportedCipherSuites() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public String[] getEnabledCipherSuites() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public void setEnabledCipherSuites(String[] strings) {
throw new UnsupportedOperationException();
}
@Override
public String[] getSupportedProtocols() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public String[] getEnabledProtocols() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public void setEnabledProtocols(String[] strings) {
throw new UnsupportedOperationException();
}
@Override
public SSLSession getSession() {
SSLSession session = this.session;
if (session == null) {
this.session = session = new SSLSession() {
@Override
public byte[] getId() {
return String.valueOf(ssl).getBytes();
}
@Override
public SSLSessionContext getSessionContext() {
return null;
}
@Override
public long getCreationTime() {
return 0;
}
@Override
public long getLastAccessedTime() {
return 0;
}
@Override
public void invalidate() {
}
@Override
public boolean isValid() {
return false;
}
@Override
public void putValue(String s, Object o) {
}
@Override
public Object getValue(String s) {
return null;
}
@Override
public void removeValue(String s) {
}
@Override
public String[] getValueNames() {
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public Certificate[] getPeerCertificates() {
return EMPTY_CERTIFICATES;
}
@Override
public Certificate[] getLocalCertificates() {
return EMPTY_CERTIFICATES;
}
@Override
public X509Certificate[] getPeerCertificateChain() {
return EMPTY_X509_CERTIFICATES;
}
@Override
public Principal getPeerPrincipal() {
return null;
}
@Override
public Principal getLocalPrincipal() {
return null;
}
@Override
public String getCipherSuite() {
return cipher;
}
@Override
public String getProtocol() {
return protocol;
}
@Override
public String getPeerHost() {
return null;
}
@Override
public int getPeerPort() {
return 0;
}
@Override
public int getPacketBufferSize() {
return MAX_ENCRYPTED_PACKET_LENGTH;
}
@Override
public int getApplicationBufferSize() {
return MAX_PLAINTEXT_LENGTH;
}
};
}
return session;
}
@Override
public synchronized void beginHandshake() throws SSLException {
if (engineClosed) {
throw ENGINE_CLOSED;
}
switch (accepted) {
case 0:
SSL.doHandshake(ssl);
accepted = 2;
break;
case 1:
// A user did not start handshake by calling this method by him/herself,
// but handshake has been started already by wrap() or unwrap() implicitly.
// Because it's the user's first time to call this method, it is unfair to
// raise an exception. From the user's standpoint, he or she never asked
// for renegotiation.
accepted = 2; // Next time this method is invoked by the user, we should raise an exception.
break;
case 2:
throw RENEGOTIATION_UNSUPPORTED;
default:
throw new Error();
}
}
private synchronized void beginHandshakeImplicitly() throws SSLException {
if (engineClosed) {
throw ENGINE_CLOSED;
}
if (accepted == 0) {
SSL.doHandshake(ssl);
accepted = 1;
}
}
private SSLEngineResult.Status getEngineStatus() {
return engineClosed? CLOSED : OK;
}
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
if (accepted == 0 || destroyed != 0) {
return NOT_HANDSHAKING;
}
// Check if we are in the initial handshake phase
if (!handshakeFinished) {
// There is pending data in the network BIO -- call wrap
if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
return NEED_WRAP;
}
// No pending data to be sent to the peer
// Check to see if we have finished handshaking
if (SSL.isInInit(ssl) == 0) {
handshakeFinished = true;
cipher = SSL.getCipherForSSL(ssl);
protocol = SSL.getNextProtoNegotiated(ssl);
return FINISHED;
}
// No pending data and still handshaking
// Must be waiting on the peer to send more data
return NEED_UNWRAP;
}
// Check if we are in the shutdown phase
if (engineClosed) {
// Waiting to send the close_notify message
if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
return NEED_WRAP;
}
// Must be waiting to receive the close_notify message
return NEED_UNWRAP;
}
return NOT_HANDSHAKING;
}
@Override
public void setUseClientMode(boolean clientMode) {
if (clientMode) {
throw new UnsupportedOperationException();
}
}
@Override
public boolean getUseClientMode() {
return false;
}
@Override
public void setNeedClientAuth(boolean b) {
if (b) {
throw new UnsupportedOperationException();
}
}
@Override
public boolean getNeedClientAuth() {
return false;
}
@Override
public void setWantClientAuth(boolean b) {
if (b) {
throw new UnsupportedOperationException();
}
}
@Override
public boolean getWantClientAuth() {
return false;
}
@Override
public void setEnableSessionCreation(boolean b) {
if (b) {
throw new UnsupportedOperationException();
}
}
@Override
public boolean getEnableSessionCreation() {
return false;
}
}

View File

@ -0,0 +1,349 @@
/*
* 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.buffer.ByteBufAllocator;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.Pool;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
*/
public final class OpenSslServerContext extends SslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslServerContext.class);
private static final List<String> DEFAULT_CIPHERS;
static {
List<String> ciphers = new ArrayList<String>();
// XXX: Make sure to sync this list with JdkSslEngineFactory.
Collections.addAll(
ciphers,
"ECDHE-RSA-AES128-GCM-SHA256",
"ECDHE-RSA-RC4-SHA",
"ECDHE-RSA-AES128-SHA",
"ECDHE-RSA-AES256-SHA",
"AES128-GCM-SHA256",
"RC4-SHA",
"RC4-MD5",
"AES128-SHA",
"AES256-SHA",
"DES-CBC3-SHA");
DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
if (logger.isDebugEnabled()) {
logger.debug("Default cipher suite (OpenSSL): " + ciphers);
}
}
private final long aprPool;
private final List<String> ciphers = new ArrayList<String>();
private final List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers);
private final long sessionCacheSize;
private final long sessionTimeout;
private final List<String> nextProtocols = new ArrayList<String>();
private final List<String> unmodifiableNextProtocols = Collections.unmodifiableList(nextProtocols);
/** The OpenSSL SSL_CTX object */
private final long ctx;
private final OpenSslSessionStats stats;
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
*/
public OpenSslServerContext(File certChainFile, File keyFile) throws SSLException {
this(certChainFile, keyFile, null);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
*/
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
this(certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
/**
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*/
public OpenSslServerContext(
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
OpenSsl.ensureAvailability();
if (certChainFile == null) {
throw new NullPointerException("certChainFile");
}
if (!certChainFile.isFile()) {
throw new IllegalArgumentException("certChainFile is not a file: " + certChainFile);
}
if (keyFile == null) {
throw new NullPointerException("keyPath");
}
if (!keyFile.isFile()) {
throw new IllegalArgumentException("keyPath is not a file: " + keyFile);
}
if (ciphers == null) {
ciphers = DEFAULT_CIPHERS;
}
if (keyPassword == null) {
keyPassword = "";
}
if (nextProtocols == null) {
nextProtocols = Collections.emptyList();
}
for (String c: ciphers) {
if (c == null) {
break;
}
this.ciphers.add(c);
}
for (String p: nextProtocols) {
if (p == null) {
break;
}
this.nextProtocols.add(p);
}
// Allocate a new APR pool.
aprPool = Pool.create(0);
// Create a new SSL_CTX and configure it.
boolean success = false;
try {
synchronized (OpenSslServerContext.class) {
try {
ctx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
} catch (Exception e) {
throw new SSLException("failed to create an SSL_CTX", e);
}
SSLContext.setOptions(ctx, SSL.SSL_OP_ALL);
SSLContext.setOptions(ctx, SSL.SSL_OP_NO_SSLv2);
SSLContext.setOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
SSLContext.setOptions(ctx, SSL.SSL_OP_SINGLE_ECDH_USE);
SSLContext.setOptions(ctx, SSL.SSL_OP_SINGLE_DH_USE);
SSLContext.setOptions(ctx, SSL.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
/* List the ciphers that the client is permitted to negotiate. */
try {
// Convert the cipher list into a colon-separated string.
StringBuilder cipherBuf = new StringBuilder();
for (String c: this.ciphers) {
cipherBuf.append(c);
cipherBuf.append(':');
}
cipherBuf.setLength(cipherBuf.length() - 1);
SSLContext.setCipherSuite(ctx, cipherBuf.toString());
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("failed to set cipher suite: " + this.ciphers, e);
}
/* Set certificate verification policy. */
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, 10);
/* Load the certificate file and private key. */
try {
if (!SSLContext.setCertificate(
ctx, certChainFile.getPath(), keyFile.getPath(), keyPassword, SSL.SSL_AIDX_RSA)) {
throw new SSLException("failed to set certificate: " +
certChainFile + " and " + keyFile + " (" + SSL.getLastError() + ')');
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("failed to set certificate: " + certChainFile + " and " + keyFile, e);
}
/* Load the certificate chain. We must skip the first cert since it was loaded above. */
if (!SSLContext.setCertificateChainFile(ctx, certChainFile.getPath(), true)) {
String error = SSL.getLastError();
if (!error.startsWith(OpenSsl.IGNORABLE_ERROR_PREFIX)) {
throw new SSLException(
"failed to set certificate chain: " + certChainFile + " (" + SSL.getLastError() + ')');
}
}
/* Set next protocols for next protocol negotiation extension, if specified */
if (!this.nextProtocols.isEmpty()) {
// Convert the protocol list into a comma-separated string.
StringBuilder nextProtocolBuf = new StringBuilder();
for (String p: this.nextProtocols) {
nextProtocolBuf.append(p);
nextProtocolBuf.append(',');
}
nextProtocolBuf.setLength(nextProtocolBuf.length() - 1);
SSLContext.setNextProtos(ctx, nextProtocolBuf.toString());
}
/* Set session cache size, if specified */
if (sessionCacheSize > 0) {
this.sessionCacheSize = sessionCacheSize;
SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
} else {
// Get the default session cache size using SSLContext.setSessionCacheSize()
this.sessionCacheSize = sessionCacheSize = SSLContext.setSessionCacheSize(ctx, 20480);
// Revert the session cache size to the default value.
SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
}
/* Set session timeout, if specified */
if (sessionTimeout > 0) {
this.sessionTimeout = sessionTimeout;
SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
} else {
// Get the default session timeout using SSLContext.setSessionCacheTimeout()
this.sessionTimeout = sessionTimeout = SSLContext.setSessionCacheTimeout(ctx, 300);
// Revert the session timeout to the default value.
SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
}
}
success = true;
} finally {
if (!success) {
destroyPools();
}
}
stats = new OpenSslSessionStats(ctx);
}
@Override
public boolean isClient() {
return false;
}
@Override
public List<String> cipherSuites() {
return unmodifiableCiphers;
}
@Override
public long sessionCacheSize() {
return sessionCacheSize;
}
@Override
public long sessionTimeout() {
return sessionTimeout;
}
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
}
@Override
public List<String> nextProtocols() {
return unmodifiableNextProtocols;
}
/**
* Returns the {@code SSL_CTX} object of this context.
*/
public long context() {
return ctx;
}
/**
* Returns the stats of this context.
*/
public OpenSslSessionStats stats() {
return stats;
}
/**
* Returns a new server-side {@link javax.net.ssl.SSLEngine} with the current configuration.
*/
@Override
public SSLEngine newEngine(ByteBufAllocator alloc) {
return new OpenSslEngine(ctx, alloc);
}
@Override
public SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
throw new UnsupportedOperationException();
}
/**
* Sets the SSL session ticket keys of this context.
*/
public void setTicketKeys(byte[] keys) {
if (keys != null) {
throw new NullPointerException("keys");
}
SSLContext.setSessionTicketKeys(ctx, keys);
}
@Override
@SuppressWarnings("FinalizeDeclaration")
protected void finalize() throws Throwable {
super.finalize();
synchronized (OpenSslServerContext.class) {
if (ctx != 0) {
SSLContext.free(ctx);
}
}
destroyPools();
}
private void destroyPools() {
if (aprPool != 0) {
Pool.destroy(aprPool);
}
}
}

View File

@ -0,0 +1,122 @@
/*
* 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 org.apache.tomcat.jni.SSLContext;
/**
* Stats exposed by an OpenSSL session context.
*
* @see <a href="https://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html"><code>SSL_CTX_sess_number</code></a>
*/
public final class OpenSslSessionStats {
private final long context;
OpenSslSessionStats(long context) {
this.context = context;
}
/**
* Returns the current number of sessions in the internal session cache.
*/
public long number() {
return SSLContext.sessionNumber(context);
}
/**
* Returns the number of started SSL/TLS handshakes in client mode.
*/
public long connect() {
return SSLContext.sessionConnect(context);
}
/**
* Returns the number of successfully established SSL/TLS sessions in client mode.
*/
public long connectGood() {
return SSLContext.sessionConnectGood(context);
}
/**
* Returns the number of start renegotiations in client mode.
*/
public long connectRenegotiate() {
return SSLContext.sessionConnectRenegotiate(context);
}
/**
* Returns the number of started SSL/TLS handshakes in server mode.
*/
public long accept() {
return SSLContext.sessionAccept(context);
}
/**
* Returns the number of successfully established SSL/TLS sessions in server mode.
*/
public long acceptGood() {
return SSLContext.sessionAcceptGood(context);
}
/**
* Returns the number of start renegotiations in server mode.
*/
public long acceptRenegotiate() {
return SSLContext.sessionAcceptRenegotiate(context);
}
/**
* Returns the number of successfully reused sessions. In client mode, a session set with {@code SSL_set_session}
* successfully reused is counted as a hit. In server mode, a session successfully retrieved from internal or
* external cache is counted as a hit.
*/
public long hits() {
return SSLContext.sessionHits(context);
}
/**
* Returns the number of successfully retrieved sessions from the external session cache in server mode.
*/
public long cbHits() {
return SSLContext.sessionCbHits(context);
}
/**
* Returns the number of sessions proposed by clients that were not found in the internal session cache
* in server mode.
*/
public long misses() {
return SSLContext.sessionMisses(context);
}
/**
* Returns the number of sessions proposed by clients and either found in the internal or external session cache
* in server mode, but that were invalid due to timeout. These sessions are not included in the {@link #hits()}
* count.
*/
public long timeouts() {
return SSLContext.sessionTimeouts(context);
}
/**
* Returns the number of sessions that were removed because the maximum session cache size was exceeded.
*/
public long cacheFull() {
return SSLContext.sessionCacheFull(context);
}
}

View File

@ -0,0 +1,137 @@
/*
* 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.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Reads a PEM file and converts it into a list of DERs so that they are imported into a {@link KeyStore} easily.
*/
final class PemReader {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PemReader.class);
private static final Pattern CERT_PATTERN = Pattern.compile(
"-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header
"([a-z0-9+/=\\r\\n]+)" + // Base64 text
"-+END\\s+.*CERTIFICATE[^-]*-+", // Footer
Pattern.CASE_INSENSITIVE);
private static final Pattern KEY_PATTERN = Pattern.compile(
"-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
"([a-z0-9+/=\\r\\n]+)" + // Base64 text
"-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
Pattern.CASE_INSENSITIVE);
static ByteBuf[] readCertificates(File file) throws CertificateException {
String content;
try {
content = readContent(file);
} catch (IOException e) {
throw new CertificateException("failed to read a file: " + file, e);
}
List<ByteBuf> certs = new ArrayList<ByteBuf>();
Matcher m = CERT_PATTERN.matcher(content);
int start = 0;
for (;;) {
if (!m.find(start)) {
break;
}
certs.add(Base64.decode(Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII)));
start = m.end();
}
if (certs.isEmpty()) {
throw new CertificateException("found no certificates: " + file);
}
return certs.toArray(new ByteBuf[certs.size()]);
}
static ByteBuf readPrivateKey(File file) throws KeyException {
String content;
try {
content = readContent(file);
} catch (IOException e) {
throw new KeyException("failed to read a file: " + file, e);
}
Matcher m = KEY_PATTERN.matcher(content);
if (!m.find()) {
throw new KeyException("found no private key: " + file);
}
return Base64.decode(Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII));
}
private static String readContent(File file) throws IOException {
InputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
byte[] buf = new byte[8192];
for (;;) {
int ret = in.read(buf);
if (ret < 0) {
break;
}
out.write(buf, 0, ret);
}
return out.toString(CharsetUtil.US_ASCII.name());
} finally {
safeClose(in);
safeClose(out);
}
}
private static void safeClose(InputStream in) {
try {
in.close();
} catch (IOException e) {
logger.warn("Failed to close a stream.", e);
}
}
private static void safeClose(OutputStream out) {
try {
out.close();
} catch (IOException e) {
logger.warn("Failed to close a stream.", e);
}
}
private PemReader() { }
}

View File

@ -0,0 +1,463 @@
/*
* 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.buffer.ByteBufAllocator;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.util.List;
/**
* A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
* Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}.
*
* <h3>Making your server support SSL/TLS</h3>
* <pre>
* // In your {@link ChannelInitializer}:
* {@link ChannelPipeline} p = channel.pipeline();
* {@link SslContext} sslCtx = {@link #newServerContext(File, File) SslContext.newServerContext(...)};
* p.addLast("ssl", {@link #newEngine(ByteBufAllocator) sslCtx.newEngine(channel.alloc())});
* ...
* </pre>
*
* <h3>Making your client support SSL/TLS</h3>
* <pre>
* // In your {@link ChannelInitializer}:
* {@link ChannelPipeline} p = channel.pipeline();
* {@link SslContext} sslCtx = {@link #newClientContext(File) SslContext.newClientContext(...)};
* p.addLast("ssl", {@link #newEngine(ByteBufAllocator, String, int) sslCtx.newEngine(channel.alloc(), host, port)});
* ...
* </pre>
*/
public abstract class SslContext {
/**
* Returns the default server-side implementation provider currently in use.
*
* @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
*/
public static SslProvider defaultServerProvider() {
if (OpenSsl.isAvailable()) {
return SslProvider.OPENSSL;
} else {
return SslProvider.JDK;
}
}
/**
* Returns the default client-side implementation provider currently in use.
*
* @return {@link SslProvider#JDK}, because it is the only implementation at the moment
*/
public static SslProvider defaultClientProvider() {
return SslProvider.JDK;
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @return a new server-side {@link SslContext}
*/
public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
return newServerContext(null, certChainFile, keyFile, null, null, null, 0, 0);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @return a new server-side {@link SslContext}
*/
public static SslContext newServerContext(
File certChainFile, File keyFile, String keyPassword) throws SSLException {
return newServerContext(null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
* @return a new server-side {@link SslContext}
*/
public static SslContext newServerContext(
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(
null, certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @return a new server-side {@link SslContext}
*/
public static SslContext newServerContext(
SslProvider provider, File certChainFile, File keyFile) throws SSLException {
return newServerContext(provider, certChainFile, keyFile, null, null, null, 0, 0);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @return a new server-side {@link SslContext}
*/
public static SslContext newServerContext(
SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
return newServerContext(provider, certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
* @return a new server-side {@link SslContext}
*/
public static SslContext newServerContext(
SslProvider provider,
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
if (provider == null) {
provider = OpenSsl.isAvailable()? SslProvider.OPENSSL : SslProvider.JDK;
}
switch (provider) {
case JDK:
return new JdkSslServerContext(
certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
case OPENSSL:
return new OpenSslServerContext(
certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
default:
throw new Error(provider.toString());
}
}
/**
* Creates a new client-side {@link SslContext}.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext() throws SSLException {
return newClientContext(null, null, null, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(File certChainFile) throws SSLException {
return newClientContext(null, certChainFile, null, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(null, null, trustManagerFactory, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(null, certChainFile, trustManagerFactory, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocolSelector the {@link ApplicationProtocolSelector} that chooses one of the application layer
* protocols returned by a TLS server.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext(
null, certChainFile, trustManagerFactory,
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(SslProvider provider) throws SSLException {
return newClientContext(provider, null, null, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
return newClientContext(provider, certChainFile, null, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(
SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(provider, null, trustManagerFactory, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(provider, certChainFile, trustManagerFactory, null, null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocolSelector the {@link ApplicationProtocolSelector} that chooses one of the application layer
* protocols returned by a TLS server.
* {@code null} to disable TLS NPN/ALPN extension.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*
* @return a new client-side {@link SslContext}
*/
public static SslContext newClientContext(
SslProvider provider,
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector,
long sessionCacheSize, long sessionTimeout) throws SSLException {
if (provider != null && provider != SslProvider.JDK) {
throw new SSLException("client context unsupported for: " + provider);
}
return new JdkSslClientContext(
certChainFile, trustManagerFactory,
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout);
}
SslContext() { }
/**
* Returns {@code true} if and only if this context is for server-side.
*/
public final boolean isServer() {
return !isClient();
}
/**
* Returns the {@code true} if and only if this context is for client-side.
*/
public abstract boolean isClient();
/**
* Returns the list of enabled cipher suites, in the order of preference.
*/
public abstract List<String> cipherSuites();
/**
* Returns the size of the cache used for storing SSL session objects.
*/
public abstract long sessionCacheSize();
/**
* Returns the timeout for the cached SSL session objects, in seconds.
*/
public abstract long sessionTimeout();
/**
* Returns the client-side {@link ApplicationProtocolSelector} for the TLS NPN/ALPN extension.
*
* @return the client-side {@link ApplicationProtocolSelector}.
* {@code null} if NPN/ALPN extension has been disabled.
*/
public abstract ApplicationProtocolSelector nextProtocolSelector();
/**
* Returns the list of server-side application layer protocols for the TLS NPN/ALPN extension,
* in the order of preference.
*
* @return the list of server-side application layer protocols.
* {@code null} if NPN/ALPN extension has been disabled.
*/
public abstract List<String> nextProtocols();
/**
* Creates a new {@link SSLEngine}.
*
* @return a new {@link SSLEngine}
*/
public abstract SSLEngine newEngine(ByteBufAllocator alloc);
/**
* Creates a new {@link SSLEngine} using advisory peer information.
*
* @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port
*
* @return a new {@link SSLEngine}
*/
public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort);
/**
* Creates a new {@link SslHandler}.
*
* @return a new {@link SslHandler}
*/
public final SslHandler newHandler(ByteBufAllocator alloc) {
return newHandler(newEngine(alloc));
}
/**
* Creates a new {@link SslHandler} with advisory peer information.
*
* @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port
*
* @return a new {@link SslHandler}
*/
public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) {
return newHandler(newEngine(alloc, peerHost, peerPort));
}
private static SslHandler newHandler(SSLEngine engine) {
return new SslHandler(engine);
}
}

View File

@ -848,7 +848,7 @@ public class SslHandler extends ByteToMessageDecoder {
packet.limit(packet.position() + recordLengths[i]);
try {
unwrapSingle(ctx, packet, totalLength);
assert !packet.hasRemaining();
assert !packet.hasRemaining() || engine.isInboundDone();
} finally {
ByteBuf decodeOut = this.decodeOut;
if (decodeOut != null && decodeOut.isReadable()) {
@ -986,6 +986,9 @@ public class SslHandler extends ByteToMessageDecoder {
*/
private void setHandshakeSuccess() {
if (handshakePromise.trySuccess(ctx.channel())) {
if (logger.isDebugEnabled()) {
logger.debug(ctx.channel() + " HANDSHAKEN: " + engine.getSession().getCipherSuite());
}
ctx.fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS);
}
}
@ -1050,7 +1053,7 @@ public class SslHandler extends ByteToMessageDecoder {
public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx;
if (ctx.channel().isActive()) {
if (ctx.channel().isActive() && engine.getUseClientMode()) {
// channelActive() event has been fired already, which means this.channelActive() will
// not be invoked. We have to initialize here instead.
handshake();

View File

@ -0,0 +1,31 @@
/*
* 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;
/**
* An enumeration of SSL/TLS protocol providers.
*/
public enum SslProvider {
/**
* JDK's default implementation.
*/
JDK,
/**
* OpenSSL-based implementation.
*/
OPENSSL
}

View File

@ -0,0 +1,61 @@
/*
* 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.util;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import static io.netty.handler.ssl.util.SelfSignedCertificate.*;
/**
* Generates a self-signed certificate using <a href="http://www.bouncycastle.org/">Bouncy Castle</a>.
*/
final class BouncyCastleSelfSignedCertGenerator {
private static final Provider PROVIDER = new BouncyCastleProvider();
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random) throws Exception {
PrivateKey key = keypair.getPrivate();
// Prepare the information required for generating an X.509 certificate.
X500Name owner = new X500Name("CN=" + fqdn);
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
owner, new BigInteger(64, random), NOT_BEFORE, NOT_AFTER, owner, keypair.getPublic());
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(key);
X509CertificateHolder certHolder = builder.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder);
cert.verify(keypair.getPublic());
return newSelfSignedCertificate(fqdn, key, cert);
}
private BouncyCastleSelfSignedCertGenerator() { }
}

View File

@ -0,0 +1,207 @@
/*
* 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.util;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.EmptyArrays;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* An {@link TrustManagerFactory} that trusts an X.509 certificate whose SHA1 checksum matches.
* <p>
* <strong>NOTE:</strong>
* Never use this {@link TrustManagerFactory} in production unless you are not sure what you are exactly doing with it.
* </p><p>
* The SHA1 checksum of an X.509 certificate is calculated from its DER encoded format. You can get the fingerprint of
* an X.509 certificate using the {@code openssl} command. For example:
* <pre>
* $ openssl x509 -fingerprint -sha1 -in my_certificate.crt
* SHA1 Fingerprint=4E:85:10:55:BC:7B:12:08:D1:EA:0A:12:C9:72:EE:F3:AA:B2:C7:CB
* -----BEGIN CERTIFICATE-----
* MIIBqjCCAROgAwIBAgIJALiT3Nvp0kvmMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
* BAMTC2V4YW1wbGUuY29tMCAXDTcwMDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5
* WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
* gYkCgYEAnadvODG0QCiHhaFZlLHtr5gLIkDQS8ErZ//KfqeCHTC/KJsl3xYFk0zG
* aCv2FcmkOlokm77qV8qOW2DZdND7WuYzX6nLVuLb+GYxZ7b45iMAbAajvGh8jc9U
* o07fUIahGqTDAIAGCWsoLUOQ9nMzO/8GRHcXJAeQ2MGY2VpCcv0CAwEAATANBgkq
* hkiG9w0BAQUFAAOBgQBpRCnmjmNM0D7yrpkUJpBTNiqinhKLbeOvPWm+YmdInUUs
* LoMu0mZ1IANemLwqbwJJ76fknngeB+YuVAj46SurvVCV6ekwHcbgpW1u063IRwKk
* tQhOBO0HQxldUS4+4MYv/kuvnKkbjfgh5qfWw89Kx4kD+cycpP4yPtgDGk8ZMA==
* -----END CERTIFICATE-----
* </pre>
* </p>
*/
public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFactory {
private static final Pattern FINGERPRINT_PATTERN = Pattern.compile("^[0-9a-fA-F:]+$");
private static final Pattern FINGERPRINT_STRIP_PATTERN = Pattern.compile(":");
private static final int SHA1_BYTE_LEN = 20;
private static final int SHA1_HEX_LEN = SHA1_BYTE_LEN * 2;
private static final ThreadLocal<MessageDigest> tlmd = new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
try {
return MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException e) {
// All Java implementation must have SHA1 digest algorithm.
throw new Error(e);
}
}
};
private final TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException {
checkTrusted("client", chain);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException {
checkTrusted("server", chain);
}
private void checkTrusted(String type, X509Certificate[] chain) throws CertificateException {
X509Certificate cert = chain[0];
byte[] fingerprint = fingerprint(cert);
boolean found = false;
for (byte[] allowedFingerprint: fingerprints) {
if (Arrays.equals(fingerprint, allowedFingerprint)) {
found = true;
break;
}
}
if (!found) {
throw new CertificateException(
type + " certificate with unknown fingerprint: " + cert.getSubjectDN());
}
}
private byte[] fingerprint(X509Certificate cert) throws CertificateEncodingException {
MessageDigest md = tlmd.get();
md.reset();
return md.digest(cert.getEncoded());
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return EmptyArrays.EMPTY_X509_CERTIFICATES;
}
};
private final byte[][] fingerprints;
/**
* Creates a new instance.
*
* @param fingerprints a list of SHA1 fingerprints in heaxdecimal form
*/
public FingerprintTrustManagerFactory(Iterable<String> fingerprints) {
this(toFingerprintArray(fingerprints));
}
/**
* Creates a new instance.
*
* @param fingerprints a list of SHA1 fingerprints in heaxdecimal form
*/
public FingerprintTrustManagerFactory(String... fingerprints) {
this(toFingerprintArray(Arrays.asList(fingerprints)));
}
/**
* Creates a new instance.
*
* @param fingerprints a list of SHA1 fingerprints
*/
public FingerprintTrustManagerFactory(byte[]... fingerprints) {
if (fingerprints == null) {
throw new NullPointerException("fingerprints");
}
List<byte[]> list = new ArrayList<byte[]>();
for (byte[] f: fingerprints) {
if (f == null) {
break;
}
if (f.length != SHA1_BYTE_LEN) {
throw new IllegalArgumentException("malformed fingerprint: " +
ByteBufUtil.hexDump(Unpooled.wrappedBuffer(f)) + " (expected: SHA1)");
}
list.add(f.clone());
}
this.fingerprints = list.toArray(new byte[list.size()][]);
}
private static byte[][] toFingerprintArray(Iterable<String> fingerprints) {
if (fingerprints == null) {
throw new NullPointerException("fingerprints");
}
List<byte[]> list = new ArrayList<byte[]>();
for (String f: fingerprints) {
if (f == null) {
break;
}
if (!FINGERPRINT_PATTERN.matcher(f).matches()) {
throw new IllegalArgumentException("malformed fingerprint: " + f);
}
f = FINGERPRINT_STRIP_PATTERN.matcher(f).replaceAll("");
if (f.length() != SHA1_HEX_LEN) {
throw new IllegalArgumentException("malformed fingerprint: " + f + " (expected: SHA1)");
}
byte[] farr = new byte[SHA1_BYTE_LEN];
for (int i = 0; i < farr.length; i ++) {
int strIdx = i << 1;
farr[i] = (byte) Integer.parseInt(f.substring(strIdx, strIdx + 2), 16);
}
}
return list.toArray(new byte[list.size()][]);
}
@Override
protected void engineInit(KeyStore keyStore) throws Exception { }
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { }
@Override
protected TrustManager[] engineGetTrustManagers() {
return new TrustManager[] { tm };
}
}

View File

@ -0,0 +1,73 @@
/*
* 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.util;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
/**
* An insecure {@link javax.net.ssl.TrustManagerFactory} that trusts all X.509 certificates without any verification.
* <p>
* <strong>NOTE:</strong>
* Never use this {@link javax.net.ssl.TrustManagerFactory} in production.
* It is purely for testing purposes, and thus it is very insecure.
* </p>
*/
public final class InsecureTrustManagerFactory extends SimpleTrustManagerFactory {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(InsecureTrustManagerFactory.class);
public static final TrustManagerFactory INSTANCE = new InsecureTrustManagerFactory();
private static final TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String s) {
logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN());
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String s) {
logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN());
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return EmptyArrays.EMPTY_X509_CERTIFICATES;
}
};
private InsecureTrustManagerFactory() { }
@Override
protected void engineInit(KeyStore keyStore) throws Exception { }
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { }
@Override
protected TrustManager[] engineGetTrustManagers() {
return new TrustManager[] { tm };
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.util;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.SecureRandom;
import static io.netty.handler.ssl.util.SelfSignedCertificate.*;
/**
* Generates a self-signed certificate using {@code sun.security.x509} package provided by OpenJDK.
*/
final class OpenJdkSelfSignedCertGenerator {
static String[] generate(String fqdn, KeyPair keypair, SecureRandom random) throws Exception {
PrivateKey key = keypair.getPrivate();
// Prepare the information required for generating an X.509 certificate.
X509CertInfo info = new X509CertInfo();
X500Name owner = new X500Name("CN=" + fqdn);
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new BigInteger(64, random)));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.VALIDITY, new CertificateValidity(NOT_BEFORE, NOT_AFTER));
info.set(X509CertInfo.KEY, new CertificateX509Key(keypair.getPublic()));
info.set(X509CertInfo.ALGORITHM_ID,
new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid)));
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(key, "SHA1withRSA");
// Update the algorithm and sign again.
info.set(CertificateAlgorithmId.NAME + '.' + CertificateAlgorithmId.ALGORITHM, cert.get(X509CertImpl.SIG_ALG));
cert = new X509CertImpl(info);
cert.sign(key, "SHA1withRSA");
cert.verify(keypair.getPublic());
return newSelfSignedCertificate(fqdn, key, cert);
}
private OpenJdkSelfSignedCertGenerator() { }
}

View File

@ -0,0 +1,207 @@
/*
* 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.util;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
/**
* Generates a temporary self-signed certificate for testing purposes.
* <p>
* <strong>NOTE:</strong>
* Never use the certificate and private key generated by this class in production.
* It is purely for testing purposes, and thus it is very insecure.
* It even uses an insecure pseudo-random generator for faster generation internally.
* </p><p>
* A X.509 certificate file and a RSA private key file are generated in a system's temporary directory using
* {@link java.io.File#createTempFile(String, String)}, and they are deleted when the JVM exits using
* {@link java.io.File#deleteOnExit()}.
* </p><p>
* At first, this method tries to use OpenJDK's X.509 implementation (the {@code sun.security.x509} package).
* If it fails, it tries to use <a href="http://www.bouncycastle.org/">Bouncy Castle</a> as a fallback.
* </p>
*/
public final class SelfSignedCertificate {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SelfSignedCertificate.class);
/** Current time minus 1 year, just in case software clock goes back due to time synchronization */
static final Date NOT_BEFORE = new Date(System.currentTimeMillis() - 86400000L * 365);
/** The maximum possible value in X.509 specification: 9999-12-31 23:59:59 */
static final Date NOT_AFTER = new Date(253402300799000L);
private final File certificate;
private final File privateKey;
/**
* Creates a new instance.
*/
public SelfSignedCertificate() throws CertificateException {
this("example.com");
}
/**
* Creates a new instance.
*
* @param fqdn a fully qualified domain name
*/
public SelfSignedCertificate(String fqdn) throws CertificateException {
// Bypass entrophy collection by using insecure random generator.
// We just want to generate it without any delay because it's for testing purposes only.
this(fqdn, ThreadLocalInsecureRandom.current(), 1024);
}
/**
* Creates a new instance.
*
* @param fqdn a fully qualified domain name
* @param random the {@link java.security.SecureRandom} to use
* @param bits the number of bits of the generated private key
*/
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits) throws CertificateException {
// Generate an RSA key pair.
final KeyPair keypair;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(bits, random);
keypair = keyGen.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
// Should not reach here because every Java implementation must have RSA key pair generator.
throw new Error(e);
}
String[] paths;
try {
// Try the OpenJDK's proprietary implementation.
paths = OpenJdkSelfSignedCertGenerator.generate(fqdn, keypair, random);
} catch (Throwable t) {
logger.debug("Failed to generate a self-signed X.509 certificate using sun.security.x509:", t);
try {
// Try Bouncy Castle if the current JVM didn't have sun.security.x509.
paths = BouncyCastleSelfSignedCertGenerator.generate(fqdn, keypair, random);
} catch (Throwable t2) {
logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t2);
throw new CertificateException(
"No provider succeeded to generate a self-signed certificate. " +
"See debug log for the root cause.");
}
}
certificate = new File(paths[0]);
privateKey = new File(paths[1]);
}
/**
* Returns the generated X.509 certificate file in PEM format.
*/
public File certificate() {
return certificate;
}
/**
* Returns the generated RSA private key file in PEM format.
*/
public File privateKey() {
return privateKey;
}
/**
* Deletes the generated X.509 certificate file and RSA private key file.
*/
public void delete() {
safeDelete(certificate);
safeDelete(privateKey);
}
static String[] newSelfSignedCertificate(
String fqdn, PrivateKey key, X509Certificate cert) throws IOException, CertificateEncodingException {
// Encode the private key into a file.
String keyText = "-----BEGIN PRIVATE KEY-----\n" +
Base64.encode(Unpooled.wrappedBuffer(key.getEncoded()), true).toString(CharsetUtil.US_ASCII) +
"\n-----END PRIVATE KEY-----\n";
File keyFile = File.createTempFile("keyutil_" + fqdn + '_', ".key");
keyFile.deleteOnExit();
OutputStream keyOut = new FileOutputStream(keyFile);
try {
keyOut.write(keyText.getBytes(CharsetUtil.US_ASCII));
keyOut.close();
keyOut = null;
} finally {
if (keyOut != null) {
safeClose(keyFile, keyOut);
safeDelete(keyFile);
}
}
// Encode the certificate into a CRT file.
String certText = "-----BEGIN CERTIFICATE-----\n" +
Base64.encode(Unpooled.wrappedBuffer(cert.getEncoded()), true).toString(CharsetUtil.US_ASCII) +
"\n-----END CERTIFICATE-----\n";
File certFile = File.createTempFile("keyutil_" + fqdn + '_', ".crt");
certFile.deleteOnExit();
OutputStream certOut = new FileOutputStream(certFile);
try {
certOut.write(certText.getBytes(CharsetUtil.US_ASCII));
certOut.close();
certOut = null;
} finally {
if (certOut != null) {
safeClose(certFile, certOut);
safeDelete(certFile);
safeDelete(keyFile);
}
}
return new String[] { certFile.getPath(), keyFile.getPath() };
}
private static void safeDelete(File certFile) {
if (!certFile.delete()) {
logger.warn("Failed to delete a file: " + certFile);
}
}
private static void safeClose(File keyFile, OutputStream keyOut) {
try {
keyOut.close();
} catch (IOException e) {
logger.warn("Failed to close a file: " + keyFile, e);
}
}
}

View File

@ -0,0 +1,132 @@
/*
* 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.util;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManagerFactorySpi;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Provider;
/**
* Helps to implement a custom {@link TrustManagerFactory}.
*/
public abstract class SimpleTrustManagerFactory extends TrustManagerFactory {
private static final Provider PROVIDER = new Provider("", 0.0, "") {
private static final long serialVersionUID = -2680540247105807895L;
};
/**
* {@link SimpleTrustManagerFactorySpi} must have a reference to {@link SimpleTrustManagerFactory}
* to delegate its callbacks back to {@link SimpleTrustManagerFactory}. However, it is impossible to do so,
* because {@link TrustManagerFactory} requires {@link TrustManagerFactorySpi} at construction time and
* does not provide a way to access it later.
*
* To work around this issue, we use an ugly hack which uses a {@link ThreadLocal}.
*/
private static final ThreadLocal<SimpleTrustManagerFactorySpi> CURRENT_SPI =
new ThreadLocal<SimpleTrustManagerFactorySpi>() {
@Override
protected SimpleTrustManagerFactorySpi initialValue() {
return new SimpleTrustManagerFactorySpi();
}
};
/**
* Creates a new instance.
*/
protected SimpleTrustManagerFactory() {
this("");
}
/**
* Creates a new instance.
*
* @param name the name of this {@link TrustManagerFactory}
*/
protected SimpleTrustManagerFactory(String name) {
super(CURRENT_SPI.get(), PROVIDER, name);
CURRENT_SPI.get().init(this);
CURRENT_SPI.remove();
if (name == null) {
throw new NullPointerException("name");
}
}
/**
* Initializes this factory with a source of certificate authorities and related trust material.
*
* @see TrustManagerFactorySpi#engineInit(KeyStore)
*/
protected abstract void engineInit(KeyStore keyStore) throws Exception;
/**
* Initializes this factory with a source of provider-specific key material.
*
* @see TrustManagerFactorySpi#engineInit(ManagerFactoryParameters)
*/
protected abstract void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception;
/**
* Returns one trust manager for each type of trust material.
*
* @see TrustManagerFactorySpi#engineGetTrustManagers()
*/
protected abstract TrustManager[] engineGetTrustManagers();
static final class SimpleTrustManagerFactorySpi extends TrustManagerFactorySpi {
private SimpleTrustManagerFactory parent;
void init(SimpleTrustManagerFactory parent) {
this.parent = parent;
}
@Override
protected void engineInit(KeyStore keyStore) throws KeyStoreException {
try {
parent.engineInit(keyStore);
} catch (KeyStoreException e) {
throw e;
} catch (Exception e) {
throw new KeyStoreException(e);
}
}
@Override
protected void engineInit(
ManagerFactoryParameters managerFactoryParameters) throws InvalidAlgorithmParameterException {
try {
parent.engineInit(managerFactoryParameters);
} catch (InvalidAlgorithmParameterException e) {
throw e;
} catch (Exception e) {
throw new InvalidAlgorithmParameterException(e);
}
}
@Override
protected TrustManager[] engineGetTrustManagers() {
return parent.engineGetTrustManagers();
}
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.util;
import io.netty.util.internal.ThreadLocalRandom;
import java.security.SecureRandom;
import java.util.Random;
/**
* Insecure {@link java.security.SecureRandom} which relies on {@link ThreadLocalRandom} for random number generation.
*/
final class ThreadLocalInsecureRandom extends SecureRandom {
private static final long serialVersionUID = -8209473337192526191L;
private static final SecureRandom INSTANCE = new ThreadLocalInsecureRandom();
static SecureRandom current() {
return INSTANCE;
}
private ThreadLocalInsecureRandom() { }
@Override
public String getAlgorithm() {
return "insecure";
}
@Override
public void setSeed(byte[] seed) { }
@Override
public void setSeed(long seed) { }
@Override
public void nextBytes(byte[] bytes) {
random().nextBytes(bytes);
}
@Override
public byte[] generateSeed(int numBytes) {
byte[] seed = new byte[numBytes];
random().nextBytes(seed);
return seed;
}
@Override
public int nextInt() {
return random().nextInt();
}
@Override
public int nextInt(int n) {
return random().nextInt(n);
}
@Override
public boolean nextBoolean() {
return random().nextBoolean();
}
@Override
public long nextLong() {
return random().nextLong();
}
@Override
public float nextFloat() {
return random().nextFloat();
}
@Override
public double nextDouble() {
return random().nextDouble();
}
@Override
public double nextGaussian() {
return random().nextGaussian();
}
private static Random random() {
return ThreadLocalRandom.current();
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2012 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.
*/
/**
* Utility classes that helps easier development of TLS/SSL applications.
*/
package io.netty.handler.ssl.util;

10
pom.xml
View File

@ -253,6 +253,16 @@
<version>2.5.0</version>
</dependency>
<!-- Our own Tomcat Native fork - completely optional, used for acclerating SSL with OpenSSL. -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-tcnative</artifactId>
<version>1.1.30.Fork1</version>
<classifier>${os.detected.classifier}</classifier>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!--
Bouncy Castle - completely optional, only needed when:
- you generate a temporary self-signed certificate using SelfSignedCertificate, and

View File

@ -69,6 +69,12 @@
<artifactId>netty-transport-udt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-tcnative</artifactId>
<classifier>${os.detected.classifier}</classifier>
<optional>true</optional>
</dependency>
</dependencies>
<properties>

View File

@ -25,17 +25,25 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.JdkSslClientContext;
import io.netty.handler.ssl.JdkSslServerContext;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.testsuite.util.BogusSslContextFactory;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import javax.net.ssl.SSLEngine;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -48,29 +56,67 @@ import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class SocketSslEchoTest extends AbstractSocketTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocketSslEchoTest.class);
private static final int FIRST_MESSAGE_SIZE = 16384;
private static final Random random = new Random();
private static final File CERT_FILE;
private static final File KEY_FILE;
static final byte[] data = new byte[1048576];
static {
random.nextBytes(data);
SelfSignedCertificate ssc;
try {
ssc = new SelfSignedCertificate();
} catch (CertificateException e) {
throw new Error(e);
}
CERT_FILE = ssc.certificate();
KEY_FILE = ssc.privateKey();
}
@Parameters(name = "{index}: useChunkedWriteHandler = {0}, useCompositeByteBuf = {1}")
public static Collection<Object[]> data() {
List<Object[]> params = new ArrayList<Object[]>();
for (int i = 0; i < 4; i ++) {
params.add(new Object[] {
(i & 2) != 0, (i & 1) != 0
});
@Parameters(name =
"{index}: serverEngine = {0}, clientEngine = {1}, useChunkedWriteHandler = {2}, useCompositeByteBuf = {3}")
public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE));
List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(new JdkSslClientContext(CERT_FILE));
boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE));
// TODO: Client mode is not supported yet.
// clientContexts.add(new OpenSslContext(CERT_FILE));
} else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
}
List<Object[]> params = new ArrayList<Object[]>();
for (SslContext sc: serverContexts) {
for (SslContext cc: clientContexts) {
for (int i = 0; i < 4; i ++) {
params.add(new Object[] { sc, cc, (i & 2) != 0, (i & 1) != 0 });
}
}
}
return params;
}
private final SslContext serverCtx;
private final SslContext clientCtx;
private final boolean useChunkedWriteHandler;
private final boolean useCompositeByteBuf;
public SocketSslEchoTest(boolean useChunkedWriteHandler, boolean useCompositeByteBuf) {
public SocketSslEchoTest(
SslContext serverCtx, SslContext clientCtx, boolean useChunkedWriteHandler, boolean useCompositeByteBuf) {
this.serverCtx = serverCtx;
this.clientCtx = clientCtx;
this.useChunkedWriteHandler = useChunkedWriteHandler;
this.useCompositeByteBuf = useCompositeByteBuf;
}
@ -97,16 +143,10 @@ public class SocketSslEchoTest extends AbstractSocketTest {
final EchoHandler sh = new EchoHandler(true, useCompositeByteBuf, autoRead);
final EchoHandler ch = new EchoHandler(false, useCompositeByteBuf, autoRead);
final SSLEngine sse = BogusSslContextFactory.getServerContext().createSSLEngine();
final SSLEngine cse = BogusSslContextFactory.getClientContext().createSSLEngine();
sse.setUseClientMode(false);
cse.setUseClientMode(true);
sb.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
@SuppressWarnings("deprecation")
public void initChannel(SocketChannel sch) throws Exception {
sch.pipeline().addFirst("ssl", new SslHandler(sse));
sch.pipeline().addLast("ssl", serverCtx.newHandler(sch.alloc()));
if (useChunkedWriteHandler) {
sch.pipeline().addLast(new ChunkedWriteHandler());
}
@ -116,9 +156,8 @@ public class SocketSslEchoTest extends AbstractSocketTest {
cb.handler(new ChannelInitializer<SocketChannel>() {
@Override
@SuppressWarnings("deprecation")
public void initChannel(SocketChannel sch) throws Exception {
sch.pipeline().addFirst("ssl", new SslHandler(cse));
sch.pipeline().addLast("ssl", clientCtx.newHandler(sch.alloc()));
if (useChunkedWriteHandler) {
sch.pipeline().addLast(new ChunkedWriteHandler());
}
@ -195,7 +234,7 @@ public class SocketSslEchoTest extends AbstractSocketTest {
}
}
private class EchoHandler extends SimpleChannelInboundHandler<ByteBuf> {
private static class EchoHandler extends SimpleChannelInboundHandler<ByteBuf> {
volatile Channel channel;
final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
volatile int counter;

View File

@ -27,24 +27,87 @@ import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.testsuite.util.BogusSslContextFactory;
import io.netty.handler.ssl.JdkSslClientContext;
import io.netty.handler.ssl.JdkSslServerContext;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import javax.net.ssl.SSLEngine;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class SocketSslGreetingTest extends AbstractSocketTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocketSslGreetingTest.class);
private static final LogLevel LOG_LEVEL = LogLevel.TRACE;
private static final File CERT_FILE;
private static final File KEY_FILE;
private final ByteBuf greeting = ReferenceCountUtil.releaseLater(Unpooled.buffer().writeByte('a'));
static {
SelfSignedCertificate ssc;
try {
ssc = new SelfSignedCertificate();
} catch (CertificateException e) {
throw new Error(e);
}
CERT_FILE = ssc.certificate();
KEY_FILE = ssc.privateKey();
}
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}")
public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE));
List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(new JdkSslClientContext(CERT_FILE));
boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE));
// TODO: Client mode is not supported yet.
// clientContexts.add(new OpenSslContext(CERT_FILE));
} else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
}
List<Object[]> params = new ArrayList<Object[]>();
for (SslContext sc: serverContexts) {
for (SslContext cc: clientContexts) {
params.add(new Object[] { sc, cc });
}
}
return params;
}
private final SslContext serverCtx;
private final SslContext clientCtx;
public SocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) {
this.serverCtx = serverCtx;
this.clientCtx = clientCtx;
}
// Test for https://github.com/netty/netty/pull/2437
@Test(timeout = 30000)
public void testSslGreeting() throws Throwable {
@ -58,12 +121,9 @@ public class SocketSslGreetingTest extends AbstractSocketTest {
sb.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel sch) throws Exception {
final SSLEngine sse = BogusSslContextFactory.getServerContext().createSSLEngine();
sse.setUseClientMode(false);
ChannelPipeline p = sch.pipeline();
p.addLast(new SslHandler(sse));
p.addLast("logger", new LoggingHandler(LOG_LEVEL));
p.addLast(serverCtx.newHandler(sch.alloc()));
p.addLast(new LoggingHandler(LOG_LEVEL));
p.addLast(sh);
}
});
@ -71,12 +131,9 @@ public class SocketSslGreetingTest extends AbstractSocketTest {
cb.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel sch) throws Exception {
final SSLEngine cse = BogusSslContextFactory.getClientContext().createSSLEngine();
cse.setUseClientMode(true);
ChannelPipeline p = sch.pipeline();
p.addLast(new SslHandler(cse));
p.addLast("logger", new LoggingHandler(LOG_LEVEL));
p.addLast(clientCtx.newHandler(sch.alloc()));
p.addLast(new LoggingHandler(LOG_LEVEL));
p.addLast(ch);
}
});

View File

@ -17,6 +17,7 @@ package io.netty.testsuite.transport.socket;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
@ -28,26 +29,84 @@ import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.JdkSslClientContext;
import io.netty.handler.ssl.JdkSslServerContext;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.testsuite.util.BogusSslContextFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import javax.net.ssl.SSLEngine;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class SocketStartTlsTest extends AbstractSocketTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocketStartTlsTest.class);
private static final LogLevel LOG_LEVEL = LogLevel.TRACE;
private static final File CERT_FILE;
private static final File KEY_FILE;
private static EventExecutorGroup executor;
static {
SelfSignedCertificate ssc;
try {
ssc = new SelfSignedCertificate();
} catch (CertificateException e) {
throw new Error(e);
}
CERT_FILE = ssc.certificate();
KEY_FILE = ssc.privateKey();
}
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}")
public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE));
List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(new JdkSslClientContext(CERT_FILE));
boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE));
// TODO: Client mode is not supported yet.
// clientContexts.add(new OpenSslContext(CERT_FILE));
} else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
}
List<Object[]> params = new ArrayList<Object[]>();
for (SslContext sc: serverContexts) {
for (SslContext cc: clientContexts) {
params.add(new Object[] { sc, cc });
}
}
return params;
}
@BeforeClass
public static void createExecutor() {
executor = new DefaultEventExecutorGroup(2);
@ -58,6 +117,14 @@ public class SocketStartTlsTest extends AbstractSocketTest {
executor.shutdownGracefully().sync();
}
private final SslContext serverCtx;
private final SslContext clientCtx;
public SocketStartTlsTest(SslContext serverCtx, SslContext clientCtx) {
this.serverCtx = serverCtx;
this.clientCtx = clientCtx;
}
@Test(timeout = 30000)
public void testStartTls() throws Throwable {
run();
@ -78,8 +145,8 @@ public class SocketStartTlsTest extends AbstractSocketTest {
private void testStartTls(ServerBootstrap sb, Bootstrap cb, boolean autoRead) throws Throwable {
final EventExecutorGroup executor = SocketStartTlsTest.executor;
final SSLEngine sse = BogusSslContextFactory.getServerContext().createSSLEngine();
final SSLEngine cse = BogusSslContextFactory.getClientContext().createSSLEngine();
SSLEngine sse = serverCtx.newEngine(PooledByteBufAllocator.DEFAULT);
SSLEngine cse = clientCtx.newEngine(PooledByteBufAllocator.DEFAULT);
final StartTlsServerHandler sh = new StartTlsServerHandler(sse, autoRead);
final StartTlsClientHandler ch = new StartTlsClientHandler(cse, autoRead);
@ -155,7 +222,7 @@ public class SocketStartTlsTest extends AbstractSocketTest {
}
}
private class StartTlsClientHandler extends SimpleChannelInboundHandler<String> {
private static class StartTlsClientHandler extends SimpleChannelInboundHandler<String> {
private final SslHandler sslHandler;
private final boolean autoRead;
private Future<Channel> handshakeFuture;
@ -207,7 +274,7 @@ public class SocketStartTlsTest extends AbstractSocketTest {
}
}
private class StartTlsServerHandler extends SimpleChannelInboundHandler<String> {
private static class StartTlsServerHandler extends SimpleChannelInboundHandler<String> {
private final SslHandler sslHandler;
private final boolean autoRead;
volatile Channel channel;

View File

@ -1,312 +0,0 @@
/*
* Copyright 2013 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.testsuite.util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* A bogus key store which provides all the required information to
* create an example SSL connection.
*
* To generate a bogus key store:
* <pre>
* keytool -genkey -alias bogus -keysize 2048 -validity 36500
* -keyalg RSA -dname "CN=bogus"
* -keypass secret -storepass secret
* -keystore cert.jks
* </pre>
*/
final class BogusKeyStore {
private static final short[] DATA = {
0xfe, 0xed, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
0x00, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x00, 0x00, 0x01, 0x1a, 0x9f, 0x57, 0xa5,
0x27, 0x00, 0x00, 0x01, 0x9a, 0x30, 0x82, 0x01,
0x96, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01,
0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05,
0x00, 0x04, 0x82, 0x01, 0x82, 0x48, 0x6d, 0xcf,
0x16, 0xb5, 0x50, 0x95, 0x36, 0xbf, 0x47, 0x27,
0x50, 0x58, 0x0d, 0xa2, 0x52, 0x7e, 0x25, 0xab,
0x14, 0x1a, 0x26, 0x5e, 0x2d, 0x8a, 0x23, 0x90,
0x60, 0x7f, 0x12, 0x20, 0x56, 0xd1, 0x43, 0xa2,
0x6b, 0x47, 0x5d, 0xed, 0x9d, 0xd4, 0xe5, 0x83,
0x28, 0x89, 0xc2, 0x16, 0x4c, 0x76, 0x06, 0xad,
0x8e, 0x8c, 0x29, 0x1a, 0x9b, 0x0f, 0xdd, 0x60,
0x4b, 0xb4, 0x62, 0x82, 0x9e, 0x4a, 0x63, 0x83,
0x2e, 0xd2, 0x43, 0x78, 0xc2, 0x32, 0x1f, 0x60,
0xa9, 0x8a, 0x7f, 0x0f, 0x7c, 0xa6, 0x1d, 0xe6,
0x92, 0x9e, 0x52, 0xc7, 0x7d, 0xbb, 0x35, 0x3b,
0xaa, 0x89, 0x73, 0x4c, 0xfb, 0x99, 0x54, 0x97,
0x99, 0x28, 0x6e, 0x66, 0x5b, 0xf7, 0x9b, 0x7e,
0x6d, 0x8a, 0x2f, 0xfa, 0xc3, 0x1e, 0x71, 0xb9,
0xbd, 0x8f, 0xc5, 0x63, 0x25, 0x31, 0x20, 0x02,
0xff, 0x02, 0xf0, 0xc9, 0x2c, 0xdd, 0x3a, 0x10,
0x30, 0xab, 0xe5, 0xad, 0x3d, 0x1a, 0x82, 0x77,
0x46, 0xed, 0x03, 0x38, 0xa4, 0x73, 0x6d, 0x36,
0x36, 0x33, 0x70, 0xb2, 0x63, 0x20, 0xca, 0x03,
0xbf, 0x5a, 0xf4, 0x7c, 0x35, 0xf0, 0x63, 0x1a,
0x12, 0x33, 0x12, 0x58, 0xd9, 0xa2, 0x63, 0x6b,
0x63, 0x82, 0x41, 0x65, 0x70, 0x37, 0x4b, 0x99,
0x04, 0x9f, 0xdd, 0x5e, 0x07, 0x01, 0x95, 0x9f,
0x36, 0xe8, 0xc3, 0x66, 0x2a, 0x21, 0x69, 0x68,
0x40, 0xe6, 0xbc, 0xbb, 0x85, 0x81, 0x21, 0x13,
0xe6, 0xa4, 0xcf, 0xd3, 0x67, 0xe3, 0xfd, 0x75,
0xf0, 0xdf, 0x83, 0xe0, 0xc5, 0x36, 0x09, 0xac,
0x1b, 0xd4, 0xf7, 0x2a, 0x23, 0x57, 0x1c, 0x5c,
0x0f, 0xf4, 0xcf, 0xa2, 0xcf, 0xf5, 0xbd, 0x9c,
0x69, 0x98, 0x78, 0x3a, 0x25, 0xe4, 0xfd, 0x85,
0x11, 0xcc, 0x7d, 0xef, 0xeb, 0x74, 0x60, 0xb1,
0xb7, 0xfb, 0x1f, 0x0e, 0x62, 0xff, 0xfe, 0x09,
0x0a, 0xc3, 0x80, 0x2f, 0x10, 0x49, 0x89, 0x78,
0xd2, 0x08, 0xfa, 0x89, 0x22, 0x45, 0x91, 0x21,
0xbc, 0x90, 0x3e, 0xad, 0xb3, 0x0a, 0xb4, 0x0e,
0x1c, 0xa1, 0x93, 0x92, 0xd8, 0x72, 0x07, 0x54,
0x60, 0xe7, 0x91, 0xfc, 0xd9, 0x3c, 0xe1, 0x6f,
0x08, 0xe4, 0x56, 0xf6, 0x0b, 0xb0, 0x3c, 0x39,
0x8a, 0x2d, 0x48, 0x44, 0x28, 0x13, 0xca, 0xe9,
0xf7, 0xa3, 0xb6, 0x8a, 0x5f, 0x31, 0xa9, 0x72,
0xf2, 0xde, 0x96, 0xf2, 0xb1, 0x53, 0xb1, 0x3e,
0x24, 0x57, 0xfd, 0x18, 0x45, 0x1f, 0xc5, 0x33,
0x1b, 0xa4, 0xe8, 0x21, 0xfa, 0x0e, 0xb2, 0xb9,
0xcb, 0xc7, 0x07, 0x41, 0xdd, 0x2f, 0xb6, 0x6a,
0x23, 0x18, 0xed, 0xc1, 0xef, 0xe2, 0x4b, 0xec,
0xc9, 0xba, 0xfb, 0x46, 0x43, 0x90, 0xd7, 0xb5,
0x68, 0x28, 0x31, 0x2b, 0x8d, 0xa8, 0x51, 0x63,
0xf7, 0x53, 0x99, 0x19, 0x68, 0x85, 0x66, 0x00,
0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35,
0x30, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x30, 0x82,
0x02, 0x36, 0x30, 0x82, 0x01, 0xe0, 0xa0, 0x03,
0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 0xf1,
0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
0x30, 0x81, 0xa0, 0x31, 0x0b, 0x30, 0x09, 0x06,
0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52,
0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67,
0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 0x30,
0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b,
0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d,
0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 0x68,
0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20,
0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31,
0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b,
0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75,
0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e,
0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65,
0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30,
0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34,
0x31, 0x33, 0x38, 0x5a, 0x18, 0x0f, 0x32, 0x31,
0x38, 0x37, 0x31, 0x31, 0x32, 0x34, 0x30, 0x35,
0x34, 0x31, 0x33, 0x38, 0x5a, 0x30, 0x81, 0xa0,
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d,
0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
0x55, 0x04, 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f,
0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69,
0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04,
0x0a, 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e,
0x65, 0x74, 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f,
0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x41,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x31, 0x30,
0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63,
0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d,
0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74,
0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79,
0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74,
0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41,
0x00, 0xc3, 0xe3, 0x5e, 0x41, 0xa7, 0x87, 0x11,
0x00, 0x42, 0x2a, 0xb0, 0x4b, 0xed, 0xb2, 0xe0,
0x23, 0xdb, 0xb1, 0x3d, 0x58, 0x97, 0x35, 0x60,
0x0b, 0x82, 0x59, 0xd3, 0x00, 0xea, 0xd4, 0x61,
0xb8, 0x79, 0x3f, 0xb6, 0x3c, 0x12, 0x05, 0x93,
0x2e, 0x9a, 0x59, 0x68, 0x14, 0x77, 0x3a, 0xc8,
0x50, 0x25, 0x57, 0xa4, 0x49, 0x18, 0x63, 0x41,
0xf0, 0x2d, 0x28, 0xec, 0x06, 0xfb, 0xb4, 0x9f,
0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00,
0x65, 0x6c, 0x30, 0x01, 0xc2, 0x8e, 0x3e, 0xcb,
0xb3, 0x77, 0x48, 0xe9, 0x66, 0x61, 0x9a, 0x40,
0x86, 0xaf, 0xf6, 0x03, 0xeb, 0xba, 0x6a, 0xf2,
0xfd, 0xe2, 0xaf, 0x36, 0x5e, 0x7b, 0xaa, 0x22,
0x04, 0xdd, 0x2c, 0x20, 0xc4, 0xfc, 0xdd, 0xd0,
0x82, 0x20, 0x1c, 0x3d, 0xd7, 0x9e, 0x5e, 0x5c,
0x92, 0x5a, 0x76, 0x71, 0x28, 0xf5, 0x07, 0x7d,
0xa2, 0x81, 0xba, 0x77, 0x9f, 0x2a, 0xd9, 0x44,
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x6d, 0x79,
0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x1a, 0x9f,
0x5b, 0x56, 0xa0, 0x00, 0x00, 0x01, 0x99, 0x30,
0x82, 0x01, 0x95, 0x30, 0x0e, 0x06, 0x0a, 0x2b,
0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01,
0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x81, 0x29,
0xa8, 0xb6, 0x08, 0x0c, 0x85, 0x75, 0x3e, 0xdd,
0xb5, 0xe5, 0x1a, 0x87, 0x68, 0xd1, 0x90, 0x4b,
0x29, 0x31, 0xee, 0x90, 0xbc, 0x9d, 0x73, 0xa0,
0x3f, 0xe9, 0x0b, 0xa4, 0xef, 0x30, 0x9b, 0x36,
0x9a, 0xb2, 0x54, 0x77, 0x81, 0x07, 0x4b, 0xaa,
0xa5, 0x77, 0x98, 0xe1, 0xeb, 0xb5, 0x7c, 0x4e,
0x48, 0xd5, 0x08, 0xfc, 0x2c, 0x36, 0xe2, 0x65,
0x03, 0xac, 0xe5, 0xf3, 0x96, 0xb7, 0xd0, 0xb5,
0x3b, 0x92, 0xe4, 0x14, 0x05, 0x7a, 0x6a, 0x92,
0x56, 0xfe, 0x4e, 0xab, 0xd3, 0x0e, 0x32, 0x04,
0x22, 0x22, 0x74, 0x47, 0x7d, 0xec, 0x21, 0x99,
0x30, 0x31, 0x64, 0x46, 0x64, 0x9b, 0xc7, 0x13,
0xbf, 0xbe, 0xd0, 0x31, 0x49, 0xe7, 0x3c, 0xbf,
0xba, 0xb1, 0x20, 0xf9, 0x42, 0xf4, 0xa9, 0xa9,
0xe5, 0x13, 0x65, 0x32, 0xbf, 0x7c, 0xcc, 0x91,
0xd3, 0xfd, 0x24, 0x47, 0x0b, 0xe5, 0x53, 0xad,
0x50, 0x30, 0x56, 0xd1, 0xfa, 0x9c, 0x37, 0xa8,
0xc1, 0xce, 0xf6, 0x0b, 0x18, 0xaa, 0x7c, 0xab,
0xbd, 0x1f, 0xdf, 0xe4, 0x80, 0xb8, 0xa7, 0xe0,
0xad, 0x7d, 0x50, 0x74, 0xf1, 0x98, 0x78, 0xbc,
0x58, 0xb9, 0xc2, 0x52, 0xbe, 0xd2, 0x5b, 0x81,
0x94, 0x83, 0x8f, 0xb9, 0x4c, 0xee, 0x01, 0x2b,
0x5e, 0xc9, 0x6e, 0x9b, 0xf5, 0x63, 0x69, 0xe4,
0xd8, 0x0b, 0x47, 0xd8, 0xfd, 0xd8, 0xe0, 0xed,
0xa8, 0x27, 0x03, 0x74, 0x1e, 0x5d, 0x32, 0xe6,
0x5c, 0x63, 0xc2, 0xfb, 0x3f, 0xee, 0xb4, 0x13,
0xc6, 0x0e, 0x6e, 0x74, 0xe0, 0x22, 0xac, 0xce,
0x79, 0xf9, 0x43, 0x68, 0xc1, 0x03, 0x74, 0x2b,
0xe1, 0x18, 0xf8, 0x7f, 0x76, 0x9a, 0xea, 0x82,
0x3f, 0xc2, 0xa6, 0xa7, 0x4c, 0xfe, 0xae, 0x29,
0x3b, 0xc1, 0x10, 0x7c, 0xd5, 0x77, 0x17, 0x79,
0x5f, 0xcb, 0xad, 0x1f, 0xd8, 0xa1, 0xfd, 0x90,
0xe1, 0x6b, 0xb2, 0xef, 0xb9, 0x41, 0x26, 0xa4,
0x0b, 0x4f, 0xc6, 0x83, 0x05, 0x6f, 0xf0, 0x64,
0x40, 0xe1, 0x44, 0xc4, 0xf9, 0x40, 0x2b, 0x3b,
0x40, 0xdb, 0xaf, 0x35, 0xa4, 0x9b, 0x9f, 0xc4,
0x74, 0x07, 0xe5, 0x18, 0x60, 0xc5, 0xfe, 0x15,
0x0e, 0x3a, 0x25, 0x2a, 0x11, 0xee, 0x78, 0x2f,
0xb8, 0xd1, 0x6e, 0x4e, 0x3c, 0x0a, 0xb5, 0xb9,
0x40, 0x86, 0x27, 0x6d, 0x8f, 0x53, 0xb7, 0x77,
0x36, 0xec, 0x5d, 0xed, 0x32, 0x40, 0x43, 0x82,
0xc3, 0x52, 0x58, 0xc4, 0x26, 0x39, 0xf3, 0xb3,
0xad, 0x58, 0xab, 0xb7, 0xf7, 0x8e, 0x0e, 0xba,
0x8e, 0x78, 0x9d, 0xbf, 0x58, 0x34, 0xbd, 0x77,
0x73, 0xa6, 0x50, 0x55, 0x00, 0x60, 0x26, 0xbf,
0x6d, 0xb4, 0x98, 0x8a, 0x18, 0x83, 0x89, 0xf8,
0xcd, 0x0d, 0x49, 0x06, 0xae, 0x51, 0x6e, 0xaf,
0xbd, 0xe2, 0x07, 0x13, 0xd8, 0x64, 0xcc, 0xbf,
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e,
0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x34, 0x30,
0x82, 0x02, 0x30, 0x30, 0x82, 0x01, 0xda, 0xa0,
0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59,
0xf2, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
0x00, 0x30, 0x81, 0x9d, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b,
0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e,
0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14,
0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61,
0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18,
0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54,
0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79,
0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04,
0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x31,
0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65,
0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74,
0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d,
0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65,
0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, 0x38, 0x30,
0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x35, 0x34,
0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37,
0x31, 0x31, 0x32, 0x33, 0x30, 0x35, 0x34, 0x35,
0x34, 0x30, 0x5a, 0x30, 0x81, 0x9d, 0x31, 0x0b,
0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79,
0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f,
0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04,
0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67,
0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a,
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74,
0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65,
0x63, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03,
0x55, 0x04, 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72,
0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75,
0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e,
0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65,
0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30,
0x48, 0x02, 0x41, 0x00, 0x95, 0xb3, 0x47, 0x17,
0x95, 0x0f, 0x57, 0xcf, 0x66, 0x72, 0x0a, 0x7e,
0x5b, 0x54, 0xea, 0x8c, 0x6f, 0x79, 0xde, 0x94,
0xac, 0x0b, 0x5a, 0xd4, 0xd6, 0x1b, 0x58, 0x12,
0x1a, 0x16, 0x3d, 0xfe, 0xdf, 0xa5, 0x2b, 0x86,
0xbc, 0x64, 0xd4, 0x80, 0x1e, 0x3f, 0xf9, 0xe2,
0x04, 0x03, 0x79, 0x9b, 0xc1, 0x5c, 0xf0, 0xf1,
0xf3, 0xf1, 0xe3, 0xbf, 0x3f, 0xc0, 0x1f, 0xdd,
0xdb, 0xc0, 0x5b, 0x21, 0x02, 0x03, 0x01, 0x00,
0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
0x03, 0x41, 0x00, 0x02, 0xd7, 0xdd, 0xbd, 0x0c,
0x8e, 0x21, 0x20, 0xef, 0x9e, 0x4f, 0x1f, 0xf5,
0x49, 0xf1, 0xae, 0x58, 0x9b, 0x94, 0x3a, 0x1f,
0x70, 0x33, 0xf0, 0x9b, 0xbb, 0xe9, 0xc0, 0xf3,
0x72, 0xcb, 0xde, 0xb6, 0x56, 0x72, 0xcc, 0x1c,
0xf0, 0xd6, 0x5a, 0x2a, 0xbc, 0xa1, 0x7e, 0x23,
0x83, 0xe9, 0xe7, 0xcf, 0x9e, 0xa5, 0xf9, 0xcc,
0xc2, 0x61, 0xf4, 0xdb, 0x40, 0x93, 0x1d, 0x63,
0x8a, 0x50, 0x4c, 0x11, 0x39, 0xb1, 0x91, 0xc1,
0xe6, 0x9d, 0xd9, 0x1a, 0x62, 0x1b, 0xb8, 0xd3,
0xd6, 0x9a, 0x6d, 0xb9, 0x8e, 0x15, 0x51 };
public static InputStream asInputStream() {
byte[] data = new byte[DATA.length];
for (int i = 0; i < data.length; i ++) {
data[i] = (byte) DATA[i];
}
return new ByteArrayInputStream(data);
}
public static char[] getCertificatePassword() {
return "secret".toCharArray();
}
public static char[] getKeyStorePassword() {
return "secret".toCharArray();
}
private BogusKeyStore() { }
}

View File

@ -1,76 +0,0 @@
/*
* Copyright 2013 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.testsuite.util;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.security.KeyStore;
import java.security.Security;
public final class BogusSslContextFactory {
private static final String PROTOCOL = "TLS";
private static final SSLContext SERVER_CONTEXT;
private static final SSLContext CLIENT_CONTEXT;
static {
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
SSLContext serverContext;
SSLContext clientContext;
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(BogusKeyStore.asInputStream(),
BogusKeyStore.getKeyStorePassword());
// Set up key manager factory to use our key store
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, BogusKeyStore.getCertificatePassword());
// Initialize the SSLContext to work with our key managers.
serverContext = SSLContext.getInstance(PROTOCOL);
serverContext.init(kmf.getKeyManagers(), null, null);
} catch (Exception e) {
throw new Error(
"Failed to initialize the server-side SSLContext", e);
}
try {
clientContext = SSLContext.getInstance(PROTOCOL);
clientContext.init(null, BogusTrustManagerFactory.getTrustManagers(), null);
} catch (Exception e) {
throw new Error(
"Failed to initialize the client-side SSLContext", e);
}
SERVER_CONTEXT = serverContext;
CLIENT_CONTEXT = clientContext;
}
public static SSLContext getServerContext() {
return SERVER_CONTEXT;
}
public static SSLContext getClientContext() {
return CLIENT_CONTEXT;
}
private BogusSslContextFactory() { }
}

View File

@ -1,69 +0,0 @@
/*
* Copyright 2013 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.testsuite.util;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
/**
* Bogus {@link TrustManagerFactorySpi} which accepts any certificate
* even if it is invalid.
*/
final class BogusTrustManagerFactory extends TrustManagerFactorySpi {
private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// NOOP
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// NOOP
}
};
public static TrustManager[] getTrustManagers() {
return new TrustManager[] { DUMMY_TRUST_MANAGER };
}
@Override
protected TrustManager[] engineGetTrustManagers() {
return getTrustManagers();
}
@Override
protected void engineInit(KeyStore keystore) {
// Unused
}
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters) {
// Unused
}
private BogusTrustManagerFactory() { }
}

View File

@ -17,6 +17,7 @@ package io.netty.channel.epoll;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.handler.ssl.SslContext;
import io.netty.testsuite.transport.TestsuitePermutation;
import io.netty.testsuite.transport.socket.SocketSslEchoTest;
@ -24,9 +25,9 @@ import java.util.List;
public class EpollSocketSslEchoTest extends SocketSslEchoTest {
public EpollSocketSslEchoTest(boolean useChunkedWriteHandler,
boolean useCompositeByteBuf) {
super(useChunkedWriteHandler, useCompositeByteBuf);
public EpollSocketSslEchoTest(
SslContext serverCtx, SslContext clientCtx, boolean useChunkedWriteHandler, boolean useCompositeByteBuf) {
super(serverCtx, clientCtx, useChunkedWriteHandler, useCompositeByteBuf);
}
@Override

View File

@ -0,0 +1,36 @@
/*
* 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.channel.epoll;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.handler.ssl.SslContext;
import io.netty.testsuite.transport.TestsuitePermutation;
import io.netty.testsuite.transport.socket.SocketSslGreetingTest;
import java.util.List;
public class EpollSocketSslGreetingTest extends SocketSslGreetingTest {
public EpollSocketSslGreetingTest(SslContext serverCtx, SslContext clientCtx) {
super(serverCtx, clientCtx);
}
@Override
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
return EpollSocketTestPermutation.INSTANCE.socket();
}
}

View File

@ -17,6 +17,7 @@ package io.netty.channel.epoll;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.handler.ssl.SslContext;
import io.netty.testsuite.transport.TestsuitePermutation;
import io.netty.testsuite.transport.socket.SocketStartTlsTest;
@ -24,6 +25,10 @@ import java.util.List;
public class EpollSocketStartTlsTest extends SocketStartTlsTest {
public EpollSocketStartTlsTest(SslContext serverCtx, SslContext clientCtx) {
super(serverCtx, clientCtx);
}
@Override
protected List<TestsuitePermutation.BootstrapComboFactory<ServerBootstrap, Bootstrap>> newFactories() {
return EpollSocketTestPermutation.INSTANCE.socket();