ReferenceCountedOpenSslEngines SSLSession must provide local certific… (#8918)

Motivation:

The SSLSession that is returned by SSLEngine.getHandshakeSession() must be able to provide the local certificates when the TrustManager is invoked on the server-side.

Modifications:

- Correctly return the local certificates
- Add unit test

Result:

Be able to obtain local certificates from handshake SSLSession during verification on the server side.
This commit is contained in:
Norman Maurer 2019-03-08 06:47:28 +01:00
parent 10f06a04e6
commit 7e76d02fe7
8 changed files with 192 additions and 3 deletions

View File

@ -2002,7 +2002,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
// thread. // thread.
private X509Certificate[] x509PeerCerts; private X509Certificate[] x509PeerCerts;
private Certificate[] peerCerts; private Certificate[] peerCerts;
private Certificate[] localCerts;
private String protocol; private String protocol;
private String cipher; private String cipher;
@ -2149,7 +2148,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
id = SSL.getSessionId(ssl); id = SSL.getSessionId(ssl);
cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl)); cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
protocol = SSL.getVersion(ssl); protocol = SSL.getVersion(ssl);
localCerts = localCertificateChain;
initPeerCerts(); initPeerCerts();
selectApplicationProtocol(); selectApplicationProtocol();
@ -2284,6 +2282,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
@Override @Override
public Certificate[] getLocalCertificates() { public Certificate[] getLocalCertificates() {
Certificate[] localCerts = ReferenceCountedOpenSslEngine.this.localCertificateChain;
if (localCerts == null) { if (localCerts == null) {
return null; return null;
} }
@ -2310,7 +2309,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
@Override @Override
public Principal getLocalPrincipal() { public Principal getLocalPrincipal() {
Certificate[] local = localCerts; Certificate[] local = ReferenceCountedOpenSslEngine.this.localCertificateChain;
if (local == null || local.length == 0) { if (local == null || local.length == 0) {
return null; return null;
} }

View File

@ -87,4 +87,11 @@ public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest {
// Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent. // Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent.
// See https://github.com/google/conscrypt/issues/593 // See https://github.com/google/conscrypt/issues/593
} }
@Ignore("Ignore due bug in Conscrypt")
@Override
public void testHandshakeSession() throws Exception {
// Ignore as Conscrypt does not correctly return the local certificates while the TrustManager is invoked.
// See https://github.com/google/conscrypt/issues/634
}
} }

View File

@ -85,4 +85,11 @@ public class ConscryptSslEngineTest extends SSLEngineTest {
// Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent. // Ignore due bug in Conscrypt where the incorrect SSLSession object is used in the SSLSessionBindingEvent.
// See https://github.com/google/conscrypt/issues/593 // See https://github.com/google/conscrypt/issues/593
} }
@Ignore("Ignore due bug in Conscrypt")
@Override
public void testHandshakeSession() throws Exception {
// Ignore as Conscrypt does not correctly return the local certificates while the TrustManager is invoked.
// See https://github.com/google/conscrypt/issues/634
}
} }

View File

@ -85,4 +85,11 @@ public class JdkConscryptSslEngineInteropTest extends SSLEngineTest {
// TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException.
return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause);
} }
@Ignore("Ignore due bug in Conscrypt")
@Override
public void testHandshakeSession() throws Exception {
// Ignore as Conscrypt does not correctly return the local certificates while the TrustManager is invoked.
// See https://github.com/google/conscrypt/issues/634
}
} }

View File

