Ensure X509KeyManager methods are called on the correct time when using server-side and support more methods of ExtendedSSLSession. (#8283)

Motivation:

Before when on server-side we just called the X509KeyManager methods when handshake() was called the first time which is not quite correct as we may not have received the full SSL hello / handshake and so could not extra for example the SNI hostname that was requested.
OpenSSL exposes the SSL_CTX_set_cert_cb function which allows to set a callback which is executed at the correct moment, so we should use it. This also allows us to support more methods of ExtendedSSLSession easily.

Modifications:

- Make use of new methods exposed by netty-tcnative since https://github.com/netty/netty-tcnative/pull/388 to ensure we select the key material at the correct time.
- Implement more methods of ExtendedOpenSslSession
- Add unit tests to ensure we are able to retrieve various things on server-side in the X509KeyManager and so verify it is called at the correct time.
- Simplify code by using new netty-tcnative methods.

Result:

More correct implementation for server-side usage and more complete implemented of ExtendedSSLSession.
This commit is contained in:
Norman Maurer 2018-09-28 11:34:38 +02:00 committed by GitHub
parent 73acac13f4
commit 59973e93dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 286 additions and 134 deletions

View File

@ -15,8 +15,6 @@
*/
package io.netty.handler.ssl;
import io.netty.util.internal.EmptyArrays;
import javax.net.ssl.ExtendedSSLSession;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
@ -60,128 +58,122 @@ abstract class ExtendedOpenSslSession extends ExtendedSSLSession implements Open
}
@Override
public void handshakeFinished() throws SSLException {
public final void handshakeFinished() throws SSLException {
wrapped.handshakeFinished();
}
@Override
public void tryExpandApplicationBufferSize(int packetLengthDataOnly) {
public final void tryExpandApplicationBufferSize(int packetLengthDataOnly) {
wrapped.tryExpandApplicationBufferSize(packetLengthDataOnly);
}
@Override
public String[] getLocalSupportedSignatureAlgorithms() {
public final String[] getLocalSupportedSignatureAlgorithms() {
return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
}
@Override
public String[] getPeerSupportedSignatureAlgorithms() {
// Always return empty for now.
return EmptyArrays.EMPTY_STRINGS;
}
@Override
public byte[] getId() {
public final byte[] getId() {
return wrapped.getId();
}
@Override
public SSLSessionContext getSessionContext() {
public final SSLSessionContext getSessionContext() {
return wrapped.getSessionContext();
}
@Override
public long getCreationTime() {
public final long getCreationTime() {
return wrapped.getCreationTime();
}
@Override
public long getLastAccessedTime() {
public final long getLastAccessedTime() {
return wrapped.getLastAccessedTime();
}
@Override
public void invalidate() {
public final void invalidate() {
wrapped.invalidate();
}
@Override
public boolean isValid() {
public final boolean isValid() {
return wrapped.isValid();
}
@Override
public void putValue(String s, Object o) {
public final void putValue(String s, Object o) {
wrapped.putValue(s, o);
}
@Override
public Object getValue(String s) {
public final Object getValue(String s) {
return wrapped.getValue(s);
}
@Override
public void removeValue(String s) {
public final void removeValue(String s) {
wrapped.removeValue(s);
}
@Override
public String[] getValueNames() {
public final String[] getValueNames() {
return wrapped.getValueNames();
}
@Override
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
public final Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
return wrapped.getPeerCertificates();
}
@Override
public Certificate[] getLocalCertificates() {
public final Certificate[] getLocalCertificates() {
return wrapped.getLocalCertificates();
}
@Override
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
return wrapped.getPeerCertificateChain();
}
@Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
public final Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return wrapped.getPeerPrincipal();
}
@Override
public Principal getLocalPrincipal() {
public final Principal getLocalPrincipal() {
return wrapped.getLocalPrincipal();
}
@Override
public String getCipherSuite() {
public final String getCipherSuite() {
return wrapped.getCipherSuite();
}
@Override
public String getProtocol() {
public final String getProtocol() {
return wrapped.getProtocol();
}
@Override
public String getPeerHost() {
public final String getPeerHost() {
return wrapped.getPeerHost();
}
@Override
public int getPeerPort() {
public final int getPeerPort() {
return wrapped.getPeerPort();
}
@Override
public int getPacketBufferSize() {
public final int getPacketBufferSize() {
return wrapped.getPacketBufferSize();
}
@Override
public int getApplicationBufferSize() {
public final int getApplicationBufferSize() {
return wrapped.getApplicationBufferSize();
}
}

View File

@ -62,6 +62,13 @@ final class Java8SslUtils {
return sniServerNames;
}
static List getSniHostName(byte[] hostname) {
if (hostname == null || hostname.length == 0) {
return Collections.emptyList();
}
return Collections.singletonList(new SNIHostName(hostname));
}
static boolean getUseCipherSuitesOrder(SSLParameters sslParameters) {
return sslParameters.getUseCipherSuitesOrder();
}

View File

@ -203,9 +203,4 @@ public final class OpenSslClientContext extends OpenSslContext {
public OpenSslSessionContext sessionContext() {
return sessionContext;
}
@Override
OpenSslKeyMaterialManager keyMaterialManager() {
return null;
}
}

View File

@ -74,36 +74,25 @@ final class OpenSslKeyMaterialManager {
if (type != null) {
String alias = chooseServerAlias(engine, type);
if (alias != null && aliases.add(alias)) {
OpenSslKeyMaterial keyMaterial = null;
try {
keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias);
if (keyMaterial != null) {
SSL.setKeyMaterialServerSide(
ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress());
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException(e);
} finally {
if (keyMaterial != null) {
keyMaterial.release();
}
}
setKeyMaterial(engine, alias);
}
}
}
}
void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, long certOut, long keyOut, String[] keyTypes,
void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, String[] keyTypes,
X500Principal[] issuer) throws SSLException {
String alias = chooseClientAlias(engine, keyTypes, issuer);
setKeyMaterial(engine, alias);
}
private void setKeyMaterial(ReferenceCountedOpenSslEngine engine, String alias) throws SSLException {
OpenSslKeyMaterial keyMaterial = null;
try {
keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias);
if (keyMaterial != null) {
SSL.setKeyMaterialClientSide(engine.sslPointer(), certOut, keyOut,
keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress());
SSL.setKeyMaterial(engine.sslPointer(),
keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress());
}
} catch (SSLException e) {
throw e;
@ -115,7 +104,6 @@ final class OpenSslKeyMaterialManager {
}
}
}
private String chooseClientAlias(ReferenceCountedOpenSslEngine engine,
String[] keyTypes, X500Principal[] issuer) {
X509KeyManager manager = provider.keyManager();

View File

@ -15,7 +15,6 @@
*/
package io.netty.handler.ssl;
import io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.ServerContext;
import io.netty.internal.tcnative.SSL;
import java.io.File;
@ -37,7 +36,6 @@ import static io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.newSessi
*/
public final class OpenSslServerContext extends OpenSslContext {
private final OpenSslServerSessionContext sessionContext;
private final OpenSslKeyMaterialManager keyMaterialManager;
/**
* Creates a new instance.
@ -349,10 +347,8 @@ public final class OpenSslServerContext extends OpenSslContext {
// Create a new SSL_CTX and configure it.
boolean success = false;
try {
ServerContext context = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
sessionContext = context.sessionContext;
keyMaterialManager = context.keyMaterialManager;
sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
success = true;
} finally {
if (!success) {
@ -365,9 +361,4 @@ public final class OpenSslServerContext extends OpenSslContext {
public OpenSslServerSessionContext sessionContext() {
return sessionContext;
}
@Override
OpenSslKeyMaterialManager keyMaterialManager() {
return keyMaterialManager;
}
}

View File

@ -15,15 +15,16 @@
*/
package io.netty.handler.ssl;
import io.netty.internal.tcnative.CertificateCallback;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.internal.tcnative.CertificateRequestedCallback;
import io.netty.internal.tcnative.SSL;
import io.netty.internal.tcnative.SSLContext;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ -68,11 +69,6 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
}
}
@Override
OpenSslKeyMaterialManager keyMaterialManager() {
return null;
}
@Override
public OpenSslSessionContext sessionContext() {
return sessionContext;
@ -118,7 +114,7 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
if (keyMaterialProvider != null) {
OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider);
SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback(
SSLContext.setCertificateCallback(ctx, new OpenSslClientCertificateCallback(
engineMap, materialManager));
}
}
@ -238,18 +234,17 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
}
}
private static final class OpenSslCertificateRequestedCallback implements CertificateRequestedCallback {
private static final class OpenSslClientCertificateCallback implements CertificateCallback {
private final OpenSslEngineMap engineMap;
private final OpenSslKeyMaterialManager keyManagerHolder;
OpenSslCertificateRequestedCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) {
OpenSslClientCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) {
this.engineMap = engineMap;
this.keyManagerHolder = keyManagerHolder;
}
@Override
public void requested(
long ssl, long certOut, long keyOut, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) {
public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception {
final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
try {
final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes);
@ -263,7 +258,7 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
}
}
keyManagerHolder.setKeyMaterialClientSide(engine, certOut, keyOut, keyTypes, issuers);
keyManagerHolder.setKeyMaterialClientSide(engine, keyTypes, issuers);
} catch (Throwable cause) {
logger.debug("request of key failed", cause);
SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
@ -281,6 +276,9 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
* {@code X509ExtendedKeyManager.chooseEngineClientAlias}.
*/
private static Set<String> supportedClientKeyTypes(byte[] clientCertificateTypes) {
if (clientCertificateTypes == null) {
return Collections.emptySet();
}
Set<String> result = new HashSet<String>(clientCertificateTypes.length);
for (byte keyTypeCode : clientCertificateTypes) {
String keyType = clientKeyType(keyTypeCode);
@ -296,15 +294,15 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
private static String clientKeyType(byte clientCertificateType) {
// See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
switch (clientCertificateType) {
case CertificateRequestedCallback.TLS_CT_RSA_SIGN:
case CertificateCallback.TLS_CT_RSA_SIGN:
return OpenSslKeyMaterialManager.KEY_TYPE_RSA; // RFC rsa_sign
case CertificateRequestedCallback.TLS_CT_RSA_FIXED_DH:
case CertificateCallback.TLS_CT_RSA_FIXED_DH:
return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
case CertificateRequestedCallback.TLS_CT_ECDSA_SIGN:
case CertificateCallback.TLS_CT_ECDSA_SIGN:
return OpenSslKeyMaterialManager.KEY_TYPE_EC; // RFC ecdsa_sign
case CertificateRequestedCallback.TLS_CT_RSA_FIXED_ECDH:
case CertificateCallback.TLS_CT_RSA_FIXED_ECDH:
return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
case CertificateRequestedCallback.TLS_CT_ECDSA_FIXED_ECDH:
case CertificateCallback.TLS_CT_ECDSA_FIXED_ECDH:
return OpenSslKeyMaterialManager.KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh
default:
return null;

View File

@ -225,7 +225,9 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
boolean success = false;
try {
try {
ctx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, mode);
int opts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 |
SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2;
ctx = SSLContext.make(opts, mode);
} catch (Exception e) {
throw new SSLException("failed to create an SSL_CTX", e);
}
@ -366,8 +368,6 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true);
}
abstract OpenSslKeyMaterialManager keyMaterialManager();
/**
* Returns a new server-side {@link SSLEngine} with the current configuration.
*/

View File

@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.internal.tcnative.Buffer;
import io.netty.internal.tcnative.SSL;
import io.netty.util.AbstractReferenceCounted;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCounted;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.ResourceLeakDetectorFactory;
@ -142,7 +143,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
// OpenSSL state
private long ssl;
private long networkBIO;
private boolean certificateSet;
private enum HandshakeState {
/**
@ -217,7 +217,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
private final Certificate[] localCerts;
private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1];
private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
private final OpenSslKeyMaterialManager keyMaterialManager;
private final boolean enableOcsp;
private int maxWrapOverhead;
private int maxWrapBufferSize;
@ -238,18 +237,69 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
* wrap or unwrap call.
* @param leakDetection {@code true} to enable leak detection of this object.
*/
ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, ByteBufAllocator alloc, String peerHost,
ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, final ByteBufAllocator alloc, String peerHost,
int peerPort, boolean jdkCompatibilityMode, boolean leakDetection) {
super(peerHost, peerPort);
OpenSsl.ensureAvailability();
this.alloc = checkNotNull(alloc, "alloc");
apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator();
clientMode = context.isClient();
if (PlatformDependent.javaVersion() >= 7 && context.isClient()) {
if (PlatformDependent.javaVersion() >= 7) {
session = new ExtendedOpenSslSession(new DefaultOpenSslSession(context.sessionContext())) {
private String[] peerSupportedSignatureAlgorithms;
private List requestedServerNames;
@Override
public List getRequestedServerNames() {
return Java8SslUtils.getSniHostNames(sniHostNames);
if (clientMode) {
return Java8SslUtils.getSniHostNames(sniHostNames);
} else {
synchronized (ReferenceCountedOpenSslEngine.this) {
if (requestedServerNames == null) {
if (isDestroyed()) {
requestedServerNames = Collections.emptyList();
} else {
String name = SSL.getSniHostname(ssl);
if (name == null) {
requestedServerNames = Collections.emptyList();
} else {
// Convert to bytes as we do not want to do any strict validation of the
// SNIHostName while creating it.
requestedServerNames =
Java8SslUtils.getSniHostName(
SSL.getSniHostname(ssl).getBytes(CharsetUtil.UTF_8));
}
}
}
return requestedServerNames;
}
}
}
@Override
public String[] getPeerSupportedSignatureAlgorithms() {
synchronized (ReferenceCountedOpenSslEngine.this) {
if (peerSupportedSignatureAlgorithms == null) {
if (isDestroyed()) {
peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS;
} else {
String[] algs = SSL.getSigAlgs(ssl);
if (algs == null) {
peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS;
} else {
List<String> algorithmList = new ArrayList<String>(algs.length);
for (String alg: algs) {
String converted = SignatureAlgorithmConverter.toJavaName(alg);
if (converted != null) {
algorithmList.add(converted);
}
}
peerSupportedSignatureAlgorithms = algorithmList.toArray(new String[0]);
}
}
}
return peerSupportedSignatureAlgorithms.clone();
}
}
@Override
@ -271,7 +321,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
}
engineMap = context.engineMap;
localCerts = context.keyCertChain;
keyMaterialManager = context.keyMaterialManager();
enableOcsp = context.enableOcsp;
this.jdkCompatibilityMode = jdkCompatibilityMode;
Lock readerLock = context.ctxLock.readLock();
@ -1593,11 +1642,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
lastAccessed = System.currentTimeMillis();
}
if (!certificateSet && keyMaterialManager != null) {
certificateSet = true;
keyMaterialManager.setKeyMaterialServerSide(this);
}
int code = SSL.doHandshake(ssl);
if (code <= 0) {
// Check if we have a pending exception that was created during the handshake and if so throw it after

View File

@ -16,6 +16,7 @@
package io.netty.handler.ssl;
import io.netty.buffer.ByteBufAllocator;
import io.netty.internal.tcnative.CertificateCallback;
import io.netty.internal.tcnative.SSL;
import io.netty.internal.tcnative.SSLContext;
import io.netty.internal.tcnative.SniHostNameMatcher;
@ -29,6 +30,7 @@ import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
@ -48,7 +50,6 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
InternalLoggerFactory.getInstance(ReferenceCountedOpenSslServerContext.class);
private static final byte[] ID = {'n', 'e', 't', 't', 'y'};
private final OpenSslServerSessionContext sessionContext;
private final OpenSslKeyMaterialManager keyMaterialManager;
ReferenceCountedOpenSslServerContext(
X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
@ -72,10 +73,8 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
// Create a new SSL_CTX and configure it.
boolean success = false;
try {
ServerContext context = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
sessionContext = context.sessionContext;
keyMaterialManager = context.keyMaterialManager;
success = true;
} finally {
if (!success) {
@ -89,23 +88,13 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
return sessionContext;
}
@Override
OpenSslKeyMaterialManager keyMaterialManager() {
return keyMaterialManager;
}
static final class ServerContext {
OpenSslServerSessionContext sessionContext;
OpenSslKeyMaterialManager keyMaterialManager;
}
static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, OpenSslEngineMap engineMap,
X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key,
String keyPassword, KeyManagerFactory keyManagerFactory)
static OpenSslServerSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx,
OpenSslEngineMap engineMap,
X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key,
String keyPassword, KeyManagerFactory keyManagerFactory)
throws SSLException {
ServerContext result = new ServerContext();
OpenSslKeyMaterialProvider keyMaterialProvider = null;
try {
try {
@ -134,7 +123,8 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
}
keyMaterialProvider = providerFor(keyManagerFactory, keyPassword);
result.keyMaterialManager = new OpenSslKeyMaterialManager(keyMaterialProvider);
SSLContext.setCertificateCallback(ctx, new OpenSslServerCertificateCallback(
engineMap, new OpenSslKeyMaterialManager(keyMaterialProvider)));
}
} catch (Exception e) {
throw new SSLException("failed to set certificate and key", e);
@ -159,8 +149,8 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
SSLContext.setCertVerifyCallback(ctx,
new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager));
SSLContext.setCertVerifyCallback(ctx, new ExtendedTrustManagerVerifyCallback(
engineMap, (X509ExtendedTrustManager) manager));
} else {
SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager));
}
@ -191,12 +181,12 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
throw new SSLException("unable to setup trustmanager", e);
}
result.sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider);
result.sessionContext.setSessionIdContext(ID);
OpenSslServerSessionContext sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider);
sessionContext.setSessionIdContext(ID);
keyMaterialProvider = null;
return result;
return sessionContext;
} finally {
if (keyMaterialProvider != null) {
keyMaterialProvider.destroy();
@ -204,6 +194,31 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
}
}
private static final class OpenSslServerCertificateCallback implements CertificateCallback {
private final OpenSslEngineMap engineMap;
private final OpenSslKeyMaterialManager keyManagerHolder;
OpenSslServerCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) {
this.engineMap = engineMap;
this.keyManagerHolder = keyManagerHolder;
}
@Override
public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception {
final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
try {
// For now we just ignore the asn1DerEncodedPrincipals as this is kind of inline with what the
// OpenJDK SSLEngineImpl does.
keyManagerHolder.setKeyMaterialServerSide(engine);
} catch (Throwable cause) {
logger.debug("Failed to set the server-side key material", cause);
SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
e.initCause(cause);
engine.handshakeException = e;
}
}
}
private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier {
private final X509TrustManager manager;

View File

@ -0,0 +1,61 @@
/*
* Copyright 2018 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Converts OpenSSL signature Algorithm names to
* <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature">
* Java signature Algorithm names</a>.
*/
final class SignatureAlgorithmConverter {
private SignatureAlgorithmConverter() { }
// OpenSSL has 3 different formats it uses at the moment we will match against all of these.
// For example:
// ecdsa-with-SHA384
// hmacWithSHA384
// dsa_with_SHA224
//
// For more details see https://github.com/openssl/openssl/blob/OpenSSL_1_0_2p/crypto/objects/obj_dat.h
private static final Pattern PATTERN = Pattern.compile(
"((^[a-zA-Z].+)With(.+)Encryption$)|((^[a-zA-Z].+)(_with_|-with-)(.+$))");
/**
* Converts an OpenSSL algorithm name to a Java algorithm name and return it,
* or return {@code null} if the conversation failed because the format is not known.
*/
static String toJavaName(String opensslName) {
if (opensslName == null) {
return null;
}
Matcher matcher = PATTERN.matcher(opensslName);
if (matcher.matches()) {
String group2 = matcher.group(2);
if (group2 != null) {
return group2.toUpperCase(Locale.ROOT) + "with" + matcher.group(3).toUpperCase(Locale.ROOT);
}
if (matcher.group(4) != null) {
return matcher.group(7).toUpperCase(Locale.ROOT) + "with" + matcher.group(5).toUpperCase(Locale.ROOT);
}
}
return null;
}
}

View File

@ -24,4 +24,8 @@ final class OpenSslTestUtils {
static void checkShouldUseKeyManagerFactory() {
assumeTrue(OpenSsl.supportsKeyManagerFactory() && OpenSsl.useKeyManagerFactory());
}
static boolean isBoringSSL() {
return "BoringSSL".equals(OpenSsl.versionString());
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2018 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class SignatureAlgorithmConverterTest {
@Test
public void testWithEncryption() {
assertEquals("SHA512withRSA", SignatureAlgorithmConverter.toJavaName("sha512WithRSAEncryption"));
}
@Test
public void testWithDash() {
assertEquals("SHA256withECDSA", SignatureAlgorithmConverter.toJavaName("ecdsa-with-SHA256"));
}
@Test
public void testWithUnderscore() {
assertEquals("SHA256withDSA", SignatureAlgorithmConverter.toJavaName("dsa_with_SHA256"));
}
@Test
public void testInvalid() {
assertNull(SignatureAlgorithmConverter.toJavaName("ThisIsSomethingInvalid"));
}
}

View File

@ -64,6 +64,7 @@ import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -166,11 +167,11 @@ final class SniClientJava8TestUtil {
}
}
static void assertSSLSession(SSLSession session, String name) {
assertSSLSession(session, new SNIHostName(name));
static void assertSSLSession(boolean clientSide, SSLSession session, String name) {
assertSSLSession(clientSide, session, new SNIHostName(name));
}
private static void assertSSLSession(SSLSession session, SNIServerName name) {
private static void assertSSLSession(boolean clientSide, SSLSession session, SNIServerName name) {
Assert.assertNotNull(session);
if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extendedSSLSession = (ExtendedSSLSession) session;
@ -178,6 +179,17 @@ final class SniClientJava8TestUtil {
Assert.assertEquals(1, names.size());
Assert.assertEquals(name, names.get(0));
Assert.assertTrue(extendedSSLSession.getLocalSupportedSignatureAlgorithms().length > 0);
if (clientSide) {
Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length);
} else {
if (session instanceof OpenSslSession && OpenSslTestUtils.isBoringSSL()) {
// BoringSSL does not support SSL_get_sigalgs(...)
// https://boringssl.googlesource.com/boringssl/+/ba16a1e405c617f4179bd780ad15522fb25b0a65%5E%21/
Assert.assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length);
} else {
Assert.assertTrue(extendedSSLSession.getPeerSupportedSignatureAlgorithms().length > 0);
}
}
}
}
@ -227,7 +239,7 @@ final class SniClientJava8TestUtil {
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine)
throws CertificateException {
assertSSLSession(sslEngine.getHandshakeSession(), name);
assertSSLSession(sslEngine.getUseClientMode(), sslEngine.getHandshakeSession(), name);
}
@Override
@ -323,7 +335,7 @@ final class SniClientJava8TestUtil {
SSLEngine sslEngine) {
SSLSession session = sslEngine.getHandshakeSession();
assertSSLSession(session, name);
assertSSLSession(sslEngine.getUseClientMode(), session, name);
return ((X509ExtendedKeyManager) km)
.chooseEngineServerAlias(s, principals, sslEngine);
}

View File

@ -155,7 +155,8 @@ public class SniClientTest {
Assert.assertNull(handler.engine().getHandshakeSession());
if (PlatformDependent.javaVersion() >= 8) {
SniClientJava8TestUtil.assertSSLSession(handler.engine().getSession(), sniHostName);
SniClientJava8TestUtil.assertSSLSession(
handler.engine().getUseClientMode(), handler.engine().getSession(), sniHostName);
}
} finally {
if (cc != null) {

View File

@ -204,7 +204,7 @@ public class SslErrorTest {
verifyException(unwrappedCause, "expired", promise);
} else if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) {
// BoringSSL uses "expired" in this case while others use "bad"
if ("BoringSSL".equals(OpenSsl.versionString())) {
if (OpenSslTestUtils.isBoringSSL()) {
verifyException(unwrappedCause, "expired", promise);
} else {
verifyException(unwrappedCause, "bad", promise);
@ -216,7 +216,7 @@ public class SslErrorTest {
verifyException(unwrappedCause, "expired", promise);
} else if (exception instanceof CertificateNotYetValidException) {
// BoringSSL uses "expired" in this case while others use "bad"
if ("BoringSSL".equals(OpenSsl.versionString())) {
if (OpenSslTestUtils.isBoringSSL()) {
verifyException(unwrappedCause, "expired", promise);
} else {
verifyException(unwrappedCause, "bad", promise);

View File

@ -241,7 +241,7 @@
<!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency -->
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
<tcnative.artifactId>netty-tcnative</tcnative.artifactId>
<tcnative.version>2.0.15.Final</tcnative.version>
<tcnative.version>2.0.17.Final</tcnative.version>
<tcnative.classifier>${os.detected.classifier}</tcnative.classifier>
<conscrypt.groupId>org.conscrypt</conscrypt.groupId>
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>