Provide convenient universal API to enable SSL/TLS

Motivation:

Although 4cff4b99fd introduced
OpenSslEngine and its helper classes, a user has to write two different
copies of SSL initialization code that does pretty much same job,
because the initialization procedure between JDK SSLEngine and
OpenSslEngine are different.

Modifications:

- Replace OpenSslContextBuilder with SslContext which provides the
  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.
- Merge OpenSslBufferPool into SslBufferPool
  - Add an option to preallocate the pool
  - Add an option to allocate direct buffers
  - When OpenSSL is in use, preallocate direct buffers, which is close
    to what OpenSslBufferPool does.
- Add JdkSslContext which is a simple wrapper of JDK's SSLContext
  - The specified PKCS#8 key and X.509 certificate chain are converted
    to JDK KeyStore in instantiation time.
  - Like OpenSslServerContext, it uses sensible default cipher suites now.
- A user does not specify certPath and caPath separately anymore. He or
  she has to merge them into a single file.  I find this more logical
  because previously ca file's first entry and cert file were always same.
- Clean up SSL tests to demonstrate the advantage of this change
  - AbstractSocketSsl*Test now uses SslContext.new*Context() to
    configure both the client and the server side.  We did this only for
    the server side previously and had to use different certificates for
    JDK SSLEngine and OpenSslEngine, but not anymore.
- 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()
- Found that OpenSslEngine performs unnecessary memory copy - optimized
  it.
- 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-14 21:01:36 +09:00
parent 4cff4b99fd
commit cb4020d4be
59 changed files with 2408 additions and 1791 deletions

View File

@ -50,6 +50,14 @@ zlib in pure Java, which can be obtained at:
* HOMEPAGE:
* http://www.jcraft.com/jzlib/
This product contains a modified version of 'Webbit', a Java event based
WebSocket and HTTP server:
* LICENSE:
* license/LICENSE.webbit.txt (BSD License)
* HOMEPAGE:
* https://github.com/joewalnes/webbit
This product optionally depends on 'Protocol Buffers', Google's data
interchange format, which can be obtained at:
@ -58,6 +66,15 @@ interchange format, which can be obtained at:
* HOMEPAGE:
* http://code.google.com/p/protobuf/
This product optionally depends on 'Bouncy Castle Crypto APIs' to generate
a temporary self-signed X.509 certificate when the JVM does not provide the
equivalent functionality. It can be obtained at:
* LICENSE:
* license/LICENSE.bouncycastle.txt (MIT License)
* HOMEPAGE:
* http://www.bouncycastle.org/
This product optionally depends on 'SLF4J', a simple logging facade for Java,
which can be obtained at:
@ -98,11 +115,3 @@ framework implementation, which can be obtained at:
* HOMEPAGE:
* http://felix.apache.org/
This product optionally depends on 'Webbit', a Java event based
WebSocket and HTTP server:
* LICENSE:
* license/LICENSE.webbit.txt (BSD License)
* HOMEPAGE:
* https://github.com/joewalnes/webbit

View File

@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc.
(http://www.bouncycastle.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

25
pom.xml
View File

@ -98,6 +98,19 @@
<optional>true</optional>
</dependency>
<!--
Bouncy Castle - completely optional, only needed when:
- you generate a temporary self-signed certificate using KeyUtil, and
- you don't use the JDK which doesn't provide sun.security.x509 package.
-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.50</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- Servlet API - completely optional -->
<!-- Used for HTTP tunneling transport -->
<dependency>
@ -362,6 +375,18 @@
<ignore>java.io.ObjectStreamClass</ignore>
<!-- Socks Codec-->
<ignore>java.net.IDN</ignore>
<!-- Self-signed certificate generation -->
<ignore>sun.security.x509.AlgorithmId</ignore>
<ignore>sun.security.x509.CertificateAlgorithmId</ignore>
<ignore>sun.security.x509.CertificateIssuerName</ignore>
<ignore>sun.security.x509.CertificateSerialNumber</ignore>
<ignore>sun.security.x509.CertificateSubjectName</ignore>
<ignore>sun.security.x509.CertificateValidity</ignore>
<ignore>sun.security.x509.CertificateVersion</ignore>
<ignore>sun.security.x509.CertificateX509Key</ignore>
<ignore>sun.security.x509.X500Name</ignore>
<ignore>sun.security.x509.X509CertInfo</ignore>
<ignore>sun.security.x509.X509CertImpl</ignore>
</ignores>
</configuration>
<executions>

View File

@ -15,17 +15,23 @@
*/
package org.jboss.netty.example.http.file;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.SelfSignedCertificate;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
public class HttpStaticFileServer {
private static final boolean useSsl = false; // Set to true to enable SSL.
private final SslContext sslCtx;
private final int port;
public HttpStaticFileServer(int port) {
public HttpStaticFileServer(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -37,19 +43,28 @@ public class HttpStaticFileServer {
Executors.newCachedThreadPool()));
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new HttpStaticFileServerPipelineFactory());
bootstrap.setPipelineFactory(new HttpStaticFileServerPipelineFactory(sslCtx));
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(port));
}
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new HttpStaticFileServer(port).run();
final SslContext sslCtx;
if (useSsl) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
new HttpStaticFileServer(sslCtx, port).run();
}
}

View File

@ -15,30 +15,31 @@
*/
package org.jboss.netty.example.http.file;
import static org.jboss.netty.channel.Channels.*;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;
// Uncomment the following lines if you want HTTPS
//import javax.net.ssl.SSLEngine;
//import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
//import org.jboss.netty.handler.ssl.SslHandler;
import static org.jboss.netty.channel.Channels.*;
public class HttpStaticFileServerPipelineFactory implements ChannelPipelineFactory {
private final SslContext sslCtx;
public HttpStaticFileServerPipelineFactory(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = pipeline();
// Uncomment the following lines if you want HTTPS
//SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
//engine.setUseClientMode(false);
//pipeline.addLast("ssl", new SslHandler(engine));
if (sslCtx != null) {
pipeline.addLast("ssl", sslCtx.newHandler());
}
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpChunkAggregator(65536));
pipeline.addLast("encoder", new HttpResponseEncoder());

View File

@ -25,6 +25,8 @@ import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.net.InetSocketAddress;
import java.net.URI;
@ -42,7 +44,7 @@ public class HttpSnoopClient {
this.uri = uri;
}
public void run() {
public void run() throws Exception {
String scheme = uri.getScheme() == null? "http" : uri.getScheme();
String host = uri.getHost() == null? "localhost" : uri.getHost();
int port = uri.getPort();
@ -59,7 +61,14 @@ public class HttpSnoopClient {
return;
}
boolean ssl = "https".equalsIgnoreCase(scheme);
// Configure SSL context if necessary.
final boolean ssl = "https".equalsIgnoreCase(scheme);
final SslContext sslCtx;
if (ssl) {
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
} else {
sslCtx = null;
}
// Configure the client.
ClientBootstrap bootstrap = new ClientBootstrap(
@ -68,7 +77,7 @@ public class HttpSnoopClient {
Executors.newCachedThreadPool()));
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new HttpSnoopClientPipelineFactory(ssl));
bootstrap.setPipelineFactory(new HttpSnoopClientPipelineFactory(sslCtx));
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));

View File

