Add support for ALPN when using openssl + NPN client mode and support for CipherSuiteFilter
Motivation: To support HTTP2 we need APLN support. This was not provided before when using OpenSslEngine, so SSLEngine (JDK one) was the only bet. Beside this CipherSuiteFilter was not supported Modifications: - Upgrade netty-tcnative and make use of new features to support ALPN and NPN in server and client mode. - Guard against segfaults after the ssl pointer is freed - support correctly different failure behaviours - add support for CipherSuiteFilter Result: Be able to use OpenSslEngine for ALPN / NPN for server and client.
This commit is contained in:
parent
da01902ea2
commit
d1a9a08f36
@ -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() { }
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,6 +113,14 @@ public final class OpenSsl {
|
||||
return UNAVAILABILITY_CAUSE == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the used version of openssl supports
|
||||
* <a href="https://tools.ietf.org/html/rfc7301">ALPN</a>.
|
||||
*/
|
||||
public static boolean isAlpnSupported() {
|
||||
return isAvailable() && SSL.version() >= 0x10002000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and
|
||||
* its OpenSSL support are available.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<String> 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<String> 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()) {
|
||||
|
@ -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<String> ciphers = new ArrayList<String>();
|
||||
private final List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers);
|
||||
private final List<String> 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<String> protocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectorFailureBehavior selectorFailureBehavior() {
|
||||
return SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
|
||||
return SelectedListenerFailureBehavior.ACCEPT;
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
List<String> ciphers = new ArrayList<String>();
|
||||
// XXX: Make sure to sync this list with JdkSslEngineFactory.
|
||||
@ -86,12 +111,13 @@ public abstract class OpenSslContext extends SslContext {
|
||||
DESTROY_UPDATER = updater;
|
||||
}
|
||||
|
||||
OpenSslContext(Iterable<String> 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<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg,
|
||||
long sessionCacheSize, long sessionTimeout, int mode) throws SSLException {
|
||||
this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode);
|
||||
}
|
||||
|
||||
OpenSslContext(Iterable<String> ciphers, OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize,
|
||||
OpenSslContext(Iterable<String> 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<String> convertedCiphers;
|
||||
if (ciphers == null) {
|
||||
ciphers = DEFAULT_CIPHERS;
|
||||
convertedCiphers = null;
|
||||
} else {
|
||||
convertedCiphers = new ArrayList<String>();
|
||||
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<String> 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<String> cipherSuites() {
|
||||
return unmodifiableCiphers;
|
||||
@ -228,15 +277,8 @@ public abstract class OpenSslContext extends SslContext {
|
||||
*/
|
||||
@Override
|
||||
public final SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||
List<String> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<String> 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();
|
||||
}
|
||||
}
|
||||
|
@ -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<String> enabled = new ArrayList<String>();
|
||||
// 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<String> 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<String> 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<String, Object> 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<String, Object> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<String> 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<String> protocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() {
|
||||
return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
|
||||
return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT;
|
||||
}
|
||||
}
|
||||
|
@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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()) {
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user