@ -128,6 +128,12 @@ public class JdkOpenSslEngineInteroptTest extends SSLEngineTest {
return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause); return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause);
} }
@Override
public void testHandshakeSession() throws Exception {
checkShouldUseKeyManagerFactory();
super.testHandshakeSession();
}
@Override @Override
protected SSLEngine wrapEngine(SSLEngine engine) { protected SSLEngine wrapEngine(SSLEngine engine) {
return Java8SslTestUtils.wrapSSLEngineForTesting(engine); return Java8SslTestUtils.wrapSSLEngineForTesting(engine);

View File

@ -161,6 +161,12 @@ public class OpenSslEngineTest extends SSLEngineTest {
super.testClientHostnameValidationFail(); super.testClientHostnameValidationFail();
} }
@Override
public void testHandshakeSession() throws Exception {
checkShouldUseKeyManagerFactory();
super.testHandshakeSession();
}
private static boolean isNpnSupported(String versionString) { private static boolean isNpnSupported(String versionString) {
String[] versionStringParts = versionString.split(" ", -1); String[] versionStringParts = versionString.split(" ", -1);
if (versionStringParts.length == 2 && "LibreSSL".equals(versionStringParts[0])) { if (versionStringParts.length == 2 && "LibreSSL".equals(versionStringParts[0])) {

View File

@ -127,6 +127,12 @@ public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest {
return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause); return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause);
} }
@Override
public void testHandshakeSession() throws Exception {
checkShouldUseKeyManagerFactory();
super.testHandshakeSession();
}
@Override @Override
protected SSLEngine wrapEngine(SSLEngine engine) { protected SSLEngine wrapEngine(SSLEngine engine) {
return Java8SslTestUtils.wrapSSLEngineForTesting(engine); return Java8SslTestUtils.wrapSSLEngineForTesting(engine);

View File

@ -37,6 +37,7 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.ssl.util.SimpleTrustManagerFactory;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
@ -71,18 +72,24 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManagerFactorySpi; import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate; import javax.security.cert.X509Certificate;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal; import java.security.Principal;
import java.security.Provider; import java.security.Provider;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.ArrayList; import java.util.ArrayList;
@ -107,6 +114,7 @@ import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2;
import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3; import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3;
import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -3060,6 +3068,149 @@ public abstract class SSLEngineTest {
} }
} }
@Test
public void testHandshakeSession() throws Exception {
final SelfSignedCertificate ssc = new SelfSignedCertificate();
final TestTrustManagerFactory clientTmf = new TestTrustManagerFactory(ssc.cert());
final TestTrustManagerFactory serverTmf = new TestTrustManagerFactory(ssc.cert());
clientSslCtx = SslContextBuilder.forClient()
.trustManager(new SimpleTrustManagerFactory() {
@Override
protected void engineInit(KeyStore keyStore) {
// NOOP
}
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters) {
// NOOP
}
@Override
protected TrustManager[] engineGetTrustManagers() {
return new TrustManager[] { clientTmf };
}
})
.keyManager(newKeyManagerFactory(ssc))
.sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.build();
serverSslCtx = SslContextBuilder.forServer(newKeyManagerFactory(ssc))
.trustManager(new SimpleTrustManagerFactory() {
@Override
protected void engineInit(KeyStore keyStore) {
// NOOP
}
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters) {
// NOOP
}
@Override
protected TrustManager[] engineGetTrustManagers() {
return new TrustManager[] { serverTmf };
}
})
.sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.clientAuth(ClientAuth.REQUIRE)
.build();
SSLEngine clientEngine = null;
SSLEngine serverEngine = null;
try {
clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
handshake(clientEngine, serverEngine);
assertTrue(clientTmf.isVerified());
assertTrue(serverTmf.isVerified());
} finally {
cleanupClientSslEngine(clientEngine);
cleanupServerSslEngine(serverEngine);
ssc.delete();
}
}
private KeyManagerFactory newKeyManagerFactory(SelfSignedCertificate ssc)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
return SslContext.buildKeyManagerFactory(
new java.security.cert.X509Certificate[] { ssc.cert() }, ssc.key(), null, null);
}
private final class TestTrustManagerFactory extends X509ExtendedTrustManager {
private final Certificate localCert;
private volatile boolean verified;
TestTrustManagerFactory(Certificate localCert) {
this.localCert = localCert;
}
boolean isVerified() {
return verified;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s, Socket socket) {
fail();
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s, Socket socket) {
fail();
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) {
verified = true;
assertFalse(sslEngine.getUseClientMode());
SSLSession session = sslEngine.getHandshakeSession();
assertNotNull(session);
Certificate[] localCertificates = session.getLocalCertificates();
assertNotNull(localCertificates);
assertEquals(1, localCertificates.length);
assertEquals(localCert, localCertificates[0]);
assertNotNull(session.getLocalPrincipal());
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) {
verified = true;
assertTrue(sslEngine.getUseClientMode());
SSLSession session = sslEngine.getHandshakeSession();
assertNotNull(session);
assertNull(session.getLocalCertificates());
assertNull(session.getLocalPrincipal());
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s) {
fail();
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s) {
fail();
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return EmptyArrays.EMPTY_X509_CERTIFICATES;
}
}
protected SSLEngine wrapEngine(SSLEngine engine) { protected SSLEngine wrapEngine(SSLEngine engine) {
return engine; return engine;
} }