@ -15,23 +15,20 @@
*/
package org.jboss.netty.example.http.snoop;
import static org.jboss.netty.channel.Channels.*;
import javax.net.ssl.SSLEngine;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpContentDecompressor;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.ssl.SslContext;
import static org.jboss.netty.channel.Channels.*;
public class HttpSnoopClientPipelineFactory implements ChannelPipelineFactory {
private final boolean ssl;
private final SslContext sslCtx;
public HttpSnoopClientPipelineFactory(boolean ssl) {
this.ssl = ssl;
public HttpSnoopClientPipelineFactory(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
public ChannelPipeline getPipeline() throws Exception {
@ -39,12 +36,8 @@ public class HttpSnoopClientPipelineFactory implements ChannelPipelineFactory {
ChannelPipeline pipeline = pipeline();
// Enable HTTPS if necessary.
if (ssl) {
SSLEngine engine =
SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
pipeline.addLast("ssl", new SslHandler(engine));
if (sslCtx != null) {
pipeline.addLast("ssl", sslCtx.newHandler());
}
pipeline.addLast("codec", new HttpClientCodec());

View File

@ -22,14 +22,14 @@ import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.http.HttpTunnelingClientSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import org.jboss.netty.handler.logging.LoggingHandler;
import org.jboss.netty.handler.ssl.JdkSslClientContext;
import org.jboss.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.jboss.netty.logging.InternalLogLevel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.URI;
@ -49,7 +49,7 @@ public class HttpTunnelingClientExample {
this.uri = uri;
}
public void run() throws IOException {
public void run() throws Exception {
String scheme = uri.getScheme() == null? "http" : uri.getScheme();
// Configure the client.
@ -72,7 +72,7 @@ public class HttpTunnelingClientExample {
// Configure SSL if necessary
if ("https".equals(scheme)) {
b.setOption("sslContext", SecureChatSslContextFactory.getClientContext());
b.setOption("sslContext", new JdkSslClientContext(InsecureTrustManagerFactory.INSTANCE).context());
} else if (!"http".equals(scheme)) {
// Only HTTP and HTTPS are supported.
System.err.println("Only HTTP(S) is supported.");

View File

@ -33,6 +33,8 @@ import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestEncoder.ErrorDataEncoderException;
import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.SelfSignedCertificate;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
@ -57,7 +59,7 @@ public class HttpUploadClient {
this.filePath = filePath;
}
public void run() {
public void run() throws Exception {
String postSimple, postFile, get;
if (baseUri.endsWith("/")) {
postSimple = baseUri + "formpost";
@ -91,7 +93,14 @@ public class HttpUploadClient {
return;
}
boolean ssl = "https".equalsIgnoreCase(scheme);
final boolean ssl = "https".equalsIgnoreCase(scheme);
final SslContext sslCtx;
if (ssl) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
URI uriFile;
try {
@ -113,7 +122,7 @@ public class HttpUploadClient {
Executors.newCachedThreadPool()));
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new HttpUploadClientPipelineFactory(ssl));
bootstrap.setPipelineFactory(new HttpUploadClientPipelineFactory(sslCtx));
// setup the factory: here using a mixed memory/disk based on size threshold
HttpDataFactory factory = new DefaultHttpDataFactory(
@ -225,7 +234,7 @@ public class HttpUploadClient {
private static List<InterfaceHttpData> formpost(ClientBootstrap bootstrap,
String host, int port,
URI uriSimple, File file, HttpDataFactory factory,
List<Entry<String, String>> headers) {
List<Entry<String, String>> headers) throws ErrorDataEncoderException {
// XXX /formpost
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
@ -242,17 +251,8 @@ public class HttpUploadClient {
HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
// Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null;
try {
bodyRequestEncoder = new HttpPostRequestEncoder(factory,
request, false); // false => not multipart
} catch (NullPointerException e) {
// should not be since args are not null
e.printStackTrace();
} catch (ErrorDataEncoderException e) {
// test if method is a POST method
e.printStackTrace();
}
HttpPostRequestEncoder bodyRequestEncoder =
new HttpPostRequestEncoder(factory, request, false); // false => not multipart
// it is legal to add directly header or cookie into the request until finalize
for (Entry<String, String> entry : headers) {
@ -260,28 +260,15 @@ public class HttpUploadClient {
}
// add Form attribute
try {
bodyRequestEncoder.addBodyAttribute("getform", "POST");
bodyRequestEncoder.addBodyAttribute("info", "first value");
bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue <20><><EFBFBD>&");
bodyRequestEncoder.addBodyAttribute("thirdinfo", textArea);
bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);
bodyRequestEncoder.addBodyAttribute("Send", "Send");
} catch (NullPointerException e) {
// should not be since not null args
e.printStackTrace();
} catch (ErrorDataEncoderException e) {
// if an encoding error occurs
e.printStackTrace();
}
bodyRequestEncoder.addBodyAttribute("getform", "POST");
bodyRequestEncoder.addBodyAttribute("info", "first value");
bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue <20><><EFBFBD>&");
bodyRequestEncoder.addBodyAttribute("thirdinfo", textArea);
bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);
// finalize request
try {
request = bodyRequestEncoder.finalizeRequest();
} catch (ErrorDataEncoderException e) {
// if an encoding error occurs
e.printStackTrace();
}
request = bodyRequestEncoder.finalizeRequest();
// Create the bodylist to be reused on the last version with Multipart support
List<InterfaceHttpData> bodylist = bodyRequestEncoder.getBodyListAttributes();
@ -311,7 +298,7 @@ public class HttpUploadClient {
*/
private static void formpostmultipart(ClientBootstrap bootstrap, String host, int port,
URI uriFile, HttpDataFactory factory,
List<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) {
List<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) throws ErrorDataEncoderException {
// XXX /formpostmultipart
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
@ -328,17 +315,8 @@ public class HttpUploadClient {
HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
// Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null;
try {
bodyRequestEncoder = new HttpPostRequestEncoder(factory,
request, true); // true => multipart
} catch (NullPointerException e) {
// should not be since no null args
e.printStackTrace();
} catch (ErrorDataEncoderException e) {
// test if method is a POST method
e.printStackTrace();
}
HttpPostRequestEncoder bodyRequestEncoder =
new HttpPostRequestEncoder(factory, request, true); // true => multipart
// it is legal to add directly header or cookie into the request until finalize
for (Entry<String, String> entry : headers) {
@ -346,23 +324,10 @@ public class HttpUploadClient {
}
// add Form attribute from previous request in formpost()
try {
bodyRequestEncoder.setBodyHttpDatas(bodylist);
} catch (NullPointerException e1) {
// should not be since previously created
e1.printStackTrace();
} catch (ErrorDataEncoderException e1) {
// again should not be since previously encoded (except if an error occurs previously)
e1.printStackTrace();
}
bodyRequestEncoder.setBodyHttpDatas(bodylist);
// finalize request
try {
bodyRequestEncoder.finalizeRequest();
} catch (ErrorDataEncoderException e) {
// if an encoding error occurs
e.printStackTrace();
}
bodyRequestEncoder.finalizeRequest();
// send request
channel.write(request);
@ -379,7 +344,7 @@ public class HttpUploadClient {
channel.getCloseFuture().awaitUninterruptibly();
}
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
logger.error(
"Usage: " + HttpUploadClient.class.getSimpleName() +
@ -983,5 +948,4 @@ public class HttpUploadClient {
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" +
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" +
"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n";
}

View File

@ -15,23 +15,21 @@
*/
package org.jboss.netty.example.http.upload;
import static org.jboss.netty.channel.Channels.*;
import javax.net.ssl.SSLEngine;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpContentDecompressor;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;
public class HttpUploadClientPipelineFactory implements ChannelPipelineFactory {
private final boolean ssl;
import static org.jboss.netty.channel.Channels.*;
public HttpUploadClientPipelineFactory(boolean ssl) {
this.ssl = ssl;
public class HttpUploadClientPipelineFactory implements ChannelPipelineFactory {
private final SslContext sslCtx;
public HttpUploadClientPipelineFactory(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
public ChannelPipeline getPipeline() throws Exception {
@ -39,12 +37,8 @@ public class HttpUploadClientPipelineFactory implements ChannelPipelineFactory {
ChannelPipeline pipeline = pipeline();
// Enable HTTPS if necessary.
if (ssl) {
SSLEngine engine =
SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
SslHandler handler = new SslHandler(engine);
if (sslCtx != null) {
SslHandler handler = sslCtx.newHandler();
handler.setIssueHandshake(true);
pipeline.addLast("ssl", handler);
}

View File

@ -15,18 +15,23 @@
*/
package org.jboss.netty.example.http.upload;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.SelfSignedCertificate;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
public class HttpUploadServer {
private final int port;
public static boolean isSSL;
private static final boolean useSsl = false; // Set to true to enable SSL.
public HttpUploadServer(int port) {
private final SslContext sslCtx;
private final int port;
public HttpUploadServer(SslContext sslCtx, int port) {
this.sslCtx = sslCtx;
this.port = port;
}
@ -38,22 +43,28 @@ public class HttpUploadServer {
Executors.newCachedThreadPool()));
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new HttpUploadServerPipelineFactory());
bootstrap.setPipelineFactory(new HttpUploadServerPipelineFactory(sslCtx));
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(port));
}
public static void main(String[] args) {
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
if (args.length > 1) {
isSSL = true;
SslContext sslCtx;
if (useSsl) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
} else {
sslCtx = null;
}
new HttpUploadServer(port).run();
new HttpUploadServer(sslCtx, port).run();
}
}

View File

@ -15,29 +15,32 @@
*/
package org.jboss.netty.example.http.upload;
import static org.jboss.netty.channel.Channels.*;
import javax.net.ssl.SSLEngine;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.codec.http.HttpContentCompressor;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.SslHandler;
import static org.jboss.netty.channel.Channels.*;
public class HttpUploadServerPipelineFactory implements ChannelPipelineFactory {
private final SslContext sslCtx;
public HttpUploadServerPipelineFactory(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = pipeline();
if (HttpUploadServer.isSSL) {
SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
SslHandler handler = new SslHandler(engine);
handler.setIssueHandshake(true);
pipeline.addLast("ssl", handler);
if (sslCtx != null) {
SslHandler handler = sslCtx.newHandler();
handler.setIssueHandshake(true);
pipeline.addLast("ssl", handler);
}
pipeline.addLast("decoder", new HttpRequestDecoder());

View File

@ -15,14 +15,16 @@
*/
package org.jboss.netty.example.portunification;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.SelfSignedCertificate;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
/**
* Serves two protocols (HTTP and Factorial) using only one port, enabling
@ -33,9 +35,11 @@ import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
*/
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;
}
@ -49,7 +53,7 @@ public class PortUnificationServer {
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new PortUnificationServerHandler());
return Channels.pipeline(new PortUnificationServerHandler(sslCtx));
}
});
@ -64,6 +68,12 @@ public class PortUnificationServer {
} else {
port = 8080;
}
new PortUnificationServer(port).run();
// Configure SSL context
SelfSignedCertificate ssc = new SelfSignedCertificate();
final SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
// Start the server.
new PortUnificationServer(sslCtx, port).run();
}
}

View File

@ -15,8 +15,6 @@
*/
package org.jboss.netty.example.portunification;
import javax.net.ssl.SSLEngine;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
@ -25,7 +23,6 @@ import org.jboss.netty.example.factorial.BigIntegerDecoder;
import org.jboss.netty.example.factorial.FactorialServerHandler;
import org.jboss.netty.example.factorial.NumberEncoder;
import org.jboss.netty.example.http.snoop.HttpSnoopServerHandler;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.codec.compression.ZlibDecoder;
import org.jboss.netty.handler.codec.compression.ZlibEncoder;
import org.jboss.netty.handler.codec.compression.ZlibWrapper;
@ -33,6 +30,7 @@ import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.jboss.netty.handler.codec.http.HttpContentCompressor;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.SslHandler;
/**
@ -41,14 +39,16 @@ import org.jboss.netty.handler.ssl.SslHandler;
*/
public class PortUnificationServerHandler extends FrameDecoder {
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;
}
@ -116,13 +116,8 @@ public class PortUnificationServerHandler extends FrameDecoder {
private void enableSsl(ChannelHandlerContext ctx) {
ChannelPipeline p = ctx.getPipeline();
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());
p.addLast("unificationA", new PortUnificationServerHandler(sslCtx, false, detectGzip));
p.remove(this);
}
@ -130,7 +125,7 @@ public class PortUnificationServerHandler extends FrameDecoder {
ChannelPipeline p = ctx.getPipeline();
p.addLast("gzipdeflater", new ZlibEncoder(ZlibWrapper.GZIP));
p.addLast("gzipinflater", new ZlibDecoder(ZlibWrapper.GZIP));
p.addLast("unificationB", new PortUnificationServerHandler(detectSsl, false));
p.addLast("unificationB", new PortUnificationServerHandler(sslCtx, detectSsl, false));
p.remove(this);
}

View File

@ -20,6 +20,8 @@ import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.example.telnet.TelnetClient;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.io.BufferedReader;
import java.io.IOException;
@ -32,10 +34,12 @@ import java.util.concurrent.Executors;
*/
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;
}
@ -48,7 +52,7 @@ public class SecureChatClient {
Executors.newCachedThreadPool()));
// Configure the pipeline factory.
bootstrap.setPipelineFactory(new SecureChatClientPipelineFactory());
bootstrap.setPipelineFactory(new SecureChatClientPipelineFactory(sslCtx));
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
@ -107,6 +111,9 @@ public class SecureChatClient {
String host = args[0];
int port = Integer.parseInt(args[1]);
new SecureChatClient(host, port).run();
// Configure the SSL context.
SslContext sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
new SecureChatClient(sslCtx, host, port).run();
}
}

View File

@ -15,23 +15,26 @@
*/
package org.jboss.netty.example.securechat;
import static org.jboss.netty.channel.Channels.*;
import javax.net.ssl.SSLEngine;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.ssl.SslContext;
import static org.jboss.netty.channel.Channels.*;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class SecureChatClientPipelineFactory implements
ChannelPipelineFactory {
public class SecureChatClientPipelineFactory implements ChannelPipelineFactory {
private final SslContext sslCtx;
public SecureChatClientPipelineFactory(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = pipeline();
@ -41,12 +44,7 @@ public class SecureChatClientPipelineFactory implements
// 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());
// 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 org.jboss.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

@ -15,21 +15,25 @@
*/
package org.jboss.netty.example.securechat;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.example.telnet.TelnetServer;
import org.jboss.netty.handler.ssl.SslContext;
import org.jboss.netty.handler.ssl.util.SelfSignedCertificate;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
/**
* 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;
}
@ -41,7 +45,7 @@ public class SecureChatServer {
Executors.newCachedThreadPool()));
// Configure the pipeline factory.
bootstrap.setPipelineFactory(new SecureChatServerPipelineFactory());
bootstrap.setPipelineFactory(new SecureChatServerPipelineFactory(sslCtx));
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(port));
@ -54,6 +58,9 @@ public class SecureChatServer {
} else {
port = 8443;
}
new SecureChatServer(port).run();
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
new SecureChatServer(sslCtx, port).run();
}
}

View File

@ -15,41 +15,36 @@
*/
package org.jboss.netty.example.securechat;
import static org.jboss.netty.channel.Channels.*;
import javax.net.ssl.SSLEngine;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.ssl.SslContext;
import static org.jboss.netty.channel.Channels.*;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*/
public class SecureChatServerPipelineFactory implements
ChannelPipelineFactory {
public class SecureChatServerPipelineFactory implements ChannelPipelineFactory {
private final SslContext sslCtx;
public SecureChatServerPipelineFactory(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = pipeline();
// Add SSL handler first to encrypt and decrypt everything.
// In this example, we use a bogus certificate in the server side
// In this example, we use a self-signed certificate in the server side
// and accept any invalid certificates in the client side.
// You will need something more complicated to identify both
// and server in the real world.
//
// 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());
// On top of the SSL handler, add the text line codec.
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(

View File

@ -1,106 +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 org.jboss.netty.example.securechat;
import org.jboss.netty.handler.ssl.SslHandler;
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;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.Security;
/**
* 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 = Security.getProperty("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,74 +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 org.jboss.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() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
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());
}
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

@ -0,0 +1,32 @@
/*
* 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 org.jboss.netty.handler.ssl;
import java.util.List;
/**
* Selects an application layer protocol in SSL NPN or ALPM.
*/
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,134 @@
/*
* 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 org.jboss.netty.handler.ssl;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
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;
public final class JdkSslClientContext extends JdkSslContext {
private final SSLContext ctx;
private final SSLSessionContext sessCtx;
public JdkSslClientContext() throws SSLException {
this(null, null, null, null, null, 0, 0);
}
public JdkSslClientContext(File certChainFile) throws SSLException {
this(certChainFile, null);
}
public JdkSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
this(null, trustManagerFactory);
}
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
this(null, certChainFile, trustManagerFactory, null, null, 0, 0);
}
/**
* Creates a new factory that creates a new client-side {@link javax.net.ssl.SSLEngine}.
*/
public JdkSslClientContext(
SslBufferPool bufPool, File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(bufPool, 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 (ChannelBuffer buf: PemReader.readCertificates(certChainFile)) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ChannelBufferInputStream(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);
}
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 true;
}
@Override
public ApplicationProtocolSelector nextProtocolSelector() {
return null;
}
@Override
public List<String> nextProtocols() {
return Collections.emptyList();
}
/**
* Returns the {@link javax.net.ssl.SSLContext} object of this factory.
*/
@Override
public SSLContext context() {
return ctx;
}
}

View File

@ -0,0 +1,169 @@
/*
* 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 org.jboss.netty.handler.ssl;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.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;
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): " + 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(SslBufferPool bufferPool, Iterable<String> ciphers) {
super(bufferPool);
cipherSuites = toCipherSuiteArray(ciphers);
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
}
public abstract SSLContext 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() {
SSLEngine engine = context().createSSLEngine();
engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient());
return engine;
}
@Override
public final SSLEngine newEngine(String host, int port) {
SSLEngine engine = context().createSSLEngine(host, port);
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,152 @@
/*
* 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 org.jboss.netty.handler.ssl;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
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;
public final class JdkSslServerContext extends JdkSslContext {
private final SSLContext ctx;
private final SSLSessionContext sessCtx;
public JdkSslServerContext(File certChainFile, File keyFile) throws SSLException {
this(certChainFile, keyFile, null);
}
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
this(null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
/**
* Creates a new factory that creates a new server-side {@link SSLEngine}.
*/
public JdkSslServerContext(
SslBufferPool bufPool,
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(bufPool, 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");
ChannelBuffer 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 (ChannelBuffer buf: PemReader.readCertificates(certChainFile)) {
certChain.add(cf.generateCertificate(new ChannelBufferInputStream(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);
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();
}
/**
* Returns the {@link SSLContext} object of this factory.
*/
@Override
public SSLContext context() {
return ctx;
}
}

View File

@ -30,6 +30,8 @@ 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 {

View File

@ -1,95 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.ssl;
import org.jboss.netty.util.internal.EmptyArrays;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Manages a pool of directly-allocated ByteBuffers.
*
* This is necessary as the reclamation of these buffers does not work appropriately
* on some platforms.
*
* TODO: Attempt to replace the directly-allocated ByteBuffers this with one APR pool.
*/
public final class OpenSslBufferPool {
private static final RuntimeException ALLOCATION_INTERRUPTED =
new IllegalStateException("buffer allocation interrupted");
static {
ALLOCATION_INTERRUPTED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}
// BUFFER_SIZE must be large enough to accomodate the maximum SSL record size.
// Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
private static final int BUFFER_SIZE = 18713;
private final BlockingQueue<ByteBuffer> buffers;
/**
* Construct a new pool with the specified capacity.
*
* @param capacity The number of buffers to instantiate.
*/
public OpenSslBufferPool(int capacity) {
OpenSsl.ensureAvailability();
buffers = new LinkedBlockingQueue<ByteBuffer>(capacity);
while (buffers.remainingCapacity() > 0) {
ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE).order(ByteOrder.nativeOrder());
buffers.offer(buf);
}
}
/**
* Take a buffer from the pool.
*
* @return a ByteBuffer.
*/
public ByteBuffer acquire() {
try {
return buffers.take();
} catch (InterruptedException ignore) {
throw ALLOCATION_INTERRUPTED;
}
}
/**
* Release a buffer back into the stream
*
* @param buffer the ByteBuffer to release
*/
public void release(ByteBuffer buffer) {
buffer.clear();
buffers.offer(buffer);
}
@Override
public String toString() {
return "[DirectBufferPool " +
buffers.size() + " buffers * " +
BUFFER_SIZE + " bytes = " +
buffers.size() * BUFFER_SIZE + " total bytes; " +
"size: " + buffers.size() +
" remainingCapacity: " + buffers.remainingCapacity() +
']';
}
}

View File

@ -1,164 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.ssl;
import org.jboss.netty.util.internal.StringUtil;
import javax.net.ssl.SSLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Builds a new OpenSSL context object.
*/
public class OpenSslContextBuilder {
private long aprPool;
private OpenSslBufferPool bufPool;
private String certPath;
private String keyPath;
private List<String> cipherSpec;
private int sessionCacheSize;
private int sessionTimeout;
private String keyPassword;
private String caPath;
private String nextProtos;
public OpenSslContextBuilder() {
OpenSsl.ensureAvailability();
}
public OpenSslContextBuilder certPath(String certPath) {
if (certPath == null) {
throw new NullPointerException("certPath");
}
this.certPath = certPath;
return this;
}
public OpenSslContextBuilder keyPath(String keyPath) {
if (keyPath == null) {
throw new NullPointerException("keyPath");
}
this.keyPath = keyPath;
return this;
}
public OpenSslContextBuilder cipherSpec(Iterable<String> cipherSpec) {
if (cipherSpec == null) {
this.cipherSpec = null;
return this;
}
List<String> list = new ArrayList<String>();
for (String c: cipherSpec) {
if (c == null) {
break;
}
if (c.contains(":")) {
for (String cc : StringUtil.split(c, ':')) {
if (cc.length() != 0) {
list.add(cc);
}
}
} else {
list.add(c);
}
}
if (list.isEmpty()) {
this.cipherSpec = null;
} else {
this.cipherSpec = list;
}
return this;
}
public OpenSslContextBuilder cipherSpec(String... cipherSpec) {
if (cipherSpec == null) {
throw new NullPointerException("cipherSpec");
}
return cipherSpec(Arrays.asList(cipherSpec));
}
public OpenSslContextBuilder keyPassword(String keyPassword) {
this.keyPassword = keyPassword;
return this;
}
public OpenSslContextBuilder caPath(String caPath) {
this.caPath = caPath;
return this;
}
public OpenSslContextBuilder nextProtos(String nextProtos) {
this.nextProtos = nextProtos;
return this;
}
public OpenSslContextBuilder sessionCacheSize(int sessionCacheSize) {
this.sessionCacheSize = sessionCacheSize;
return this;
}
public OpenSslContextBuilder sessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
return this;
}
public OpenSslContextBuilder aprPool(long aprPool) {
this.aprPool = aprPool;
return this;
}
public OpenSslContextBuilder bufPool(OpenSslBufferPool bufPool) {
this.bufPool = bufPool;
return this;
}
/**
* Creates a new server context.
*
* @throws SSLException if the required fields are not assigned
*/
public OpenSslServerContext newServerContext() throws SSLException {
// If the cipherSpec was not specified or empty, use the default.
if (cipherSpec == null) {
cipherSpec = new ArrayList<String>();
}
if (cipherSpec.isEmpty()) {
Collections.addAll(
cipherSpec,
"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");
}
return new OpenSslServerContext(
aprPool, bufPool,
certPath, keyPath, keyPassword, caPath, nextProtos, cipherSpec, sessionCacheSize, sessionTimeout);
}
}

View File

@ -67,8 +67,6 @@ public final class OpenSslEngine extends SSLEngine {
private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
private static final int MAX_ENCRYPTED_PACKET = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
private static final String SSL_IGNORABLE_ERROR_PREFIX = "error:00000000:";
private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
@ -95,10 +93,10 @@ public final class OpenSslEngine extends SSLEngine {
private int lastPrimingReadResult;
private final OpenSslBufferPool bufPool;
private final SslBufferPool bufPool;
private SSLSession session;
public OpenSslEngine(long sslContext, OpenSslBufferPool bufPool) {
public OpenSslEngine(long sslContext, SslBufferPool bufPool) {
OpenSsl.ensureAvailability();
if (sslContext == 0) {
throw new NullPointerException("sslContext");
@ -129,107 +127,148 @@ public final class OpenSslEngine extends SSLEngine {
* Calling this function with src.remaining == 0 is undefined.
*/
private int writePlaintextData(final ByteBuffer src) {
final ByteBuffer buf = bufPool.acquire();
final long addr = Buffer.address(buf);
try {
int position = src.position();
int limit = src.limit();
int len = Math.min(src.remaining(), MAX_PLAINTEXT_LENGTH);
if (len > buf.capacity()) {
throw new IllegalStateException("buffer pool write overflow");
}
src.limit(position + len);
final int pos = src.position();
final int limit = src.limit();
final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
final int sslWrote;
buf.put(src);
src.limit(limit);
final int sslWrote = SSL.writeToSSL(ssl, addr, len);
if (src.isDirect()) {
final long addr = Buffer.address(src) + pos;
sslWrote = SSL.writeToSSL(ssl, addr, len);
if (sslWrote > 0) {
src.position(position + sslWrote);
src.position(pos + sslWrote);
return sslWrote;
} else {
src.position(position);
throw new IllegalStateException("SSL.writeToSSL() returned a non-positive value: " + sslWrote);
}
} finally {
bufPool.release(buf);
} else {
final ByteBuffer buf = bufPool.acquireBuffer();
try {
assert buf.isDirect();
assert len <= buf.capacity() : "buffer pool write overflow";
final long addr = Buffer.address(buf);
src.limit(pos + len);
buf.put(src);
src.limit(limit);
sslWrote = SSL.writeToSSL(ssl, addr, len);
if (sslWrote > 0) {
src.position(pos + sslWrote);
return sslWrote;
} else {
src.position(pos);
}
} finally {
bufPool.releaseBuffer(buf);
}
}
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 ByteBuffer buf = bufPool.acquire();
final long addr = Buffer.address(buf);
try {
int position = src.position();
int len = src.remaining();
if (len > buf.capacity()) {
throw new IllegalStateException("buffer pool write overflow");
}
buf.put(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(position + netWrote);
src.position(pos + netWrote);
lastPrimingReadResult = SSL.readFromSSL(ssl, addr, 0); // priming read
return netWrote;
} else {
src.position(position);
return 0;
}
} finally {
bufPool.release(buf);
} else {
final ByteBuffer buf = bufPool.acquireBuffer();
try {
assert buf.isDirect();
assert len <= buf.capacity();
final long addr = Buffer.address(buf);
buf.put(src);
final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
src.position(pos + netWrote);
if (netWrote >= 0) {
lastPrimingReadResult = SSL.readFromSSL(ssl, addr, 0); // priming read
return netWrote;
}
} finally {
bufPool.releaseBuffer(buf);
}
}
return 0;
}
/**
* Read plaintext data from the OpenSSL internal BIO
*/
private int readPlaintextData(final ByteBuffer dst) {
final ByteBuffer buf = bufPool.acquire();
final long addr = Buffer.address(buf);
try {
final int len = Math.min(buf.capacity(), dst.capacity());
buf.limit(len);
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) {
buf.limit(sslRead);
dst.put(buf);
dst.position(pos + sslRead);
return sslRead;
} else {
return 0;
}
} finally {
bufPool.release(buf);
} else {
final ByteBuffer buf = bufPool.acquireBuffer();
try {
assert buf.isDirect();
final long addr = Buffer.address(buf);
final int len = Math.min(buf.capacity(), dst.remaining());
buf.limit(len);
final int sslRead = SSL.readFromSSL(ssl, addr, len);
if (sslRead > 0) {
buf.limit(sslRead);
dst.put(buf);
return sslRead;
}
} finally {
bufPool.releaseBuffer(buf);
}
}
return 0;
}
/**
* Read encrypted data from the OpenSSL network BIO
*/
private int readEncryptedData(final ByteBuffer dst, final int pending) {
final ByteBuffer buf = bufPool.acquire();
final long addr = Buffer.address(buf);
try {
if (pending > buf.capacity()) {
throw new IllegalStateException("network BIO read overflow " +
"(pending: " + pending + ", capacity: " + buf.capacity() + ')');
}
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) {
buf.limit(bioRead);
dst.put(buf);
dst.position(pos + bioRead);
return bioRead;
} else {
return 0;
}
} finally {
bufPool.release(buf);
} else {
final ByteBuffer buf = bufPool.acquireBuffer();
try {
assert buf.isDirect();
final long addr = Buffer.address(buf);
assert buf.capacity() >= pending :
"network BIO read overflow (pending: " + pending + ", capacity: " + buf.capacity() + ')';
final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
if (bioRead > 0) {
buf.limit(bioRead);
dst.put(buf);
return bioRead;
}
} finally {
bufPool.releaseBuffer(buf);
}
}
return 0;
}
/**
@ -412,7 +451,7 @@ public final class OpenSslEngine extends SSLEngine {
// Check for OpenSSL errors caused by the priming read
String error = SSL.getLastError();
if (error != null && !error.startsWith(SSL_IGNORABLE_ERROR_PREFIX)) {
if (error != null && !error.startsWith(OpenSsl.IGNORABLE_ERROR_PREFIX)) {
if (logger.isInfoEnabled()) {
logger.info(
"SSL_read failed: primingReadResult: " + lastPrimingReadResult +

View File

@ -21,9 +21,12 @@ import org.apache.tomcat.jni.SSLContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
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;
@ -46,101 +49,106 @@ import java.util.List;
* </pre>
*
*/
public final class OpenSslServerContext {
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 OpenSslBufferPool bufPool;
private final boolean destroyAprPool;
private final List<String> cipherSpec = new ArrayList<String>();
private final List<String> unmodifiableCipherSpec = Collections.unmodifiableList(cipherSpec);
private final String cipherSpecText;
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 String nextProtos;
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;
public OpenSslServerContext(File certChainFile, File keyFile) throws SSLException {
this(certChainFile, keyFile, null);
}
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
this(null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
public OpenSslServerContext(
long aprPool, OpenSslBufferPool bufPool,
String certPath, String keyPath, String keyPassword,
String caPath, String nextProtos, Iterable<String> ciphers,
SslBufferPool bufPool,
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(bufPool);
OpenSsl.ensureAvailability();
if (certPath == null) {
throw new NullPointerException("certPath");
if (certChainFile == null) {
throw new NullPointerException("certChainFile");
}
if (certPath.length() == 0) {
throw new IllegalArgumentException("certPath is empty.");
if (!certChainFile.isFile()) {
throw new IllegalArgumentException("certChainFile is not a file: " + certChainFile);
}
if (keyPath == null) {
if (keyFile == null) {
throw new NullPointerException("keyPath");
}
if (keyPath.length() == 0) {
throw new IllegalArgumentException("keyPath is empty.");
if (!keyFile.isFile()) {
throw new IllegalArgumentException("keyPath is not a file: " + keyFile);
}
if (ciphers == null) {
throw new NullPointerException("ciphers");
ciphers = DEFAULT_CIPHERS;
}
if (keyPassword == null) {
keyPassword = "";
}
if (caPath == null) {
caPath = "";
}
if (nextProtos == null) {
nextProtos = "";
if (nextProtocols == null) {
nextProtocols = Collections.emptyList();
}
for (String c: ciphers) {
if (c == null) {
break;
}
cipherSpec.add(c);
this.ciphers.add(c);
}
this.nextProtos = nextProtos;
// Convert the cipher list into a colon-separated string.
StringBuilder cipherSpecBuf = new StringBuilder();
for (String c: cipherSpec) {
cipherSpecBuf.append(c);
cipherSpecBuf.append(':');
}
cipherSpecBuf.setLength(cipherSpecBuf.length() - 1);
cipherSpecText = cipherSpecBuf.toString();
// Allocate a new APR pool if necessary.
if (aprPool == 0) {
aprPool = Pool.create(0);
destroyAprPool = true;
} else {
destroyAprPool = false;
}
// Allocate a new OpenSSL buffer pool if necessary.
boolean success = false;
try {
if (bufPool == null) {
bufPool = new OpenSslBufferPool(Runtime.getRuntime().availableProcessors() * 2);
}
success = true;
} finally {
if (!success && destroyAprPool) {
Pool.destroy(aprPool);
for (String p: nextProtocols) {
if (p == null) {
break;
}
this.nextProtocols.add(p);
}
this.aprPool = aprPool;
this.bufPool = bufPool;
// Allocate a new APR pool.
aprPool = Pool.create(0);
// Create a new SSL_CTX and configure it.
success = false;
boolean success = false;
try {
synchronized (OpenSslServerContext.class) {
try {
@ -158,11 +166,19 @@ public final class OpenSslServerContext {
/* List the ciphers that the client is permitted to negotiate. */
try {
SSLContext.setCipherSuite(ctx, cipherSpecText);
// 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: " + cipherSpecText, e);
throw new SSLException("failed to set cipher suite: " + this.ciphers, e);
}
/* Set certificate verification policy. */
@ -171,30 +187,36 @@ public final class OpenSslServerContext {
/* Load the certificate file and private key. */
try {
if (!SSLContext.setCertificate(
ctx, certPath, keyPath, keyPassword, SSL.SSL_AIDX_RSA)) {
throw new SSLException(
"failed to set certificate: " + certPath + " (" + SSL.getLastError() + ')');
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: " + certPath, e);
throw new SSLException("failed to set certificate: " + certChainFile + " and " + keyFile, e);
}
/* Load certificate chain file, if specified */
if (caPath.length() != 0) {
/* If named same as cert file, we must skip the first cert since it was loaded above. */
boolean skipFirstCert = certPath.equals(caPath);
if (!SSLContext.setCertificateChainFile(ctx, caPath, skipFirstCert)) {
/* 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: " + caPath + " (" + SSL.getLastError() + ')');
"failed to set certificate chain: " + certChainFile + " (" + SSL.getLastError() + ')');
}
}
/* Set next protocols for next protocol negotiation extension, if specified */
if (nextProtos.length() != 0) {
SSLContext.setNextProtos(ctx, nextProtos);
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 */
@ -229,31 +251,69 @@ public final class OpenSslServerContext {
stats = new OpenSslSessionStats(ctx);
}
public List<String> cipherSpec() {
return unmodifiableCipherSpec;
@Override
SslBufferPool newBufferPool() {
return new SslBufferPool(true, true);
}
public String cipherSpecText() {
return cipherSpecText;
@Override
public boolean isClient() {
return false;
}
public String nextProtos() {
return nextProtos;
@Override
public List<String> cipherSuites() {
return unmodifiableCiphers;
}
public int sessionCacheSize() {
return sessionCacheSize > Integer.MAX_VALUE? Integer.MAX_VALUE : (int) sessionCacheSize;
@Override
public long sessionCacheSize() {
return sessionCacheSize;
}
public int sessionTimeout() {
return sessionTimeout > Integer.MAX_VALUE? Integer.MAX_VALUE : (int) sessionTimeout;
@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 factory.
*/
public long context() {
return ctx;
}
public OpenSslSessionStats stats() {
return stats;
}
/**
* Returns a new server-side {@link SSLEngine} with the current configuration.
*/
@Override
public SSLEngine newEngine() {
return new OpenSslEngine(ctx, bufferPool());
}
@Override
public SSLEngine newEngine(String host, int port) {
throw new UnsupportedOperationException();
}
public void setTicketKeys(byte[] keys) {
if (keys != null) {
throw new NullPointerException("keys");
}
SSLContext.setSessionTicketKeys(ctx, keys);
}
@ -271,15 +331,8 @@ public final class OpenSslServerContext {
}
private void destroyPools() {
if (destroyAprPool && aprPool != 0) {
if (aprPool != 0) {
Pool.destroy(aprPool);
}
}
/**
* Returns a new server-side {@link SSLEngine} with the current configuration.
*/
public SSLEngine newEngine() {
return new OpenSslEngine(ctx, bufPool);
}
}

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 org.jboss.netty.handler.ssl;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.handler.codec.base64.Base64;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.util.CharsetUtil;
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 ChannelBuffer[] 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<ChannelBuffer> certs = new ArrayList<ChannelBuffer>();
Matcher m = CERT_PATTERN.matcher(content);
int start = 0;
for (;;) {
if (!m.find(start)) {
break;
}
certs.add(Base64.decode(ChannelBuffers.copiedBuffer(m.group(1), CharsetUtil.US_ASCII)));
start = m.end();
}
if (certs.isEmpty()) {
throw new CertificateException("found no certificates: " + file);
}
return certs.toArray(new ChannelBuffer[certs.size()]);
}
static ChannelBuffer 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(ChannelBuffers.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

@ -17,6 +17,9 @@ package org.jboss.netty.handler.ssl;
import javax.net.ssl.SSLEngine;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A {@link ByteBuffer} pool dedicated for {@link SslHandler} performance
@ -36,37 +39,77 @@ public class SslBufferPool {
// Add 1024 as a room for compressed data and another 1024 for Apache Harmony compatibility.
private static final int MAX_PACKET_SIZE = 16665 + 2048;
private static final int DEFAULT_POOL_SIZE = MAX_PACKET_SIZE * 1024;
private static final int MAX_PACKET_SIZE_ALIGNED = (MAX_PACKET_SIZE / 128 + 1) * 128;
private final ByteBuffer[] pool;
private static final int DEFAULT_POOL_SIZE = MAX_PACKET_SIZE_ALIGNED * 1024;
private final ByteBuffer preallocated;
private final BlockingQueue<ByteBuffer> pool;
private final int maxBufferCount;
private int index;
private final boolean allocateDirect;
/**
* Creates a new buffer pool whose size is {@code 18113536}, which can
* The number of buffers allocated so far. Used only when {@link #preallocated} is null.
*/
private final AtomicInteger numAllocations;
/**
* Creates a new buffer pool whose size is {@code 19267584}, which can
* hold {@code 1024} buffers.
*/
public SslBufferPool() {
this(DEFAULT_POOL_SIZE);
}
/**
* Creates a new buffer pool whose size is {@code 19267584}, which can
* hold {@code 1024} buffers.
*/
public SslBufferPool(boolean preallocate, boolean allocateDirect) {
this(DEFAULT_POOL_SIZE, preallocate, allocateDirect);
}
/**
* Creates a new buffer pool.
*
* @param maxPoolSize the maximum number of bytes that this pool can hold
*/
public SslBufferPool(int maxPoolSize) {
this(maxPoolSize, false, false);
}
/**
* Creates a new buffer pool.
*
* @param maxPoolSize the maximum number of bytes that this pool can hold
*/
public SslBufferPool(int maxPoolSize, boolean preallocate, boolean allocateDirect) {
if (maxPoolSize <= 0) {
throw new IllegalArgumentException("maxPoolSize: " + maxPoolSize);
}
int maxBufferCount = maxPoolSize / MAX_PACKET_SIZE;
if (maxPoolSize % MAX_PACKET_SIZE != 0) {
int maxBufferCount = maxPoolSize / MAX_PACKET_SIZE_ALIGNED;
if (maxPoolSize % MAX_PACKET_SIZE_ALIGNED != 0) {
maxBufferCount ++;
}
pool = new ByteBuffer[maxBufferCount];
this.maxBufferCount = maxBufferCount;
this.allocateDirect = allocateDirect;
pool = new ArrayBlockingQueue<ByteBuffer>(maxBufferCount);
if (preallocate) {
preallocated = allocate(maxBufferCount * MAX_PACKET_SIZE_ALIGNED);
numAllocations = null;
for (int i = 0; i < maxBufferCount; i ++) {
int pos = i * MAX_PACKET_SIZE_ALIGNED;
preallocated.clear().position(pos).limit(pos + MAX_PACKET_SIZE_ALIGNED);
pool.add(preallocated.slice());
}
} else {
preallocated = null;
numAllocations = new AtomicInteger();
}
}
/**
@ -74,7 +117,7 @@ public class SslBufferPool {
* can be somewhat different from what was specified in the constructor.
*/
public int getMaxPoolSize() {
return maxBufferCount * MAX_PACKET_SIZE;
return maxBufferCount * MAX_PACKET_SIZE_ALIGNED;
}
/**
@ -84,45 +127,55 @@ public class SslBufferPool {
* pool is getting exhausted. If it keeps returns a unnecessarily big
* value, it means the pool is wasting the heap space.
*/
public synchronized int getUnacquiredPoolSize() {
return index * MAX_PACKET_SIZE;
public int getUnacquiredPoolSize() {
return pool.size() * MAX_PACKET_SIZE_ALIGNED;
}
/**
* Acquire a new {@link ByteBuffer} out of the {@link SslBufferPool}
*
*/
public synchronized ByteBuffer acquireBuffer() {
if (index == 0) {
return ByteBuffer.allocate(MAX_PACKET_SIZE);
public ByteBuffer acquireBuffer() {
ByteBuffer buf;
if (preallocated != null || numAllocations.get() >= maxBufferCount) {
boolean interrupted = false;
for (;;) {
try {
buf = pool.take();
break;
} catch (InterruptedException ignore) {
interrupted = true;
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
} else {
return (ByteBuffer) pool[-- index].clear();
buf = pool.poll();
if (buf == null) {
// Note that we can allocate more buffers than maxBufferCount.
// We will discard the buffers allocated after numAllocations reached maxBufferCount in releaseBuffer().
numAllocations.incrementAndGet();
buf = allocate(MAX_PACKET_SIZE);
}
}
}
/**
* Will get removed. Please use {@link #acquireBuffer()}
*
*/
@Deprecated
ByteBuffer acquire() {
return acquireBuffer();
buf.clear();
return buf;
}
/**
* Release a previous acquired {@link ByteBuffer}
*/
public synchronized void releaseBuffer(ByteBuffer buffer) {
if (index < maxBufferCount) {
pool[index ++] = buffer;
}
public void releaseBuffer(ByteBuffer buffer) {
pool.offer(buffer);
}
/**
* @deprecated Use {@link #releaseBuffer(ByteBuffer)}
*/
@Deprecated
void release(ByteBuffer buffer) {
releaseBuffer(buffer);
private ByteBuffer allocate(int capacity) {
if (allocateDirect) {
return ByteBuffer.allocateDirect(capacity);
} else {
return ByteBuffer.allocate(capacity);
}
}
}

View File

@ -0,0 +1,206 @@
/*
* 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 org.jboss.netty.handler.ssl;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.util.List;
/**
* Creates a new {@link SSLEngine}. Using its factory methods, you can let it choose the optimal {@link SSLEngine}
* implementation available to you (the default JDK {@link SSLEngine} or the one that uses OpenSSL native library).
*/
public abstract class SslContext {
public static SslProvider defaultServerProvider() {
if (OpenSsl.isAvailable()) {
return SslProvider.OPENSSL;
} else {
return SslProvider.JDK;
}
}
public static SslProvider defaultClientProvider() {
return SslProvider.JDK;
}
public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
return newServerContext(null, null, certChainFile, keyFile, null, null, null, 0, 0);
}
public static SslContext newServerContext(
File certChainFile, File keyFile, String keyPassword) throws SSLException {
return newServerContext(null, null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
public static SslContext newServerContext(
SslBufferPool bufPool,
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(
null, bufPool, certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
}
public static SslContext newServerContext(
SslProvider provider, File certChainFile, File keyFile) throws SSLException {
return newServerContext(provider, null, certChainFile, keyFile, null, null, null, 0, 0);
}
public static SslContext newServerContext(
SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
return newServerContext(provider, null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
}
public static SslContext newServerContext(
SslProvider provider, SslBufferPool bufPool,
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(
bufPool, certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
case OPENSSL:
return new OpenSslServerContext(
bufPool, certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
default:
throw new Error(provider.toString());
}
}
public static SslContext newClientContext() throws SSLException {
return newClientContext(null, null, null, null, null, null, 0, 0);
}
public static SslContext newClientContext(File certChainFile) throws SSLException {
return newClientContext(null, null, certChainFile, null, null, null, 0, 0);
}
public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(null, null, null, trustManagerFactory, null, null, 0, 0);
}
public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(null, null, certChainFile, trustManagerFactory, null, null, 0, 0);
}
public static SslContext newClientContext(
SslBufferPool bufPool,
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, ApplicationProtocolSelector nextProtocolSelector,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext(
null, bufPool, certChainFile, trustManagerFactory,
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout);
}
public static SslContext newClientContext(SslProvider provider) throws SSLException {
return newClientContext(provider, null, null, null, null, null, 0, 0);
}
public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
return newClientContext(provider, null, certChainFile, null, null, null, 0, 0);
}
public static SslContext newClientContext(
SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(provider, null, null, trustManagerFactory, null, null, 0, 0);
}
public static SslContext newClientContext(
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(provider, null, certChainFile, trustManagerFactory, null, null, 0, 0);
}
public static SslContext newClientContext(
SslProvider provider, SslBufferPool bufPool,
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(
bufPool, certChainFile, trustManagerFactory,
ciphers, nextProtocolSelector, sessionCacheSize, sessionTimeout);
}
private final SslBufferPool bufferPool;
SslContext(SslBufferPool bufferPool) {
this.bufferPool = bufferPool == null? newBufferPool() : bufferPool;
}
SslBufferPool newBufferPool() {
return new SslBufferPool(false, false);
}
public final boolean isServer() {
return !isClient();
}
public final SslBufferPool bufferPool() {
return bufferPool;
}
public abstract boolean isClient();
public abstract List<String> cipherSuites();
public abstract long sessionCacheSize();
public abstract long sessionTimeout();
public abstract ApplicationProtocolSelector nextProtocolSelector();
public abstract List<String> nextProtocols();
public abstract SSLEngine newEngine();
public abstract SSLEngine newEngine(String host, int port);
public final SslHandler newHandler() {
return newHandler(newEngine());
}
public final SslHandler newHandler(String host, int port) {
return newHandler(newEngine(host, port));
}
private SslHandler newHandler(SSLEngine engine) {
SslHandler handler = new SslHandler(engine, bufferPool());
if (isClient()) {
handler.setIssueHandshake(true);
}
handler.setCloseOnSSLException(true);
return handler;
}
}

View File

@ -243,7 +243,7 @@ public class SslHandler extends FrameDecoder
private volatile boolean writeBeforeHandshakeDone;
private final SSLEngineInboundCloseFuture sslEngineCloseFuture = new SSLEngineInboundCloseFuture();
private boolean closeOnSSLException;
private boolean closeOnSslException;
private int packetLength;
@ -440,7 +440,7 @@ public class SslHandler extends FrameDecoder
hsFuture.setFailure(cause);
fireExceptionCaught(ctx, cause);
if (closeOnSSLException) {
if (closeOnSslException) {
Channels.close(ctx, future(channel));
}
}
@ -450,13 +450,13 @@ public class SslHandler extends FrameDecoder
handshakeFuture.setFailure(e);
fireExceptionCaught(ctx, e);
if (closeOnSSLException) {
if (closeOnSslException) {
Channels.close(ctx, future(channel));
}
}
} else { // Failed to initiate handshake.
fireExceptionCaught(ctx, exception);
if (closeOnSSLException) {
if (closeOnSslException) {
Channels.close(ctx, future(channel));
}
}
@ -484,7 +484,7 @@ public class SslHandler extends FrameDecoder
return wrapNonAppData(ctx, channel);
} catch (SSLException e) {
fireExceptionCaught(ctx, e);
if (closeOnSSLException) {
if (closeOnSslException) {
Channels.close(ctx, future(channel));
}
return failedFuture(channel, e);
@ -562,11 +562,11 @@ public class SslHandler extends FrameDecoder
if (ctx != null) {
throw new IllegalStateException("Can only get changed before attached to ChannelPipeline");
}
closeOnSSLException = closeOnSslException;
this.closeOnSslException = closeOnSslException;
}
public boolean getCloseOnSSLException() {
return closeOnSSLException;
return closeOnSslException;
}
public void handleDownstream(
@ -933,7 +933,7 @@ public class SslHandler extends FrameDecoder
NotSslRecordException e = new NotSslRecordException(
"not an SSL/TLS record: " + ChannelBuffers.hexDump(in));
in.skipBytes(in.readableBytes());
if (closeOnSSLException) {
if (closeOnSslException) {
// first trigger the exception and then close the channel
fireExceptionCaught(ctx, e);
Channels.close(ctx, future(channel));
@ -1271,9 +1271,9 @@ public class SslHandler extends FrameDecoder
ChannelBuffer nettyInNetBuf, ByteBuffer nioInNetBuf,
ChannelBuffer nettyOutAppBuf, int initialNettyOutAppBufCapacity) throws SSLException {
final ByteBuffer nioOutAppBuf = bufferPool.acquireBuffer();
final int nettyInNetBufStartOffset = nettyInNetBuf.readerIndex();
final int nioInNetBufStartOffset = nioInNetBuf.position();
final ByteBuffer nioOutAppBuf = bufferPool.acquireBuffer();
try {
boolean needsWrap = false;
@ -1545,6 +1545,10 @@ public class SslHandler extends FrameDecoder
cancelHandshakeTimeout();
}
if (logger.isDebugEnabled()) {
logger.debug(channel + " HANDSHAKEN: " + engine.getSession().getCipherSuite());
}
handshakeFuture.setSuccess();
}
@ -1580,7 +1584,7 @@ public class SslHandler extends FrameDecoder
}
handshakeFuture.setFailure(cause);
if (closeOnSSLException) {
if (closeOnSslException) {
Channels.close(ctx, future(channel));
}
}

View File

@ -0,0 +1,25 @@
/*
* 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 org.jboss.netty.handler.ssl;
/**
* An enumeration of SSL/TLS protocol providers.
*/
public enum SslProvider {
JDK,
OPENSSL
}

View File

@ -0,0 +1,58 @@
/*
* 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 org.jboss.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 org.jboss.netty.handler.ssl.util.SelfSignedCertificate.*;
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,162 @@
/*
* 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 org.jboss.netty.handler.ssl.util;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.util.internal.EmptyArrays;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
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;
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() {
public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException {
checkTrusted("client", chain);
}
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());
}
public X509Certificate[] getAcceptedIssuers() {
return EmptyArrays.EMPTY_X509_CERTIFICATES;
}
};
private final byte[][] fingerprints;
public FingerprintTrustManagerFactory(Iterable<String> fingerprints) {
this(toFingerprintArray(fingerprints));
}
public FingerprintTrustManagerFactory(String... fingerprints) {
this(toFingerprintArray(Arrays.asList(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: " +
ChannelBuffers.hexDump(ChannelBuffers.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,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 org.jboss.netty.handler.ssl.util;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.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;
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() {
public void checkClientTrusted(X509Certificate[] chain, String s) {
logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN());
}
public void checkServerTrusted(X509Certificate[] chain, String s) {
logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN());
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
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,69 @@
/*
* 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 org.jboss.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 org.jboss.netty.handler.ssl.util.SelfSignedCertificate.*;
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,177 @@
/*
* 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 org.jboss.netty.handler.ssl.util;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.handler.codec.base64.Base64;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.util.CharsetUtil;
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.
* 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>
* At first, this method tries to use OpenJDK's X.509 implementation ({@code sun.security.x509}).
* If it fails, it secondly tries to use <a href="http://www.bouncycastle.org/">Bouncy Castle</a>.
* </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;
public SelfSignedCertificate() throws CertificateException {
this("example.com");
}
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);
}
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]);
}
public File certificate() {
return certificate;
}
public File privateKey() {
return privateKey;
}
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(ChannelBuffers.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.name()));
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(ChannelBuffers.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.name()));
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,109 @@
/*
* 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 org.jboss.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();
}
};
protected SimpleTrustManagerFactory() {
this("");
}
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");
}
}
protected abstract void engineInit(KeyStore keyStore) throws Exception;
protected abstract void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception;
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 org.jboss.netty.handler.ssl.util;
import org.jboss.netty.util.internal.ThreadLocalRandom;
import java.security.SecureRandom;
import java.util.Random;
/**
* Insecure {@link 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 org.jboss.netty.handler.ssl.util;

View File

@ -17,6 +17,7 @@
package org.jboss.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

@ -46,7 +46,7 @@ import java.util.Random;
*
* @since 1.7
*/
final class ThreadLocalRandom extends Random {
public final class ThreadLocalRandom extends Random {
// same constants as Random, but must be redeclared because private
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
@ -87,7 +87,7 @@ final class ThreadLocalRandom extends Random {
*
* @return the current thread's {@code ThreadLocalRandom}
*/
static ThreadLocalRandom current() {
public static ThreadLocalRandom current() {
return localRandom.get();
}

View File

@ -1,39 +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 org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import java.util.concurrent.Executor;
public class NioNioSocketSslEchoTest extends AbstractSocketSslEchoTest {
public NioNioSocketSslEchoTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
super(serverEngineFactory, clientEngineFactory);
}
@Override
protected ChannelFactory newClientSocketChannelFactory(Executor executor) {
return new NioClientSocketChannelFactory(executor, executor);
}
@Override
protected ChannelFactory newServerSocketChannelFactory(Executor executor) {
return new NioServerSocketChannelFactory(executor, executor);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.ssl.AbstractSocketSslEchoTest.SSLEngineFactory;
import java.util.concurrent.Executor;
public class NioNioSocketSslGreetingTest extends AbstractSocketSslGreetingTest {
public NioNioSocketSslGreetingTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
super(serverEngineFactory, clientEngineFactory);
}
@Override
protected ChannelFactory newClientSocketChannelFactory(Executor executor) {
return new NioClientSocketChannelFactory(executor, executor);
}
@Override
protected ChannelFactory newServerSocketChannelFactory(Executor executor) {
return new NioServerSocketChannelFactory(executor, executor);
}
}

View File

@ -1,40 +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 org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory;
import java.util.concurrent.Executor;
public class NioOioSocketSslEchoTest extends AbstractSocketSslEchoTest {
public NioOioSocketSslEchoTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
super(serverEngineFactory, clientEngineFactory);
}
@Override
protected ChannelFactory newClientSocketChannelFactory(Executor executor) {
return new NioClientSocketChannelFactory(executor, executor);
}
@Override
protected ChannelFactory newServerSocketChannelFactory(Executor executor) {
return new OioServerSocketChannelFactory(executor, executor);
}
}

View File

@ -1,40 +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 org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import java.util.concurrent.Executor;
public class OioNioSocketSslEchoTest extends AbstractSocketSslEchoTest {
public OioNioSocketSslEchoTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
super(serverEngineFactory, clientEngineFactory);
}
@Override
protected ChannelFactory newClientSocketChannelFactory(Executor executor) {
return new OioClientSocketChannelFactory(executor);
}
@Override
protected ChannelFactory newServerSocketChannelFactory(Executor executor) {
return new NioServerSocketChannelFactory(executor, executor);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import org.jboss.netty.handler.ssl.AbstractSocketSslEchoTest.SSLEngineFactory;
import java.util.concurrent.Executor;
public class OioNioSocketSslGreetingTest extends AbstractSocketSslGreetingTest {
public OioNioSocketSslGreetingTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
super(serverEngineFactory, clientEngineFactory);
}
@Override
protected ChannelFactory newClientSocketChannelFactory(Executor executor) {
return new OioClientSocketChannelFactory(executor);
}
@Override
protected ChannelFactory newServerSocketChannelFactory(Executor executor) {
return new NioServerSocketChannelFactory(executor, executor);
}
}

View File

@ -1,44 +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 org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory;
import java.util.concurrent.Executor;
public class OioOioSocketSslEchoTest extends AbstractSocketSslEchoTest {
public OioOioSocketSslEchoTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
super(serverEngineFactory, clientEngineFactory);
}
@Override
protected ChannelFactory newClientSocketChannelFactory(Executor executor) {
return new OioClientSocketChannelFactory(executor);
}
@Override
protected ChannelFactory newServerSocketChannelFactory(Executor executor) {
return new OioServerSocketChannelFactory(executor, executor);
}
@Override
protected boolean isExecutorRequired() {
return true;
}
}

View File

@ -27,156 +27,35 @@ import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.util.TestUtil;
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.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public abstract class AbstractSocketSslEchoTest {
static final InternalLogger logger =
InternalLoggerFactory.getInstance(AbstractSocketSslEchoTest.class);
public class SocketSslEchoTest extends SslTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocketSslEchoTest.class);
private static final Random random = new Random();
static final byte[] data = new byte[1048576];
static {
random.nextBytes(data);
}
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}")
public static Collection<SSLEngineFactory[]> engines() throws Exception {
List<SSLEngineFactory[]> params = new ArrayList<SSLEngineFactory[]>();
List<SSLEngineFactory> serverEngines = new ArrayList<SSLEngineFactory>();
serverEngines.add(new SSLEngineFactory("JDK") {
@Override
public SSLEngine newEngine() {
return SecureChatSslContextFactory.getServerContext().createSSLEngine();
}
});
List<SSLEngineFactory> clientEngines = new ArrayList<SSLEngineFactory>();
clientEngines.add(new SSLEngineFactory("JDK") {
@Override
public SSLEngine newEngine() {
return SecureChatSslContextFactory.getClientContext().createSSLEngine();
}
});
boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) {
final String certPath = File.createTempFile("ssl_test_", ".crt").getAbsolutePath();
new File(certPath).deleteOnExit();
final String keyPath = File.createTempFile("ssl_test_", ".pem").getAbsolutePath();
new File(keyPath).deleteOnExit();
ClassLoader cl = AbstractSocketSslEchoTest.class.getClassLoader();
FileOutputStream certOut = new FileOutputStream(certPath);
InputStream certIn = cl.getResourceAsStream("openssl.crt");
for (;;) {
int b = certIn.read();
if (b < 0) {
break;
}
certOut.write(b);
}
certOut.close();
FileOutputStream keyOut = new FileOutputStream(keyPath);
InputStream keyIn = cl.getResourceAsStream("openssl.pem");
for (;;) {
int b = keyIn.read();
if (b < 0) {
break;
}
keyOut.write(b);
}
keyOut.close();
final OpenSslContextBuilder ctxBuilder = new OpenSslContextBuilder();
final OpenSslServerContext ctx = ctxBuilder.certPath(certPath).keyPath(keyPath).newServerContext();
serverEngines.add(new SSLEngineFactory("TCNative") {
@Override
public SSLEngine newEngine() throws Exception {
return ctx.newEngine();
}
});
// TODO: Client mode is not supported yet.
/*
clientEngines.add(new SSLEngineFactory("TCNative") {
public SSLEngine newEngine() {
return new OpenSSLEngine(contextHolder, sslBufferPool);
}
});
*/
} else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
}
for (SSLEngineFactory sf: serverEngines) {
for (SSLEngineFactory cf: clientEngines) {
params.add(new SSLEngineFactory[] { sf, cf });
}
}
return params;
}
protected abstract static class SSLEngineFactory {
private final String name;
SSLEngineFactory(String name) {
this.name = name;
}
public abstract SSLEngine newEngine() throws Exception;
@Override
public String toString() {
return name;
}
}
private final SSLEngineFactory serverEngineFactory;
private final SSLEngineFactory clientEngineFactory;
protected AbstractSocketSslEchoTest(SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
this.serverEngineFactory = serverEngineFactory;
this.clientEngineFactory = clientEngineFactory;
}
protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor);
protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor);
protected boolean isExecutorRequired() {
return false;
public SocketSslEchoTest(
SslContext serverCtx, SslContext clientCtx,
ChannelFactory serverChannelFactory, ChannelFactory clientChannelFactory) {
super(serverCtx, clientCtx, serverChannelFactory, clientChannelFactory);
}
@Test
@ -203,44 +82,34 @@ public abstract class AbstractSocketSslEchoTest {
private void testSslEcho(
boolean serverUsesDelegatedTaskExecutor, boolean clientUsesDelegatedTaskExecutor) throws Throwable {
ExecutorService delegatedTaskExecutor = Executors.newCachedThreadPool();
ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(Executors.newCachedThreadPool()));
ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(Executors.newCachedThreadPool()));
ServerBootstrap sb = new ServerBootstrap(serverChannelFactory);
ClientBootstrap cb = new ClientBootstrap(clientChannelFactory);
EchoHandler sh = new EchoHandler(true);
EchoHandler ch = new EchoHandler(false);
SSLEngine sse = serverEngineFactory.newEngine();
SSLEngine cse = clientEngineFactory.newEngine();
sse.setUseClientMode(false);
cse.setUseClientMode(true);
// Workaround for blocking I/O transport write-write dead lock.
sb.setOption("receiveBufferSize", 1048576);
sb.setOption("receiveBufferSize", 1048576);
// Configure the server pipeline.
if (serverUsesDelegatedTaskExecutor) {
sb.getPipeline().addFirst("ssl", new SslHandler(sse, delegatedTaskExecutor));
sb.getPipeline().addFirst(
"ssl", new SslHandler(serverCtx.newEngine(), serverCtx.bufferPool(), delegatedTaskExecutor));
} else {
sb.getPipeline().addFirst("ssl", new SslHandler(sse));
sb.getPipeline().addFirst("ssl", serverCtx.newHandler());
}
sb.getPipeline().addLast("handler", sh);
// Configure the client pipeline.
if (clientUsesDelegatedTaskExecutor) {
cb.getPipeline().addFirst("ssl", new SslHandler(cse, delegatedTaskExecutor));
cb.getPipeline().addFirst(
"ssl", new SslHandler(clientCtx.newEngine(), clientCtx.bufferPool(), delegatedTaskExecutor));
} else {
cb.getPipeline().addFirst("ssl", new SslHandler(cse));
cb.getPipeline().addFirst("ssl", clientCtx.newHandler());
}
cb.getPipeline().addLast("handler", ch);
ExecutorService eventExecutor = null;
if (isExecutorRequired()) {
eventExecutor = new OrderedMemoryAwareThreadPoolExecutor(16, 0, 0);
sb.getPipeline().addFirst("executor", new ExecutionHandler(eventExecutor));
cb.getPipeline().addFirst("executor", new ExecutionHandler(eventExecutor));
}
Channel sc = sb.bind(new InetSocketAddress(0));
int port = ((InetSocketAddress) sc.getLocalAddress()).getPort();
@ -303,15 +172,8 @@ public abstract class AbstractSocketSslEchoTest {
sh.channel.close().awaitUninterruptibly();
ch.channel.close().awaitUninterruptibly();
sc.close().awaitUninterruptibly();
cb.shutdown();
sb.shutdown();
cb.releaseExternalResources();
sb.releaseExternalResources();
delegatedTaskExecutor.shutdown();
if (eventExecutor != null) {
eventExecutor.shutdown();
}
if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) {
throw sh.exception.get();
}

View File

@ -27,70 +27,45 @@ import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.ssl.AbstractSocketSslEchoTest.SSLEngineFactory;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.util.TestUtil;
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.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public abstract class AbstractSocketSslGreetingTest {
static final InternalLogger logger =
InternalLoggerFactory.getInstance(AbstractSocketSslGreetingTest.class);
public class SocketSslGreetingTest extends SslTest {
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}")
public static Collection<SSLEngineFactory[]> engines() throws Exception {
return AbstractSocketSslEchoTest.engines();
}
static final InternalLogger logger = InternalLoggerFactory.getInstance(SocketSslGreetingTest.class);
private final ChannelBuffer greeting = ChannelBuffers.wrappedBuffer(new byte[] {'a'});
protected abstract ChannelFactory newServerSocketChannelFactory(Executor executor);
protected abstract ChannelFactory newClientSocketChannelFactory(Executor executor);
private final SSLEngineFactory serverEngineFactory;
private final SSLEngineFactory clientEngineFactory;
protected AbstractSocketSslGreetingTest(
SSLEngineFactory serverEngineFactory, SSLEngineFactory clientEngineFactory) {
this.serverEngineFactory = serverEngineFactory;
this.clientEngineFactory = clientEngineFactory;
public SocketSslGreetingTest(
SslContext serverCtx, SslContext clientCtx,
ChannelFactory serverChannelFactory, ChannelFactory clientChannelFactory) {
super(serverCtx, clientCtx, serverChannelFactory, clientChannelFactory);
}
@Test
public void testSslEcho() throws Throwable {
ServerBootstrap sb = new ServerBootstrap(newServerSocketChannelFactory(Executors.newCachedThreadPool()));
ClientBootstrap cb = new ClientBootstrap(newClientSocketChannelFactory(Executors.newCachedThreadPool()));
ServerBootstrap sb = new ServerBootstrap(serverChannelFactory);
ClientBootstrap cb = new ClientBootstrap(clientChannelFactory);
ServerHandler sh = new ServerHandler();
ClientHandler ch = new ClientHandler();
SSLEngine sse = serverEngineFactory.newEngine();
SSLEngine cse = clientEngineFactory.newEngine();
sse.setUseClientMode(false);
cse.setUseClientMode(true);
// Workaround for blocking I/O transport write-write dead lock.
sb.setOption("receiveBufferSize", 1048576);
sb.setOption("receiveBufferSize", 1048576);
sb.getPipeline().addFirst("ssl", new SslHandler(sse));
sb.getPipeline().addFirst("ssl", serverCtx.newHandler());
sb.getPipeline().addLast("handler", sh);
cb.getPipeline().addFirst("ssl", new SslHandler(cse));
cb.getPipeline().addFirst("ssl", clientCtx.newHandler());
cb.getPipeline().addLast("handler", ch);
Channel sc = sb.bind(new InetSocketAddress(0));
@ -120,10 +95,6 @@ public abstract class AbstractSocketSslGreetingTest {
sh.channel.close().awaitUninterruptibly();
cc.close().awaitUninterruptibly();
sc.close().awaitUninterruptibly();
cb.shutdown();
sb.shutdown();
cb.releaseExternalResources();
sb.releaseExternalResources();
if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) {
throw sh.exception.get();

View File

@ -19,33 +19,33 @@ import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.util.CharsetUtil;
import org.junit.Assert;
import org.junit.Test;
import javax.net.ssl.SSLEngine;
import java.net.InetSocketAddress;
public class SslCloseTest {
public class SslCloseTest extends SslTest{
public SslCloseTest(
SslContext serverCtx, SslContext clientCtx,
ChannelFactory serverChannelFactory, ChannelFactory clientChannelFactory) {
super(serverCtx, clientCtx, serverChannelFactory, clientChannelFactory);
}
/**
* Try to write a testcase to reproduce #343
*/
@Test
public void testCloseOnSslException() {
ServerBootstrap sb = new ServerBootstrap(new NioServerSocketChannelFactory());
ClientBootstrap cb = new ClientBootstrap(new NioClientSocketChannelFactory());
ServerBootstrap sb = new ServerBootstrap(serverChannelFactory);
ClientBootstrap cb = new ClientBootstrap(clientChannelFactory);
SSLEngine sse = SecureChatSslContextFactory.getServerContext().createSSLEngine();
sse.setUseClientMode(false);
sb.getPipeline().addFirst("ssl", new SslHandler(sse));
sb.getPipeline().addFirst("ssl", serverCtx.newHandler());
sb.getPipeline().addLast("handler", new SimpleChannelUpstreamHandler() {
@Override
@ -64,8 +64,5 @@ public class SslCloseTest {
Assert.assertTrue(cc.getCloseFuture().awaitUninterruptibly(5000));
serverChannel.close();
cb.releaseExternalResources();
sb.releaseExternalResources();
}
}

View File

@ -1,45 +0,0 @@
/*
* 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 org.jboss.netty.handler.ssl;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.jboss.netty.handler.codec.embedder.CodecEmbedderException;
import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.util.Random;
public class SslHandlerTest {
private final Random random = new Random();
@Test
@Ignore
public void testDetectNonSslRecord() {
byte[] data = new byte[1024];
random.nextBytes(data);
DecoderEmbedder<ChannelBuffer> em = new DecoderEmbedder<ChannelBuffer>(new SslHandler(SecureChatSslContextFactory.getServerContext().createSSLEngine()));
try {
em.offer(ChannelBuffers.wrappedBuffer(data));
Assert.fail();
} catch (CodecEmbedderException e) {
Assert.assertTrue(e.getCause() instanceof NotSslRecordException);
}
}
}

View File

@ -19,6 +19,7 @@ import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
@ -29,20 +30,15 @@ import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.ClientSocketChannelFactory;
import org.jboss.netty.channel.socket.ServerSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import org.jboss.netty.example.securechat.SecureChatSslContextFactory;
import org.junit.Test;
import javax.net.ssl.SSLEngine;
import java.net.InetSocketAddress;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
public class SslHandshakeRaceTester {
public class SslHandshakeRaceTest extends SslTest {
private static final int COUNT = 10;
private static final Random random = new Random();
static final byte[] data = new byte[1048576];
@ -52,35 +48,25 @@ public class SslHandshakeRaceTester {
random.nextBytes(data);
}
public void run(int rounds, boolean nio) throws Throwable {
ClientSocketChannelFactory clientFactory;
if (nio) {
clientFactory = new NioClientSocketChannelFactory();
} else {
clientFactory = new OioClientSocketChannelFactory();
}
ClientBootstrap cb = new ClientBootstrap(clientFactory);
public SslHandshakeRaceTest(
SslContext serverCtx, SslContext clientCtx,
ChannelFactory serverChannelFactory, ChannelFactory clientChannelFactory) {
super(serverCtx, clientCtx, serverChannelFactory, clientChannelFactory);
}
@Test
public void testHandshakeRace() throws Throwable {
ClientBootstrap cb = new ClientBootstrap(clientChannelFactory);
cb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline cp = Channels.pipeline();
SSLEngine cse = SecureChatSslContextFactory.getClientContext().createSSLEngine();
cse.setUseClientMode(true);
cp.addFirst("ssl", new SslHandler(cse));
cp.addFirst("ssl", clientCtx.newHandler());
cp.addLast("handler", new TestHandler());
return cp;
}
});
ServerSocketChannelFactory serverFactory;
if (nio) {
serverFactory = new NioServerSocketChannelFactory();
} else {
serverFactory = new OioServerSocketChannelFactory();
}
ServerBootstrap sb = new ServerBootstrap(serverFactory);
ServerBootstrap sb = new ServerBootstrap(serverChannelFactory);
sb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline cp = Channels.pipeline();
@ -98,14 +84,13 @@ public class SslHandshakeRaceTester {
});
++count;
System.out.println("Connection #" + count);
if (count % 100 == 0) {
System.out.println("Connection #" + count);
}
}
});
SSLEngine sse = SecureChatSslContextFactory.getServerContext().createSSLEngine();
sse.setUseClientMode(false);
cp.addFirst("ssl", new SslHandler(sse));
cp.addFirst("ssl", serverCtx.newHandler());
cp.addLast("handler", new TestHandler());
return cp;
}
@ -114,22 +99,17 @@ public class SslHandshakeRaceTester {
Channel sc = sb.bind(new InetSocketAddress(0));
int port = ((InetSocketAddress) sc.getLocalAddress()).getPort();
for (int i = 0; i < rounds; i++) {
for (int i = 0; i < COUNT; i++) {
connectAndSend(cb, port);
}
cb.shutdown();
cb.releaseExternalResources();
sc.close().awaitUninterruptibly();
sb.shutdown();
sb.releaseExternalResources();
}
private static void connectAndSend(ClientBootstrap cb, int port) throws Throwable {
ChannelFuture ccf = cb.connect(new InetSocketAddress("127.0.0.1", port));
ccf.awaitUninterruptibly();
if (!ccf.isSuccess()) {
ccf.getCause().printStackTrace();
throw ccf.getCause();
}
TestHandler ch = ccf.getChannel().getPipeline().get(TestHandler.class);
@ -138,7 +118,6 @@ public class SslHandshakeRaceTester {
ChannelFuture hf = cc.getPipeline().get(SslHandler.class).handshake();
hf.awaitUninterruptibly();
if (!hf.isSuccess()) {
hf.getCause().printStackTrace();
ch.channel.close();
throw hf.getCause();
}
@ -183,15 +162,4 @@ public class SslHandshakeRaceTester {
e.getChannel().close();
}
}
public static void main(String[] args) throws Throwable {
int count = 20000;
boolean nio = false;
if (args.length == 2) {
count = Integer.parseInt(args[0]);
nio = Boolean.parseBoolean(args[1]);
}
SslHandshakeRaceTester test = new SslHandshakeRaceTester();
test.run(count, nio);
}
}

View File

@ -0,0 +1,120 @@
/*
* 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 org.jboss.netty.handler.ssl;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory;
import org.jboss.netty.handler.ssl.util.SelfSignedCertificate;
import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.io.File;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RunWith(Parameterized.class)
public abstract class SslTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslTest.class);
private static final ExecutorService executor = Executors.newCachedThreadPool();
private static final File CERT_FILE;
private static final File KEY_FILE;
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}: serverCtx = {0}, clientCtx = {1}, serverChannelFactory = {2}, clientChannelFactory = {3}")
public static Collection<Object[]> params() throws Exception {
// Populate the permutations.
List<Object[]> params = new ArrayList<Object[]>();
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));
List<ChannelFactory> serverChannelFactories = new ArrayList<ChannelFactory>();
serverChannelFactories.add(new NioServerSocketChannelFactory(executor, executor));
serverChannelFactories.add(new OioServerSocketChannelFactory(executor, executor));
List<ChannelFactory> clientChannelFactories = new ArrayList<ChannelFactory>();
clientChannelFactories.add(new NioClientSocketChannelFactory(executor, executor));
clientChannelFactories.add(new OioClientSocketChannelFactory(executor));
boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(null, CERT_FILE, KEY_FILE, null, null, null, 0, 0));
// TODO: Client mode is not supported yet.
// clientContexts.add(new OpenSslContext(null, CERT_FILE, null, null, null, 0, 0));
} else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
}
for (SslContext sctx: serverContexts) {
for (SslContext cctx: clientContexts) {
for (ChannelFactory scf: serverChannelFactories) {
for (ChannelFactory ccf: clientChannelFactories) {
if (scf instanceof OioServerSocketChannelFactory &&
ccf instanceof OioClientSocketChannelFactory) {
continue;
}
params.add(new Object[] { sctx, cctx, scf, ccf });
}
}
}
}
return params;
}
protected final SslContext serverCtx;
protected final SslContext clientCtx;
protected final ChannelFactory serverChannelFactory;
protected final ChannelFactory clientChannelFactory;
protected SslTest(
SslContext serverCtx, SslContext clientCtx,
ChannelFactory serverChannelFactory, ChannelFactory clientChannelFactory) {
this.serverCtx = serverCtx;
this.clientCtx = clientCtx;
this.serverChannelFactory = serverChannelFactory;
this.clientChannelFactory = clientChannelFactory;
}
}

View File

@ -1,30 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFGzCCAwOgAwIBAgIJAKuufohIAUhRMA0GCSqGSIb3DQEBBQUAMCMxCzAJBgNV
BAYTAktSMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAgFw0xNDA0MjExMzMxMzJaGA8y
Mjg4MDIwNDEzMzEzMlowIzELMAkGA1UEBhMCS1IxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxtiOHUgcCtSvEvH4
cPgOuBEIy6EPoHlOfUm1S0m7vOy8ESynBuOAn5Be1QP8lB/F69DQoRD41OBbdrA1
pcpIQmCORheKUwv3EZSt3kzVJonqE4HW1FKyQA8Ej5RqsfHtDaCfO8uzAmuJXMg2
2Gj/6fO/BKuPdkrAwywIqAxW2bKSVwKSId1sRilRku+rK9a1d0xiWAGr8YC8KZrv
5101I7A9OcTAJtoI46OB5pcltjLhXKSyrN3wyoxsKVQkGHzB5Uvnua/E9CnOPmW8
SDuQ/1w+/haUftKsFAPdjy5auDEIctti89YcOJaJfMzZP1hxrSAuDaMHKEdjySpx
DXxp3Nvab0uiwYEPCHKE72JjY7/3oaZ6AzNxpjSwyHLCmuFFg4BsL9V1zAuz7E6F
zxrm+CBq3J5U0NNBW9/zBTAqPTjBJSX7KZTvwlWMTs/kKs4pfeJX/Z97JYwUk7Kk
6uVfaoHIIezfksJX8/dzzXn0p11+3OkPqcHw/7dDtxlX5rtontCrhLbq0LhvAsrh
7dEOBnNFXxYzF7yNM7spBeRvqxcNPKNCclQiINeYbO8JCRBw+jmtevUDxqLiRJsu
7Q1SxUlnGD0MQZUvy4p5S+L/amE74qJDgSaCftWW7KKnAB0Z2ZaNHV3aZ+Sn6ExF
CSrTFEJzaXT7Cvbo6pa6XQ2sij0CAwEAAaNQME4wHQYDVR0OBBYEFHWMy8+VATLk
sDQGt9fMc6MssoS2MB8GA1UdIwQYMBaAFHWMy8+VATLksDQGt9fMc6MssoS2MAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggIBAFoGovC4fNFJjiSIo8BDNjOx
XEBdhUpzm+9i6h0PjBriwd2SyQHmldVXsshJmcmnNaC7cMrQG4cSk4DzzHAZIFl9
xQmhktlJTqSzKr+bda27URQqw3rPIntvcQe3SchOG73wOLxtoar3z8G/o3m6kPyB
5l2ZjrI658iN11tjuc/wqmm5VCIOanWmJUDoryh/Ndg4nLcxsx36/iD+V/NV//xO
jTL1HdnLtK895hrW5Q6/1qN3KlerVoI4WOYvfcpNg380vfcxYuhZ+db5WHRJXG+I
V/nnDMmnFHgQbVqat7TZFUoEyac7BqScs5x25uXx6liTGZ9I7kpPrx3mE1fhatyp
poc+gSm1Va6tNo/UI+oo8NRbKWHJE2TSsMAuHDfyKzDi0yMdTPuVzpbLVjCgXVdv
cOUD05AX3imqlucwqhtFciwhvRgB82CZhBONrMr2zwWGedWMv50m+r80AFFvaTht
Xs+LWAtT29RuhhtsRz1yZFxvCiYHclpONGqy09Z1qYPQvqv51tz/SunbCjizhJm9
/qNvQ/oqUPsPz30TV6GE9G6q6exS/S7zZdlwI/fNdnOTRbd0r0bvb8hLXc9Ooup8
7F9Py5ZIvjgpmFj6aZHG2inzw5KVT/sR8kznipuTR5uqQNBlfUypjpY2BX+5sJ7d
UL8VfLsSGIwQN3CFZG8k
-----END CERTIFICATE-----

View File

@ -1,52 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDG2I4dSBwK1K8S
8fhw+A64EQjLoQ+geU59SbVLSbu87LwRLKcG44CfkF7VA/yUH8Xr0NChEPjU4Ft2
sDWlykhCYI5GF4pTC/cRlK3eTNUmieoTgdbUUrJADwSPlGqx8e0NoJ87y7MCa4lc
yDbYaP/p878Eq492SsDDLAioDFbZspJXApIh3WxGKVGS76sr1rV3TGJYAavxgLwp
mu/nXTUjsD05xMAm2gjjo4HmlyW2MuFcpLKs3fDKjGwpVCQYfMHlS+e5r8T0Kc4+
ZbxIO5D/XD7+FpR+0qwUA92PLlq4MQhy22Lz1hw4lol8zNk/WHGtIC4NowcoR2PJ
KnENfGnc29pvS6LBgQ8IcoTvYmNjv/ehpnoDM3GmNLDIcsKa4UWDgGwv1XXMC7Ps
ToXPGub4IGrcnlTQ00Fb3/MFMCo9OMElJfsplO/CVYxOz+Qqzil94lf9n3sljBST
sqTq5V9qgcgh7N+Swlfz93PNefSnXX7c6Q+pwfD/t0O3GVfmu2ie0KuEturQuG8C
yuHt0Q4Gc0VfFjMXvI0zuykF5G+rFw08o0JyVCIg15hs7wkJEHD6Oa169QPGouJE
my7tDVLFSWcYPQxBlS/LinlL4v9qYTviokOBJoJ+1ZbsoqcAHRnZlo0dXdpn5Kfo
TEUJKtMUQnNpdPsK9ujqlrpdDayKPQIDAQABAoICAQC/zPzv07Fw5Qvmm04IMc2I
0K2KNVYsdTY3dZSRBZM4PaV+b3LBG0rjHs/KaukEO82elDHZWtSaCbtPtdJZk8+1
bwttIqHDT0RHSgGX7safQOJvZItDDG1xisrcb82mzPPadDeD5w1JZU7/FwSIJGfN
U9bJ+24LLTnYSK4k4poXrL6pfQpV7g3Vc1+C+vlB9P3fD+fAegRPk9xryU5k/iwW
u5WjFlw+XYu7f+j58otmvpdQ1HCgfAgaZ+6gws96a0RgF6JyItA4r+aHm3xMtGA9
YM5GKqOb3TwspjndNVo+VtWObH17M+jO0K3XmoRnLLmhw+uILdvmh04CKZUY7Avz
UJybZkchnODvC0htfpHB5+KYGu8S1RqnaV9UjQPBcw93Zvlz0+Z3lpd/kn3vzi/9
03qiDId2bCsKamxFRMvdSzua3ZWfwT3y4/UApEASDc4lqbNU6Cck0vyJFmy06fEv
11ybp9bxovAE4vewHYSCrEZoLaFMafvhgSzv5IBU7+dLkGJ2EYyoQ9govJVrRoaY
OoSjUpUFhKfGAP5ExgV1oPKMySlGUyZa+SsYiZO7BEklQyLHYed5h+b5Mc0/O6m5
yw6NXgLgPBswE1i7XNMSVstgyfF0+whh92FUSuFdcnC0l020qWkBg0e5t5CTV0Wt
BtO7eEH5XBLgcPJKtejApQKCAQEA7iXmGVn4OgYOXtloYFTsYMrf3JwX4u8lk3i8
3giyhm5857aiHmToe+3PjH3xk9N7qmuBB39Oc6lH52wvpXMOQUMdZxgQpNTjmmlk
a4K2QGrHaef30nU8ZAIhSIz/Wz9XzMUyj7sgbNbuLtTa8xsasDEE+u4YO1dfSWp1
dZuwgCH+dSnHiK/u7xaIEDoZrgK4IKYNfkVILM+Z6RaVmOoWM5nwbYaVjyfW5BLP
ER4LhlWSAsnnXSLx8WmV0sLnXaDQ+1ELx456bJr7xV/TYyDZ/eobCRuhFVRh4Wn8
6mLZMklV621dogZDC+j0dXpXszi3B9qfD+ZpzFH4XPZvXfY1GwKCAQEA1cBxITNe
MzfCTMGfj6sgPIciMFfU2rLk2dajiAZy15tLl9Ggb3AwF5I4HAU9bWsQd2eossZR
s7AKppAHNO5KgQ9gap+kAhthOEvFMutODMc/YeOYAh00XtzXVA9cm5ll58W0M0tw
YOSf/sdkH45sXIh17IeSSCxYJGj9upc0RDmHIiKOxn2c/A7VcmWLUN/0iHBgJahw
g1CnZ3WSTpk+geB6WVabQZVVh/yW0Ew8nwPNqmfbYhIiQlaZrlQA40evXkuqI4RS
PIlPTuDSLmeJEAoCUCrjdnomVXSe9+64BIyvriTU2SVGfoW13Jb3l4jaKMNX5gU0
tj/svTLSb3ErhwKCAQBK3Bz1kSOHUcoIpLy7s1ZOotc16NdVXalpLwXnocJEocyf
pfhWJ5AxDLM2TYy31D3Gd16q9ai6kNbqd0aO8pjjiO5gLt9kgQs+yaxoY0FZBHfE
4cc0H+go48aEoNXQYBwAYWigZ8ksjW93xy9ARh/gjLgtDNUPKkrosA9WNeeIj6cG
O2jenbc07tc8ipbx7SbmFGyfabXjaCrQa2oBFGyAMf6y3yB6TKKvNP5kOhaTVqi8
Oa2ByG9XXMJc6ymBdADdTz+trirrothL1fCD85qyx3lSJp4/LzRJgGChaQsvVIAu
rkVFnkRLJSJbH2ccDlLypBVmER7mvEmfJ1QgrDlLAoIBABLFKmoK87u1qsdIvXVF
Rrhp+XObUuK9kOEcOxIH1u/eAD5dtyPyCxcWzPyQ7Tr9it2haawBRE1uKRFFVoys
XDiHHCVgzWst8lSaOr/k796pZIR5EvEklWO7KuGD09MKHmvC7bcPadfoW7Bo2/71
Z8icZxaVNSBHXJod6iIHgxx2nMo/lKATicSMnOvFxP0eHYAZtie4aeDuJTUtmuJy
DI4lIzlTJKEWtwMRCEFUYIJqOBY9toMfKoj+x9IKKGXM9rZmGJOz2/Da8e7wSSSw
DNYuutNFYOIXieQ5MHh/KwOjQw4cx+AlqNIVdcngK+/PobCwrrhRPra+WFCo9Ne6
bWMCggEBAJT6tQ9J5lFbrPKa9MDhK7vYP9VWiKqGImhM/P3dx/EGo5RtChmLSUMM
miQg+r6nK6OQ/RhBEUb0lNFVCc4vTIh9rWkwt8M42MJnyE6orHltcKqPWcY3/Ml1
FII+ueMLoqdJ97SFi7F06Awaga7T/kmR2btqr6AVF1ZRbaoDkdpfNhJibMCX5Sq1
/t8s+OiHM5vlu8kag0JWoZ47RZYBY+DrElSckHj5r4RxG1htbvbm/7ZCpwVwU4cy
PF07MBqOAI+arljgedPO3DeKb7yKC/XXrq/OsXjsPo257kONg2jnOdfw8/mU2nPS
bduIWhdShKYyRjkDQQXwrb5KQtHeOqU=
-----END PRIVATE KEY-----