diff --git a/common/src/main/java/io/netty/util/internal/EmptyArrays.java b/common/src/main/java/io/netty/util/internal/EmptyArrays.java
index 52c9f24bee..f838f6476d 100644
--- a/common/src/main/java/io/netty/util/internal/EmptyArrays.java
+++ b/common/src/main/java/io/netty/util/internal/EmptyArrays.java
@@ -17,6 +17,7 @@
package io.netty.util.internal;
import java.nio.ByteBuffer;
+import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
public final class EmptyArrays {
@@ -34,7 +35,10 @@ 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 Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
public static final X509Certificate[] EMPTY_X509_CERTIFICATES = new X509Certificate[0];
+ public static final javax.security.cert.X509Certificate[] EMPTY_JAVAX_X509_CERTIFICATES =
+ new javax.security.cert.X509Certificate[0];
private EmptyArrays() { }
}
diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/netty/util/internal/StringUtil.java
index 2c94f2e3fa..ca69b8f707 100644
--- a/common/src/main/java/io/netty/util/internal/StringUtil.java
+++ b/common/src/main/java/io/netty/util/internal/StringUtil.java
@@ -33,9 +33,9 @@ public final class StringUtil {
public static final char COMMA = ',';
public static final char LINE_FEED = '\n';
public static final char CARRIAGE_RETURN = '\r';
+ public static final String EMPTY_STRING = "";
private static final String[] BYTE2HEX_PAD = new String[256];
private static final String[] BYTE2HEX_NOPAD = new String[256];
- private static final String EMPTY_STRING = "";
/**
* 2 - Quote character at beginning and end.
* 5 - Extra allowance for anticipated escape characters that may be added.
diff --git a/example/src/main/java/io/netty/example/http2/client/Http2Client.java b/example/src/main/java/io/netty/example/http2/client/Http2Client.java
index 65dfb31d8e..18f1402b38 100644
--- a/example/src/main/java/io/netty/example/http2/client/Http2Client.java
+++ b/example/src/main/java/io/netty/example/http2/client/Http2Client.java
@@ -31,6 +31,7 @@ import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
@@ -61,7 +62,8 @@ public final class Http2Client {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
- sslCtx = SslContext.newClientContext(SslProvider.JDK,
+ SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK;
+ sslCtx = SslContext.newClientContext(provider,
null, InsecureTrustManagerFactory.INSTANCE,
Http2SecurityUtil.CIPHERS,
/* NOTE: the following filter may not include all ciphers required by the HTTP/2 specification
@@ -69,8 +71,8 @@ public final class Http2Client {
SupportedCipherSuiteFilter.INSTANCE,
new ApplicationProtocolConfig(
Protocol.ALPN,
- SelectorFailureBehavior.FATAL_ALERT,
- SelectedListenerFailureBehavior.FATAL_ALERT,
+ SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
+ SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
SelectedProtocol.HTTP_2.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);
diff --git a/example/src/main/java/io/netty/example/http2/server/Http2Server.java b/example/src/main/java/io/netty/example/http2/server/Http2Server.java
index 85cef929c6..c98aec7f22 100644
--- a/example/src/main/java/io/netty/example/http2/server/Http2Server.java
+++ b/example/src/main/java/io/netty/example/http2/server/Http2Server.java
@@ -30,6 +30,7 @@ import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
@@ -42,14 +43,16 @@ import io.netty.handler.ssl.util.SelfSignedCertificate;
public final class Http2Server {
static final boolean SSL = System.getProperty("ssl") != null;
+
static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
+ SslProvider provider = OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK;
SelfSignedCertificate ssc = new SelfSignedCertificate();
- sslCtx = SslContext.newServerContext(SslProvider.JDK,
+ sslCtx = SslContext.newServerContext(provider,
ssc.certificate(), ssc.privateKey(), null,
Http2SecurityUtil.CIPHERS,
/* NOTE: the following filter may not include all ciphers required by the HTTP/2 specification
@@ -57,8 +60,8 @@ public final class Http2Server {
SupportedCipherSuiteFilter.INSTANCE,
new ApplicationProtocolConfig(
Protocol.ALPN,
- SelectorFailureBehavior.FATAL_ALERT,
- SelectedListenerFailureBehavior.FATAL_ALERT,
+ SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
+ SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
SelectedProtocol.HTTP_2.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);
diff --git a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java
index 1ce0da16d8..4dc6ae4fdb 100644
--- a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java
+++ b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java
@@ -53,15 +53,15 @@ public final class MemcacheClient {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
- sslCtx = SslContext.newClientContext(SslProvider.JDK,
+ sslCtx = SslContext.newClientContext(null,
null, InsecureTrustManagerFactory.INSTANCE, Http2SecurityUtil.CIPHERS,
/* NOTE: the following filter may not include all ciphers required by the HTTP/2 specification
* Please refer to the HTTP/2 specification for cipher requirements. */
SupportedCipherSuiteFilter.INSTANCE,
new ApplicationProtocolConfig(
Protocol.ALPN,
- SelectorFailureBehavior.FATAL_ALERT,
- SelectedListenerFailureBehavior.FATAL_ALERT,
+ SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
+ SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
SelectedProtocol.HTTP_2.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);
diff --git a/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java b/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java
index c33d4ebfce..74bbb2dabb 100644
--- a/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java
+++ b/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java
@@ -61,8 +61,8 @@ public final class SpdyClient {
null, InsecureTrustManagerFactory.INSTANCE, null, IdentityCipherSuiteFilter.INSTANCE,
new ApplicationProtocolConfig(
Protocol.NPN,
- SelectorFailureBehavior.FATAL_ALERT,
- SelectedListenerFailureBehavior.FATAL_ALERT,
+ SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
+ SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
SelectedProtocol.SPDY_3_1.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);
diff --git a/example/src/main/java/io/netty/example/spdy/server/SpdyServer.java b/example/src/main/java/io/netty/example/spdy/server/SpdyServer.java
index 40ce2a9cc3..ff1a6b7d1e 100644
--- a/example/src/main/java/io/netty/example/spdy/server/SpdyServer.java
+++ b/example/src/main/java/io/netty/example/spdy/server/SpdyServer.java
@@ -61,8 +61,8 @@ public final class SpdyServer {
ssc.certificate(), ssc.privateKey(), null, null, IdentityCipherSuiteFilter.INSTANCE,
new ApplicationProtocolConfig(
Protocol.NPN,
- SelectorFailureBehavior.FATAL_ALERT,
- SelectedListenerFailureBehavior.FATAL_ALERT,
+ SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
+ SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
SelectedProtocol.SPDY_3_1.protocolName(),
SelectedProtocol.HTTP_1_1.protocolName()),
0, 0);
diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java
index d5562bada2..e6d8e81857 100644
--- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java
+++ b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java
@@ -79,6 +79,9 @@ public final class ApplicationProtocolConfig {
if (protocol == Protocol.NONE) {
throw new IllegalArgumentException("protocol (" + Protocol.NONE + ") must not be " + Protocol.NONE + '.');
}
+ if (supportedProtocols.isEmpty()) {
+ throw new IllegalArgumentException("supportedProtocols must be not empty");
+ }
}
/**
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java
index 3b23509496..49e2da42ed 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java
@@ -113,6 +113,14 @@ public final class OpenSsl {
return UNAVAILABILITY_CAUSE == null;
}
+ /**
+ * Returns {@code true} if the used version of openssl supports
+ * ALPN.
+ */
+ public static boolean isAlpnSupported() {
+ return isAvailable() && SSL.version() >= 0x10002000L;
+ }
+
/**
* Ensure that {@code netty-tcnative} and
* its OpenSSL support are available.
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java
index 7cd2e1126e..aa5b419dd3 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java
@@ -19,10 +19,19 @@ package io.netty.handler.ssl;
* OpenSSL version of {@link ApplicationProtocolNegotiator}.
*/
public interface OpenSslApplicationProtocolNegotiator extends ApplicationProtocolNegotiator {
- // The current need for this interface is primarily for consistency with the JDK provider
- // How the OpenSsl provider will provide extensibility to control the application selection
- // and notification algorithms is not yet known (JNI, pure java, tcnative hooks, etc...).
- // OpenSSL itself is currently not in compliance with the specification for the 2 supported
- // protocols (ALPN, NPN) with respect to allowing the handshakes to fail during the application
- // protocol negotiation process. Issue https://github.com/openssl/openssl/issues/188 has been created for this.
+
+ /**
+ * Returns the {@link ApplicationProtocolConfig.Protocol} which should be used.
+ */
+ ApplicationProtocolConfig.Protocol protocol();
+
+ /**
+ * Get the desired behavior for the peer who selects the application protocol.
+ */
+ ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior();
+
+ /**
+ * Get the desired behavior for the peer who is notified of the selected protocol.
+ */
+ ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior();
}
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java
index e3582222a4..d88e289b3e 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java
@@ -45,7 +45,7 @@ public final class OpenSslClientContext extends OpenSslContext {
* Creates a new instance.
*/
public OpenSslClientContext() throws SSLException {
- this(null, null, null, null, 0, 0);
+ this(null, null, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0);
}
/**
@@ -79,10 +79,13 @@ public final class OpenSslClientContext extends OpenSslContext {
* {@code null} to use the default.
*/
public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
- this(certChainFile, trustManagerFactory, null, null, 0, 0);
+ this(certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0);
}
/**
+ * @deprecated use {@link #OpenSslClientContext(File, TrustManagerFactory, Iterable,
+ * CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
+ *
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
@@ -97,10 +100,35 @@ public final class OpenSslClientContext extends OpenSslContext {
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*/
+ @Deprecated
public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers,
ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout)
throws SSLException {
- super(ciphers, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT);
+ this(certChainFile, trustManagerFactory, ciphers, IdentityCipherSuiteFilter.INSTANCE,
+ apn, sessionCacheSize, sessionTimeout);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param certChainFile an X.509 certificate chain file in PEM format
+ * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
+ * that verifies the certificates sent from servers.
+ * {@code null} to use the default..
+ * @param ciphers the cipher suites to enable, in the order of preference.
+ * {@code null} to use the default cipher suites.
+ * @param cipherFilter a filter to apply over the supplied list of ciphers
+ * @param apn Provides a means to configure parameters related to application protocol negotiation.
+ * @param sessionCacheSize the size of the cache used for storing SSL session objects.
+ * {@code 0} to use the default value.
+ * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
+ * {@code 0} to use the default value.
+ */
+ public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers,
+ CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
+ long sessionCacheSize, long sessionTimeout)
+ throws SSLException {
+ super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT);
boolean success = false;
try {
if (certChainFile != null && !certChainFile.isFile()) {
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java
index 4080b01f8e..06f9d510cc 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java
@@ -31,12 +31,15 @@ import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
+import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
+import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
public abstract class OpenSslContext extends SslContext {
@@ -48,10 +51,9 @@ public abstract class OpenSslContext extends SslContext {
protected static final int VERIFY_DEPTH = 10;
private final long aprPool;
- @SuppressWarnings("unused")
+ @SuppressWarnings({ "unused", "FieldMayBeFinal" })
private volatile int aprPoolDestroyed;
- private final List ciphers = new ArrayList();
- private final List unmodifiableCiphers = Collections.unmodifiableList(ciphers);
+ private final List unmodifiableCiphers;
private final long sessionCacheSize;
private final long sessionTimeout;
private final OpenSslApplicationProtocolNegotiator apn;
@@ -59,6 +61,29 @@ public abstract class OpenSslContext extends SslContext {
protected final long ctx;
private final int mode;
+ static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
+ new OpenSslApplicationProtocolNegotiator() {
+ @Override
+ public ApplicationProtocolConfig.Protocol protocol() {
+ return ApplicationProtocolConfig.Protocol.NONE;
+ }
+
+ @Override
+ public List protocols() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public SelectorFailureBehavior selectorFailureBehavior() {
+ return SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
+ }
+
+ @Override
+ public SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
+ return SelectedListenerFailureBehavior.ACCEPT;
+ }
+ };
+
static {
List ciphers = new ArrayList();
// XXX: Make sure to sync this list with JdkSslEngineFactory.
@@ -86,12 +111,13 @@ public abstract class OpenSslContext extends SslContext {
DESTROY_UPDATER = updater;
}
- OpenSslContext(Iterable ciphers, ApplicationProtocolConfig apnCfg, long sessionCacheSize,
- long sessionTimeout, int mode) throws SSLException {
- this(ciphers, toNegotiator(apnCfg, mode == SSL.SSL_MODE_SERVER), sessionCacheSize, sessionTimeout, mode);
+ OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg,
+ long sessionCacheSize, long sessionTimeout, int mode) throws SSLException {
+ this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode);
}
- OpenSslContext(Iterable ciphers, OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize,
+ OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter,
+ OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize,
long sessionTimeout, int mode) throws SSLException {
OpenSsl.ensureAvailability();
@@ -100,22 +126,26 @@ public abstract class OpenSslContext extends SslContext {
}
this.mode = mode;
+ final List convertedCiphers;
if (ciphers == null) {
- ciphers = DEFAULT_CIPHERS;
+ convertedCiphers = null;
+ } else {
+ convertedCiphers = new ArrayList();
+ for (String c: ciphers) {
+ if (c == null) {
+ break;
+ }
+
+ String converted = CipherSuiteConverter.toOpenSsl(c);
+ if (converted != null) {
+ c = converted;
+ }
+ convertedCiphers.add(c);
+ }
}
- for (String c: ciphers) {
- if (c == null) {
- break;
- }
-
- String converted = CipherSuiteConverter.toOpenSsl(c);
- if (converted != null) {
- c = converted;
- }
-
- this.ciphers.add(c);
- }
+ unmodifiableCiphers = Arrays.asList(checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
+ convertedCiphers, DEFAULT_CIPHERS, OpenSsl.availableCipherSuites()));
this.apn = checkNotNull(apn, "apn");
@@ -142,25 +172,33 @@ public abstract class OpenSslContext extends SslContext {
/* List the ciphers that are permitted to negotiate. */
try {
- SSLContext.setCipherSuite(ctx, CipherSuiteConverter.toOpenSsl(this.ciphers));
+ SSLContext.setCipherSuite(ctx, CipherSuiteConverter.toOpenSsl(unmodifiableCiphers));
} catch (SSLException e) {
throw e;
} catch (Exception e) {
- throw new SSLException("failed to set cipher suite: " + this.ciphers, e);
+ throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
}
List nextProtoList = apn.protocols();
/* Set next protocols for next protocol negotiation extension, if specified */
if (!nextProtoList.isEmpty()) {
- // Convert the protocol list into a comma-separated string.
- StringBuilder nextProtocolBuf = new StringBuilder();
- for (String p: nextProtoList) {
- nextProtocolBuf.append(p);
- nextProtocolBuf.append(',');
- }
- nextProtocolBuf.setLength(nextProtocolBuf.length() - 1);
+ String[] protocols = nextProtoList.toArray(new String[nextProtoList.size()]);
+ int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
- SSLContext.setNextProtos(ctx, nextProtocolBuf.toString());
+ switch (apn.protocol()) {
+ case NPN:
+ SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
+ break;
+ case ALPN:
+ SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
+ break;
+ case NPN_AND_ALPN:
+ SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
+ SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
+ break;
+ default:
+ throw new Error();
+ }
}
/* Set session cache size, if specified */
@@ -193,6 +231,17 @@ public abstract class OpenSslContext extends SslContext {
}
}
+ private static int opensslSelectorFailureBehavior(SelectorFailureBehavior behavior) {
+ switch (behavior) {
+ case NO_ADVERTISE:
+ return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
+ case CHOOSE_MY_LAST_PROTOCOL:
+ return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
+ default:
+ throw new Error();
+ }
+ }
+
@Override
public final List cipherSuites() {
return unmodifiableCiphers;
@@ -228,15 +277,8 @@ public abstract class OpenSslContext extends SslContext {
*/
@Override
public final SSLEngine newEngine(ByteBufAllocator alloc) {
- List protos = applicationProtocolNegotiator().protocols();
OpenSslEngineMap engineMap = engineMap();
- final OpenSslEngine engine;
- if (protos.isEmpty()) {
- engine = new OpenSslEngine(ctx, alloc, null, isClient(), sessionContext(), engineMap);
- } else {
- engine = new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient(),
- sessionContext(), engineMap);
- }
+ final OpenSslEngine engine = new OpenSslEngine(ctx, alloc, isClient(), sessionContext(), apn, engineMap);
engineMap.add(engine);
return engine;
}
@@ -312,35 +354,41 @@ public abstract class OpenSslContext extends SslContext {
* Translate a {@link ApplicationProtocolConfig} object to a
* {@link OpenSslApplicationProtocolNegotiator} object.
* @param config The configuration which defines the translation
- * @param isServer {@code true} if a server {@code false} otherwise.
* @return The results of the translation
*/
- static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config,
- boolean isServer) {
+ static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) {
if (config == null) {
- return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
+ return NONE_PROTOCOL_NEGOTIATOR;
}
switch (config.protocol()) {
- case NONE:
- return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
- case NPN:
- if (isServer) {
- switch (config.selectedListenerFailureBehavior()) {
- case CHOOSE_MY_LAST_PROTOCOL:
- return new OpenSslNpnApplicationProtocolNegotiator(config.supportedProtocols());
- default:
- throw new UnsupportedOperationException(
- new StringBuilder("OpenSSL provider does not support ")
- .append(config.selectedListenerFailureBehavior())
- .append(" behavior").toString());
- }
- } else {
- throw new UnsupportedOperationException("OpenSSL provider does not support client mode");
+ case NONE:
+ return NONE_PROTOCOL_NEGOTIATOR;
+ case ALPN:
+ case NPN:
+ case NPN_AND_ALPN:
+ switch (config.selectedListenerFailureBehavior()) {
+ case CHOOSE_MY_LAST_PROTOCOL:
+ case ACCEPT:
+ switch (config.selectorFailureBehavior()) {
+ case CHOOSE_MY_LAST_PROTOCOL:
+ case NO_ADVERTISE:
+ return new OpenSslDefaultApplicationProtocolNegotiator(
+ config);
+ default:
+ throw new UnsupportedOperationException(
+ new StringBuilder("OpenSSL provider does not support ")
+ .append(config.selectorFailureBehavior())
+ .append(" behavior").toString());
}
default:
- throw new UnsupportedOperationException(new StringBuilder("OpenSSL provider does not support ")
- .append(config.protocol()).append(" protocol").toString());
+ throw new UnsupportedOperationException(
+ new StringBuilder("OpenSSL provider does not support ")
+ .append(config.selectedListenerFailureBehavior())
+ .append(" behavior").toString());
+ }
+ default:
+ throw new Error();
}
}
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java
index 65191d91c0..fd7d44a9ef 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java
@@ -15,21 +15,36 @@
*/
package io.netty.handler.ssl;
-import java.util.Collections;
import java.util.List;
-/**
- * Provides no {@link ApplicationProtocolNegotiator} functionality for OpenSSL.
- */
-final class OpenSslDefaultApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
- static final OpenSslDefaultApplicationProtocolNegotiator INSTANCE =
- new OpenSslDefaultApplicationProtocolNegotiator();
+import static io.netty.util.internal.ObjectUtil.checkNotNull;
- private OpenSslDefaultApplicationProtocolNegotiator() {
+/**
+ * OpenSSL {@link ApplicationProtocolNegotiator} for ALPN and NPN.
+ */
+public final class OpenSslDefaultApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
+ private final ApplicationProtocolConfig config;
+ public OpenSslDefaultApplicationProtocolNegotiator(ApplicationProtocolConfig config) {
+ this.config = checkNotNull(config, "config");
}
@Override
public List protocols() {
- return Collections.emptyList();
+ return config.supportedProtocols();
+ }
+
+ @Override
+ public ApplicationProtocolConfig.Protocol protocol() {
+ return config.protocol();
+ }
+
+ @Override
+ public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
+ return config.selectorFailureBehavior();
+ }
+
+ @Override
+ public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
+ return config.selectedListenerFailureBehavior();
}
}
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
index 8e1066fa7f..6067dd7eb7 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
@@ -19,8 +19,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.EmptyArrays;
-import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
+import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.Buffer;
@@ -49,6 +49,8 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
+import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
import static javax.net.ssl.SSLEngineResult.Status.*;
@@ -60,7 +62,9 @@ public final class OpenSslEngine extends SSLEngine {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslEngine.class);
- private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
+ private static final Certificate[] EMPTY_CERTIFICATES = EmptyArrays.EMPTY_CERTIFICATES;
+ private static final X509Certificate[] EMPTY_X509_CERTIFICATES = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
+
private static final SSLException ENGINE_CLOSED = new SSLException("engine closed");
private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException("renegotiation unsupported");
private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException("encrypted packet oversized");
@@ -145,10 +149,9 @@ public final class OpenSslEngine extends SSLEngine {
private final boolean clientMode;
private final ByteBufAllocator alloc;
- private final String fallbackApplicationProtocol;
private final OpenSslSessionContext sessionContext;
private final OpenSslEngineMap engineMap;
-
+ private final OpenSslApplicationProtocolNegotiator apn;
private final SSLSession session = new OpenSslSession();
/**
@@ -158,8 +161,9 @@ public final class OpenSslEngine extends SSLEngine {
* @param alloc the {@link ByteBufAllocator} that will be used by this engine
*/
@Deprecated
- public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
- this(sslCtx, alloc, fallbackApplicationProtocol, false, null, OpenSslEngineMap.EMPTY);
+ public OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
+ @SuppressWarnings("unused") String fallbackApplicationProtocol) {
+ this(sslCtx, alloc, false, null, OpenSslContext.NONE_PROTOCOL_NEGOTIATOR, OpenSslEngineMap.EMPTY);
}
/**
@@ -170,17 +174,18 @@ public final class OpenSslEngine extends SSLEngine {
* @param clientMode {@code true} if this is used for clients, {@code false} otherwise
* @param sessionContext the {@link OpenSslSessionContext} this {@link SSLEngine} belongs to.
*/
- OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol,
- boolean clientMode, OpenSslSessionContext sessionContext, OpenSslEngineMap engineMap) {
+ OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
+ boolean clientMode, OpenSslSessionContext sessionContext,
+ OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap) {
OpenSsl.ensureAvailability();
if (sslCtx == 0) {
throw new NullPointerException("sslCtx");
}
- this.alloc = ObjectUtil.checkNotNull(alloc, "alloc");
+ this.alloc = checkNotNull(alloc, "alloc");
+ this.apn = checkNotNull(apn, "apn");
ssl = SSL.newSSL(sslCtx, !clientMode);
networkBIO = SSL.makeNetworkBIO(ssl);
- this.fallbackApplicationProtocol = fallbackApplicationProtocol;
this.clientMode = clientMode;
this.sessionContext = sessionContext;
this.engineMap = engineMap;
@@ -396,7 +401,7 @@ public final class OpenSslEngine extends SSLEngine {
// In handshake or close_notify stages, check if call to wrap was made
// without regard to the handshake status.
- SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+ SSLEngineResult.HandshakeStatus handshakeStatus = handshakeStatus0();
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_UNWRAP) {
return new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, 0, 0);
@@ -428,7 +433,7 @@ public final class OpenSslEngine extends SSLEngine {
shutdown();
}
- return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
+ return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), 0, bytesProduced);
}
// There was no pending data in the network BIO -- encrypt any application data
@@ -455,7 +460,7 @@ public final class OpenSslEngine extends SSLEngine {
int capacity = dst.remaining();
if (capacity < pendingNet) {
return new SSLEngineResult(
- BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
+ BUFFER_OVERFLOW, handshakeStatus0(), bytesConsumed, bytesProduced);
}
// Write the pending data from the network BIO into the dst buffer
@@ -465,12 +470,12 @@ public final class OpenSslEngine extends SSLEngine {
throw new SSLException(e);
}
- return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), bytesConsumed, bytesProduced);
}
}
}
- return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), bytesConsumed, bytesProduced);
}
public synchronized SSLEngineResult unwrap(
@@ -520,7 +525,7 @@ public final class OpenSslEngine extends SSLEngine {
// In handshake or close_notify stages, check if call to unwrap was made
// without regard to the handshake status.
- SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+ SSLEngineResult.HandshakeStatus handshakeStatus = handshakeStatus0();
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_WRAP) {
return new SSLEngineResult(getEngineStatus(), NEED_WRAP, 0, 0);
}
@@ -604,7 +609,7 @@ public final class OpenSslEngine extends SSLEngine {
if (pendingApp > 0) {
// Do we have enough room in dsts to write decrypted data?
if (capacity < pendingApp) {
- return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, 0);
+ return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus0(), bytesConsumed, 0);
}
// Write decrypted data to dsts buffers
@@ -647,7 +652,7 @@ public final class OpenSslEngine extends SSLEngine {
closeInbound();
}
- return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+ return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), bytesConsumed, bytesProduced);
}
public SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException {
@@ -723,7 +728,14 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public String[] getEnabledCipherSuites() {
- String[] enabled = SSL.getCiphers(ssl);
+ final String[] enabled;
+ synchronized (this) {
+ if (destroyed == 0) {
+ enabled = SSL.getCiphers(ssl);
+ } else {
+ return EmptyArrays.EMPTY_STRINGS;
+ }
+ }
if (enabled == null) {
return EmptyArrays.EMPTY_STRINGS;
} else {
@@ -739,7 +751,7 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public void setEnabledCipherSuites(String[] cipherSuites) {
- ObjectUtil.checkNotNull(cipherSuites, "cipherSuites");
+ checkNotNull(cipherSuites, "cipherSuites");
final StringBuilder buf = new StringBuilder();
for (String c: cipherSuites) {
@@ -766,10 +778,17 @@ public final class OpenSslEngine extends SSLEngine {
buf.setLength(buf.length() - 1);
final String cipherSuiteSpec = buf.toString();
- try {
- SSL.setCipherSuites(ssl, cipherSuiteSpec);
- } catch (Exception e) {
- throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e);
+
+ synchronized (this) {
+ if (destroyed == 0) {
+ try {
+ SSL.setCipherSuites(ssl, cipherSuiteSpec);
+ } catch (Exception e) {
+ throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e);
+ }
+ } else {
+ throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec);
+ }
}
}
@@ -783,7 +802,15 @@ public final class OpenSslEngine extends SSLEngine {
List enabled = new ArrayList();
// Seems like there is no way to explict disable SSLv2Hello in openssl so it is always enabled
enabled.add(PROTOCOL_SSL_V2_HELLO);
- int opts = SSL.getOptions(ssl);
+
+ int opts;
+ synchronized (this) {
+ if (destroyed == 0) {
+ opts = SSL.getOptions(ssl);
+ } else {
+ return enabled.toArray(new String[1]);
+ }
+ }
if ((opts & SSL.SSL_OP_NO_TLSv1) == 0) {
enabled.add(PROTOCOL_TLS_V1);
}
@@ -799,12 +826,7 @@ public final class OpenSslEngine extends SSLEngine {
if ((opts & SSL.SSL_OP_NO_SSLv3) == 0) {
enabled.add(PROTOCOL_SSL_V3);
}
- int size = enabled.size();
- if (size == 0) {
- return EmptyArrays.EMPTY_STRINGS;
- } else {
- return enabled.toArray(new String[size]);
- }
+ return enabled.toArray(new String[enabled.size()]);
}
@Override
@@ -834,23 +856,29 @@ public final class OpenSslEngine extends SSLEngine {
tlsv1_2 = true;
}
}
- // Enable all and then disable what we not want
- SSL.setOptions(ssl, SSL.SSL_OP_ALL);
+ synchronized (this) {
+ if (destroyed == 0) {
+ // Enable all and then disable what we not want
+ SSL.setOptions(ssl, SSL.SSL_OP_ALL);
- if (!sslv2) {
- SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv2);
- }
- if (!sslv3) {
- SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv3);
- }
- if (!tlsv1) {
- SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1);
- }
- if (!tlsv1_1) {
- SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_1);
- }
- if (!tlsv1_2) {
- SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2);
+ if (!sslv2) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv2);
+ }
+ if (!sslv3) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv3);
+ }
+ if (!tlsv1) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1);
+ }
+ if (!tlsv1_1) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_1);
+ }
+ if (!tlsv1_2) {
+ SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2);
+ }
+ } else {
+ throw new IllegalStateException("failed to enable protocols: " + protocols);
+ }
}
}
@@ -915,7 +943,7 @@ public final class OpenSslEngine extends SSLEngine {
} else {
// if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update
// handshakeFinished directly and so eliminate uncessary calls to SSL.isInInit(...)
- handshakeFinished = true;
+ handshakeFinished();
}
}
@@ -927,10 +955,76 @@ public final class OpenSslEngine extends SSLEngine {
}
}
+ private void handshakeFinished() throws SSLException {
+ SelectedListenerFailureBehavior behavior = apn.selectedListenerFailureBehavior();
+ List protocols = apn.protocols();
+ String applicationProtocol;
+ switch (apn.protocol()) {
+ case NONE:
+ break;
+ // We always need to check for applicationProtocol == null as the remote peer may not support
+ // the TLS extension or may have returned an empty selection.
+ case ALPN:
+ applicationProtocol = SSL.getAlpnSelected(ssl);
+ if (applicationProtocol != null) {
+ this.applicationProtocol = selectApplicationProtocol(protocols, behavior, applicationProtocol);
+ }
+ break;
+ case NPN:
+ applicationProtocol = SSL.getNextProtoNegotiated(ssl);
+ if (applicationProtocol != null) {
+ this.applicationProtocol = selectApplicationProtocol(protocols, behavior, applicationProtocol);
+ }
+ break;
+ case NPN_AND_ALPN:
+ applicationProtocol = SSL.getAlpnSelected(ssl);
+ if (applicationProtocol == null) {
+ applicationProtocol = SSL.getNextProtoNegotiated(ssl);
+ }
+ if (applicationProtocol != null) {
+ this.applicationProtocol = selectApplicationProtocol(protocols, behavior, applicationProtocol);
+ }
+ break;
+ default:
+ throw new Error();
+ }
+ handshakeFinished = true;
+ }
+
+ private static String selectApplicationProtocol(List protocols,
+ SelectedListenerFailureBehavior behavior,
+ String applicationProtocol) throws SSLException {
+ applicationProtocol = applicationProtocol.replace(':', '_');
+ if (behavior == SelectedListenerFailureBehavior.ACCEPT) {
+ return applicationProtocol;
+ } else {
+ int size = protocols.size();
+ assert size > 0;
+ if (protocols.contains(applicationProtocol)) {
+ return applicationProtocol;
+ } else {
+ if (behavior == SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL) {
+ return protocols.get(size - 1);
+ } else {
+ throw new SSLException("Unknown protocol " + applicationProtocol);
+ }
+ }
+ }
+ }
+
private SSLEngineResult.Status getEngineStatus() {
return engineClosed? CLOSED : OK;
}
+ private SSLEngineResult.HandshakeStatus handshakeStatus0() throws SSLException {
+ SSLEngineResult.HandshakeStatus status = getHandshakeStatus();
+ if (status == FINISHED) {
+ handshakeFinished();
+ }
+
+ return status;
+ }
+
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
if (accepted == 0 || destroyed != 0) {
@@ -947,7 +1041,6 @@ public final class OpenSslEngine extends SSLEngine {
// No pending data to be sent to the peer
// Check to see if we have finished handshaking
if (SSL.isInInit(ssl) == 0) {
- handshakeFinished = true;
return FINISHED;
}
@@ -1088,8 +1181,15 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public byte[] getId() {
+ final byte[] id;
+ synchronized (OpenSslEngine.this) {
+ if (destroyed == 0) {
+ id = SSL.getSessionId(ssl);
+ } else {
+ id = EmptyArrays.EMPTY_BYTES;
+ }
+ }
// We don't cache that to keep memory usage to a minimum.
- byte[] id = SSL.getSessionId(ssl);
if (id == null) {
// The id should never be null, if it was null then the SESSION itself was not valid.
throw new IllegalStateException("SSL session ID not available");
@@ -1104,8 +1204,13 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public long getCreationTime() {
- // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
- return SSL.getTime(ssl) * 1000L;
+ synchronized (OpenSslEngine.this) {
+ if (destroyed == 0) {
+ // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
+ return SSL.getTime(ssl) * 1000L;
+ }
+ return 0;
+ }
}
@Override
@@ -1126,9 +1231,12 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public void putValue(String name, Object value) {
- ObjectUtil.checkNotNull(name, "name");
- ObjectUtil.checkNotNull(value, "value");
-
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ if (value == null) {
+ throw new NullPointerException("value");
+ }
Map values = this.values;
if (values == null) {
// Use size of 2 to keep the memory overhead small
@@ -1143,8 +1251,9 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public Object getValue(String name) {
- ObjectUtil.checkNotNull(name, "name");
-
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
if (values == null) {
return null;
}
@@ -1153,8 +1262,9 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public void removeValue(String name) {
- ObjectUtil.checkNotNull(name, "name");
-
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
Map values = this.values;
if (values == null) {
return;
@@ -1183,53 +1293,20 @@ public final class OpenSslEngine extends SSLEngine {
// these are lazy created to reduce memory overhead
Certificate[] c = peerCerts;
if (c == null) {
- if (SSL.isInInit(ssl) != 0) {
- throw new SSLPeerUnverifiedException("peer not verified");
+ synchronized (OpenSslEngine.this) {
+ if (destroyed == 0) {
+ if (SSL.isInInit(ssl) != 0) {
+ throw new SSLPeerUnverifiedException("peer not verified");
+ }
+ c = peerCerts = initPeerCertChain();
+ } else {
+ c = peerCerts = EMPTY_CERTIFICATES;
+ }
}
- c = peerCerts = initPeerCertChain();
}
return c;
}
- private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
- byte[][] chain = SSL.getPeerCertChain(ssl);
- byte[] clientCert;
- if (!clientMode) {
- // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
- // We use SSL_get_peer_certificate to get it in this case and add it to our array later.
- //
- // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
- clientCert = SSL.getPeerCertificate(ssl);
- } else {
- clientCert = null;
- }
-
- if (chain == null && clientCert == null) {
- throw new SSLPeerUnverifiedException("peer not verified");
- }
- int len = 0;
- if (chain != null) {
- len += chain.length;
- }
-
- int i = 0;
- Certificate[] peerCerts;
- if (clientCert != null) {
- len++;
- peerCerts = new Certificate[len];
- peerCerts[i++] = new OpenSslX509Certificate(clientCert);
- } else {
- peerCerts = new Certificate[len];
- }
- if (chain != null) {
- int a = 0;
- for (; i < peerCerts.length; i++) {
- peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
- }
- }
- return peerCerts;
- }
-
@Override
public Certificate[] getLocalCertificates() {
// TODO: Find out how to get these
@@ -1241,10 +1318,18 @@ public final class OpenSslEngine extends SSLEngine {
// these are lazy created to reduce memory overhead
X509Certificate[] c = x509PeerCerts;
if (c == null) {
- if (SSL.isInInit(ssl) != 0) {
- throw new SSLPeerUnverifiedException("peer not verified");
+ final byte[][] chain;
+ synchronized (OpenSslEngine.this) {
+ if (destroyed == 0) {
+ if (SSL.isInInit(ssl) != 0) {
+ throw new SSLPeerUnverifiedException("peer not verified");
+ }
+ chain = SSL.getPeerCertChain(ssl);
+ } else {
+ c = x509PeerCerts = EMPTY_X509_CERTIFICATES;
+ return c;
+ }
}
- byte[][] chain = SSL.getPeerCertChain(ssl);
if (chain == null) {
throw new SSLPeerUnverifiedException("peer not verified");
}
@@ -1289,7 +1374,14 @@ public final class OpenSslEngine extends SSLEngine {
return INVALID_CIPHER;
}
if (cipher == null) {
- String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
+ final String c;
+ synchronized (OpenSslEngine.this) {
+ if (destroyed == 0) {
+ c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
+ } else {
+ c = INVALID_CIPHER;
+ }
+ }
if (c != null) {
cipher = c;
}
@@ -1300,19 +1392,15 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public String getProtocol() {
String applicationProtocol = OpenSslEngine.this.applicationProtocol;
- if (applicationProtocol == null) {
- applicationProtocol = SSL.getNextProtoNegotiated(ssl);
- if (applicationProtocol == null) {
- applicationProtocol = fallbackApplicationProtocol;
- }
- if (applicationProtocol != null) {
- OpenSslEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
+ final String version;
+ synchronized (OpenSslEngine.this) {
+ if (destroyed == 0) {
+ version = SSL.getVersion(ssl);
} else {
- OpenSslEngine.this.applicationProtocol = applicationProtocol = "";
+ return StringUtil.EMPTY_STRING;
}
}
- String version = SSL.getVersion(ssl);
- if (applicationProtocol.isEmpty()) {
+ if (applicationProtocol == null || applicationProtocol.isEmpty()) {
return version;
} else {
return version + ':' + applicationProtocol;
@@ -1338,5 +1426,44 @@ public final class OpenSslEngine extends SSLEngine {
public int getApplicationBufferSize() {
return MAX_PLAINTEXT_LENGTH;
}
+
+ private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
+ byte[][] chain = SSL.getPeerCertChain(ssl);
+ final byte[] clientCert;
+ if (!clientMode) {
+ // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
+ // We use SSL_get_peer_certificate to get it in this case and add it to our array later.
+ //
+ // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
+ clientCert = SSL.getPeerCertificate(ssl);
+ } else {
+ clientCert = null;
+ }
+
+ if (chain == null && clientCert == null) {
+ throw new SSLPeerUnverifiedException("peer not verified");
+ }
+ int len = 0;
+ if (chain != null) {
+ len += chain.length;
+ }
+
+ int i = 0;
+ Certificate[] peerCerts;
+ if (clientCert != null) {
+ len++;
+ peerCerts = new Certificate[len];
+ peerCerts[i++] = new OpenSslX509Certificate(clientCert);
+ } else {
+ peerCerts = new Certificate[len];
+ }
+ if (chain != null) {
+ int a = 0;
+ for (; i < peerCerts.length; i++) {
+ peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
+ }
+ }
+ return peerCerts;
+ }
}
}
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslNpnApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslNpnApplicationProtocolNegotiator.java
index e4b4e8f043..a4bf6853fa 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslNpnApplicationProtocolNegotiator.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslNpnApplicationProtocolNegotiator.java
@@ -22,7 +22,10 @@ import java.util.List;
/**
* OpenSSL {@link ApplicationProtocolNegotiator} for NPN.
+ *
+ * @deprecated use {@link OpenSslDefaultApplicationProtocolNegotiator}
*/
+@Deprecated
public final class OpenSslNpnApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
private final List protocols;
@@ -34,8 +37,23 @@ public final class OpenSslNpnApplicationProtocolNegotiator implements OpenSslApp
this.protocols = checkNotNull(toList(protocols), "protocols");
}
+ @Override
+ public ApplicationProtocolConfig.Protocol protocol() {
+ return ApplicationProtocolConfig.Protocol.NPN;
+ }
+
@Override
public List protocols() {
return protocols;
}
+
+ @Override
+ public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
+ return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
+ }
+
+ @Override
+ public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
+ return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT;
+ }
}
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java
index 8d73cf1f2a..603d57d6ee 100644
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java
+++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java
@@ -64,11 +64,14 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code null} if it's not password-protected.
*/
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
- this(certChainFile, keyFile, keyPassword, null, null,
- OpenSslDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
+ this(certChainFile, keyFile, keyPassword, null, null, IdentityCipherSuiteFilter.INSTANCE,
+ NONE_PROTOCOL_NEGOTIATOR, 0, 0);
}
/**
+ * @deprecated use {@link #OpenSslServerContext(
+ * File, File, String, Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
+ *
* Creates a new instance.
*
* @param certChainFile an X.509 certificate chain file in PEM format
@@ -83,12 +86,13 @@ public final class OpenSslServerContext extends OpenSslContext {
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
*/
+ @Deprecated
public OpenSslServerContext(
File certChainFile, File keyFile, String keyPassword,
Iterable ciphers, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
this(certChainFile, keyFile, keyPassword, null, ciphers,
- toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
+ toNegotiator(apn), sessionCacheSize, sessionTimeout);
}
/**
@@ -105,13 +109,17 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
+ * @deprecated use {@link #OpenSslServerContext(
+ * File, File, String, TrustManagerFactory, Iterable,
+ * CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
*/
+ @Deprecated
public OpenSslServerContext(
File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory,
Iterable ciphers, ApplicationProtocolConfig config,
long sessionCacheSize, long sessionTimeout) throws SSLException {
this(certChainFile, keyFile, keyPassword, trustManagerFactory, ciphers,
- toNegotiator(config, true), sessionCacheSize, sessionTimeout);
+ toNegotiator(config), sessionCacheSize, sessionTimeout);
}
/**
@@ -128,14 +136,89 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
+ * @deprecated use {@link #OpenSslServerContext(
+ * File, File, String, TrustManagerFactory, Iterable,
+ * CipherSuiteFilter, OpenSslApplicationProtocolNegotiator, long, long)}
*/
+ @Deprecated
public OpenSslServerContext(
File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory,
Iterable ciphers, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
+ this(certChainFile, keyFile, keyPassword, trustManagerFactory, ciphers,
+ IdentityCipherSuiteFilter.INSTANCE, apn, sessionCacheSize, sessionTimeout);
+ }
- super(ciphers, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER);
- OpenSsl.ensureAvailability();
+ /**
+ * Creates a new instance.
+ *
+ * @param certChainFile an X.509 certificate chain file in PEM format
+ * @param keyFile a PKCS#8 private key file in PEM format
+ * @param keyPassword the password of the {@code keyFile}.
+ * {@code null} if it's not password-protected.
+ * @param ciphers the cipher suites to enable, in the order of preference.
+ * {@code null} to use the default cipher suites.
+ * @param cipherFilter a filter to apply over the supplied list of ciphers
+ * @param apn Provides a means to configure parameters related to application protocol negotiation.
+ * @param sessionCacheSize the size of the cache used for storing SSL session objects.
+ * {@code 0} to use the default value.
+ * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
+ * {@code 0} to use the default value.
+ */
+ public OpenSslServerContext(
+ File certChainFile, File keyFile, String keyPassword,
+ Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
+ long sessionCacheSize, long sessionTimeout) throws SSLException {
+ this(certChainFile, keyFile, keyPassword, null, ciphers, cipherFilter,
+ toNegotiator(apn), sessionCacheSize, sessionTimeout);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param certChainFile an X.509 certificate chain file in PEM format
+ * @param keyFile a PKCS#8 private key file in PEM format
+ * @param keyPassword the password of the {@code keyFile}.
+ * {@code null} if it's not password-protected.
+ * @param ciphers the cipher suites to enable, in the order of preference.
+ * {@code null} to use the default cipher suites.
+ * @param cipherFilter a filter to apply over the supplied list of ciphers
+ * @param config Application protocol config.
+ * @param sessionCacheSize the size of the cache used for storing SSL session objects.
+ * {@code 0} to use the default value.
+ * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
+ * {@code 0} to use the default value.
+ */
+ public OpenSslServerContext(
+ File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory,
+ Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config,
+ long sessionCacheSize, long sessionTimeout) throws SSLException {
+ this(certChainFile, keyFile, keyPassword, trustManagerFactory, ciphers, cipherFilter,
+ toNegotiator(config), sessionCacheSize, sessionTimeout);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param certChainFile an X.509 certificate chain file in PEM format
+ * @param keyFile a PKCS#8 private key file in PEM format
+ * @param keyPassword the password of the {@code keyFile}.
+ * {@code null} if it's not password-protected.
+ * @param ciphers the cipher suites to enable, in the order of preference.
+ * {@code null} to use the default cipher suites.
+ * @param cipherFilter a filter to apply over the supplied list of ciphers
+ * @param apn Application protocol negotiator.
+ * @param sessionCacheSize the size of the cache used for storing SSL session objects.
+ * {@code 0} to use the default value.
+ * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
+ * {@code 0} to use the default value.
+ */
+ public OpenSslServerContext(
+ File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory,
+ Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
+ long sessionCacheSize, long sessionTimeout) throws SSLException {
+ super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER);
+ OpenSsl.ensureAvailability();
checkNotNull(certChainFile, "certChainFile");
if (!certChainFile.isFile()) {
diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java
index 45ecf94e46..09cfde3451 100644
--- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java
+++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java
@@ -262,7 +262,7 @@ public abstract class SslContext {
case OPENSSL:
return new OpenSslServerContext(
keyCertChainFile, keyFile, keyPassword, trustManagerFactory,
- ciphers, apn, sessionCacheSize, sessionTimeout);
+ ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
default:
throw new Error(provider.toString());
}
@@ -485,7 +485,8 @@ public abstract class SslContext {
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
case OPENSSL:
return new OpenSslClientContext(
- trustCertChainFile, trustManagerFactory, ciphers, apn, sessionCacheSize, sessionTimeout);
+ trustCertChainFile, trustManagerFactory, ciphers, cipherFilter, apn,
+ sessionCacheSize, sessionTimeout);
}
// Should never happen!!
throw new Error();
diff --git a/pom.xml b/pom.xml
index 2fff911a0b..85875c6a4b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -615,7 +615,7 @@
${project.groupId}
netty-tcnative
- 1.1.32.Fork1
+ 1.1.33.Fork1
${os.detected.classifier}
compile
true