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:
parent
4cff4b99fd
commit
cb4020d4be
25
NOTICE.txt
25
NOTICE.txt
@ -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:
|
||||
|
||||
@ -97,12 +114,4 @@ framework implementation, which can be obtained at:
|
||||
* license/LICENSE.felix.txt (Apache License 2.0)
|
||||
* 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
|
||||
|
||||
|
23
license/LICENSE.bouncycastle.txt
Normal file
23
license/LICENSE.bouncycastle.txt
Normal 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
25
pom.xml
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
@ -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.");
|
||||
|
@ -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";
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
169
src/main/java/org/jboss/netty/handler/ssl/JdkSslContext.java
Normal file
169
src/main/java/org/jboss/netty/handler/ssl/JdkSslContext.java
Normal 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()]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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() +
|
||||
']';
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 +
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
137
src/main/java/org/jboss/netty/handler/ssl/PemReader.java
Normal file
137
src/main/java/org/jboss/netty/handler/ssl/PemReader.java
Normal 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() { }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
206
src/main/java/org/jboss/netty/handler/ssl/SslContext.java
Normal file
206
src/main/java/org/jboss/netty/handler/ssl/SslContext.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
25
src/main/java/org/jboss/netty/handler/ssl/SslProvider.java
Normal file
25
src/main/java/org/jboss/netty/handler/ssl/SslProvider.java
Normal 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
|
||||
}
|
@ -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() { }
|
||||
}
|
@ -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 };
|
||||
}
|
||||
}
|
@ -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 };
|
||||
}
|
||||
}
|
@ -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() { }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
@ -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() { }
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
120
src/test/java/org/jboss/netty/handler/ssl/SslTest.java
Normal file
120
src/test/java/org/jboss/netty/handler/ssl/SslTest.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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-----
|
@ -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-----
|
Loading…
Reference in New Issue
Block a user