Correctly convert between openssl / boringssl and java cipher names when using TLSv1.3 (#8485)

Motivation:

We did not correctly convert between openssl / boringssl and java ciphers when using TLV1.3 which had different effects when either using openssl or boringssl.
 - When using openssl and TLSv1.3 we always returned SSL_NULL_WITH_NULL_NULL as cipher name
 - When using boringssl with TLSv1.3 we always returned an incorrect constructed cipher name which does not match what is defined by Java.

Modifications:

 - Add correct mappings in CipherSuiteConverter for both openssl and boringssl
 - Add unit tests for CipherSuiteConvert
 - Add unit in SSLEngine which checks that we do not return SSL_NULL_WITH_NULL_NULL ever and that server and client returns the same cipher name.

Result:

Fixes https://github.com/netty/netty/issues/8477.
This commit is contained in:
Norman Maurer 2018-11-14 08:49:13 +01:00 committed by GitHub
parent 11ec7d892e
commit d1654484c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 229 additions and 61 deletions

View File

@ -20,12 +20,15 @@ import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.Collections.singletonMap;
/**
* Converts a Java cipher suite string to an OpenSSL cipher suite string and vice versa.
*
@ -95,6 +98,26 @@ final class CipherSuiteConverter {
*/
private static final ConcurrentMap<String, Map<String, String>> o2j = PlatformDependent.newConcurrentHashMap();
private static final Map<String, String> j2oTls13;
private static final Map<String, Map<String, String>> o2jTls13;
static {
Map<String, String> j2oTls13Map = new HashMap<String, String>();
j2oTls13Map.put("TLS_AES_128_GCM_SHA256", "AEAD-AES128-GCM-SHA256");
j2oTls13Map.put("TLS_AES_256_GCM_SHA384", "AEAD-AES256-GCM-SHA384");
j2oTls13Map.put("TLS_CHACHA20_POLY1305_SHA256", "AEAD-CHACHA20-POLY1305-SHA256");
j2oTls13 = Collections.unmodifiableMap(j2oTls13Map);
Map<String, Map<String, String>> o2jTls13Map = new HashMap<String, Map<String, String>>();
o2jTls13Map.put("TLS_AES_128_GCM_SHA256", singletonMap("TLS", "TLS_AES_128_GCM_SHA256"));
o2jTls13Map.put("TLS_AES_256_GCM_SHA384", singletonMap("TLS", "TLS_AES_256_GCM_SHA384"));
o2jTls13Map.put("TLS_CHACHA20_POLY1305_SHA256", singletonMap("TLS", "TLS_CHACHA20_POLY1305_SHA256"));
o2jTls13Map.put("AEAD-AES128-GCM-SHA256", singletonMap("TLS", "TLS_AES_128_GCM_SHA256"));
o2jTls13Map.put("AEAD-AES256-GCM-SHA384", singletonMap("TLS", "TLS_AES_256_GCM_SHA384"));
o2jTls13Map.put("AEAD-CHACHA20-POLY1305-SHA256", singletonMap("TLS", "TLS_CHACHA20_POLY1305_SHA256"));
o2jTls13 = Collections.unmodifiableMap(o2jTls13Map);
}
/**
* Clears the cache for testing purpose.
*/
@ -127,17 +150,21 @@ final class CipherSuiteConverter {
*
* @return {@code null} if the conversion has failed
*/
static String toOpenSsl(String javaCipherSuite) {
static String toOpenSsl(String javaCipherSuite, boolean boringSSL) {
String converted = j2o.get(javaCipherSuite);
if (converted != null) {
return converted;
} else {
return cacheFromJava(javaCipherSuite);
}
return cacheFromJava(javaCipherSuite, boringSSL);
}
private static String cacheFromJava(String javaCipherSuite) {
String openSslCipherSuite = toOpenSslUncached(javaCipherSuite);
private static String cacheFromJava(String javaCipherSuite, boolean boringSSL) {
String converted = j2oTls13.get(javaCipherSuite);
if (converted != null) {
return boringSSL ? converted : javaCipherSuite;
}
String openSslCipherSuite = toOpenSslUncached(javaCipherSuite, boringSSL);
if (openSslCipherSuite == null) {
return null;
}
@ -158,7 +185,12 @@ final class CipherSuiteConverter {
return openSslCipherSuite;
}
static String toOpenSslUncached(String javaCipherSuite) {
static String toOpenSslUncached(String javaCipherSuite, boolean boringSSL) {
String converted = j2oTls13.get(javaCipherSuite);
if (converted != null) {
return boringSSL ? converted : javaCipherSuite;
}
Matcher m = JAVA_CIPHERSUITE_PATTERN.matcher(javaCipherSuite);
if (!m.matches()) {
return null;
@ -260,14 +292,23 @@ final class CipherSuiteConverter {
String javaCipherSuite = p2j.get(protocol);
if (javaCipherSuite == null) {
javaCipherSuite = protocol + '_' + p2j.get("");
String cipher = p2j.get("");
if (cipher == null) {
return null;
}
javaCipherSuite = protocol + '_' + cipher;
}
return javaCipherSuite;
}
private static Map<String, String> cacheFromOpenSsl(String openSslCipherSuite) {
String javaCipherSuiteSuffix = toJavaUncached(openSslCipherSuite);
Map<String, String> converted = o2jTls13.get(openSslCipherSuite);
if (converted != null) {
return converted;
}
String javaCipherSuiteSuffix = toJavaUncached0(openSslCipherSuite, false);
if (javaCipherSuiteSuffix == null) {
return null;
}
@ -293,6 +334,17 @@ final class CipherSuiteConverter {
}
static String toJavaUncached(String openSslCipherSuite) {
return toJavaUncached0(openSslCipherSuite, true);
}
private static String toJavaUncached0(String openSslCipherSuite, boolean checkTls13) {
if (checkTls13) {
Map<String, String> converted = o2jTls13.get(openSslCipherSuite);
if (converted != null) {
return converted.get("TLS");
}
}
Matcher m = OPENSSL_CIPHERSUITE_PATTERN.matcher(openSslCipherSuite);
if (!m.matches()) {
return null;
@ -402,14 +454,14 @@ final class CipherSuiteConverter {
* guaranteed that at least one of the {@link StringBuilder}s contain some ciphers that can be used to configure
* OpenSSL.
*/
static void convertToCipherStrings(
Iterable<String> cipherSuites, StringBuilder cipherBuilder, StringBuilder cipherTLSv13Builder) {
static void convertToCipherStrings(Iterable<String> cipherSuites, StringBuilder cipherBuilder,
StringBuilder cipherTLSv13Builder, boolean boringSSL) {
for (String c: cipherSuites) {
if (c == null) {
break;
}
String converted = toOpenSsl(c);
String converted = toOpenSsl(c, boringSSL);
if (converted == null) {
converted = c;
}
@ -418,7 +470,7 @@ final class CipherSuiteConverter {
throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')');
}
if (SslUtils.isTLSv13Cipher(converted)) {
if (SslUtils.isTLSv13Cipher(converted) || SslUtils.isTLSv13Cipher(c)) {
cipherTLSv13Builder.append(converted);
cipherTLSv13Builder.append(':');
} else {

View File

@ -61,11 +61,6 @@ public final class OpenSsl {
private static final boolean SUPPORTS_HOSTNAME_VALIDATION;
private static final boolean USE_KEYMANAGER_FACTORY;
private static final boolean SUPPORTS_OCSP;
private static final String TLSV13_CIPHERS = "TLS_AES_256_GCM_SHA384" + ':' +
"TLS_CHACHA20_POLY1305_SHA256" + ':' +
"TLS_AES_128_GCM_SHA256" + ':' +
"TLS_AES_128_CCM_8_SHA256" + ':' +
"TLS_AES_128_CCM_SHA256";
private static final boolean TLSV13_SUPPORTED;
private static final boolean IS_BORINGSSL;
static final Set<String> SUPPORTED_PROTOCOLS_SET;
@ -149,8 +144,22 @@ public final class OpenSsl {
long certBio = 0;
try {
try {
SSLContext.setCipherSuite(sslCtx, TLSV13_CIPHERS, true);
tlsv13Supported = true;
StringBuilder tlsv13Ciphers = new StringBuilder();
for (String cipher: TLSV13_CIPHERS) {
String converted = CipherSuiteConverter.toOpenSsl(cipher, IS_BORINGSSL);
if (converted != null) {
tlsv13Ciphers.append(converted).append(':');
}
}
if (tlsv13Ciphers.length() == 0) {
tlsv13Supported = false;
} else {
tlsv13Ciphers.setLength(tlsv13Ciphers.length() - 1);
SSLContext.setCipherSuite(sslCtx, tlsv13Ciphers.toString() , true);
tlsv13Supported = true;
}
} catch (Exception ignore) {
tlsv13Supported = false;
}
@ -174,7 +183,10 @@ public final class OpenSsl {
Collections.addAll(availableOpenSslCipherSuites,
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384" ,
"TLS_CHACHA20_POLY1305_SHA256");
"TLS_CHACHA20_POLY1305_SHA256",
"AEAD-AES128-GCM-SHA256",
"AEAD-AES256-GCM-SHA384",
"AEAD-CHACHA20-POLY1305-SHA256");
}
try {
SSL.setHostNameValidation(ssl, 0, "netty.io");
@ -461,7 +473,7 @@ public final class OpenSsl {
* Both Java-style cipher suite and OpenSSL-style cipher suite are accepted.
*/
public static boolean isCipherSuiteAvailable(String cipherSuite) {
String converted = CipherSuiteConverter.toOpenSsl(cipherSuite);
String converted = CipherSuiteConverter.toOpenSsl(cipherSuite, IS_BORINGSSL);
if (converted != null) {
cipherSuite = converted;
}

View File

@ -250,7 +250,7 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
}
} else {
CipherSuiteConverter.convertToCipherStrings(
unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder);
unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder, OpenSsl.isBoringSSL());
// Set non TLSv1.3 ciphers.
SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false);

View File

@ -134,7 +134,6 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
private static final AtomicIntegerFieldUpdater<ReferenceCountedOpenSslEngine> DESTROYED_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed");
private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
private static final SSLEngineResult NEED_UNWRAP_OK = new SSLEngineResult(OK, NEED_UNWRAP, 0, 0);
private static final SSLEngineResult NEED_UNWRAP_CLOSED = new SSLEngineResult(CLOSED, NEED_UNWRAP, 0, 0);
private static final SSLEngineResult NEED_WRAP_OK = new SSLEngineResult(OK, NEED_WRAP, 0, 0);
@ -1409,7 +1408,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
final StringBuilder buf = new StringBuilder();
final StringBuilder bufTLSv13 = new StringBuilder();
CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13);
CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13, OpenSsl.isBoringSSL());
final String cipherSuiteSpec = buf.toString();
final String cipherSuiteSpecTLSv13 = bufTLSv13.toString();
@ -1715,7 +1714,8 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
return null;
}
String prefix = toJavaCipherSuitePrefix(SSL.getVersion(ssl));
String version = SSL.getVersion(ssl);
String prefix = toJavaCipherSuitePrefix(version);
return CipherSuiteConverter.toJava(openSslCipherSuite, prefix);
}
@ -2238,7 +2238,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
public String getCipherSuite() {
synchronized (ReferenceCountedOpenSslEngine.this) {
if (cipher == null) {
return INVALID_CIPHER;
return SslUtils.INVALID_CIPHER;
}
return cipher;
}

View File

@ -29,7 +29,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@ -42,7 +42,7 @@ import static java.util.Arrays.asList;
*/
final class SslUtils {
// See https://tools.ietf.org/html/rfc8446#appendix-B.4
private static final Set<String> TLSV13_CIPHERS = Collections.unmodifiableSet(new HashSet<String>(
static final Set<String> TLSV13_CIPHERS = Collections.unmodifiableSet(new LinkedHashSet<String>(
asList("TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256",
"TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_8_SHA256",
"TLS_AES_128_CCM_SHA256")));
@ -55,6 +55,8 @@ final class SslUtils {
static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
static final String PROTOCOL_TLS_V1_3 = "TLSv1.3";
static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
/**
* change cipher spec
*/

View File

@ -122,10 +122,14 @@ public class CipherSuiteConverterTest {
testJ2OMapping("TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-PSK-CHACHA20-POLY1305");
testJ2OMapping("TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "DHE-PSK-CHACHA20-POLY1305");
testJ2OMapping("TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", "RSA-PSK-CHACHA20-POLY1305");
testJ2OMapping("TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256");
testJ2OMapping("TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384");
testJ2OMapping("TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256");
}
private static void testJ2OMapping(String javaCipherSuite, String openSslCipherSuite) {
final String actual = CipherSuiteConverter.toOpenSslUncached(javaCipherSuite);
final String actual = CipherSuiteConverter.toOpenSslUncached(javaCipherSuite, false);
logger.info("{} => {}", javaCipherSuite, actual);
assertThat(actual, is(openSslCipherSuite));
}
@ -316,15 +320,18 @@ public class CipherSuiteConverterTest {
private static void testUnknownJavaCiphersToOpenSSL(String javaCipherSuite) {
CipherSuiteConverter.clearCache();
assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite));
assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite));
assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite, false));
assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite, true));
}
private static void testCachedJ2OMapping(String javaCipherSuite, String openSslCipherSuite) {
CipherSuiteConverter.clearCache();
final String actual1 = CipherSuiteConverter.toOpenSsl(javaCipherSuite);
// For TLSv1.3 this should make no diffierence if boringSSL is true or false
final String actual1 = CipherSuiteConverter.toOpenSsl(javaCipherSuite, false);
assertThat(actual1, is(openSslCipherSuite));
final String actual2 = CipherSuiteConverter.toOpenSsl(javaCipherSuite, true);
assertEquals(actual1, actual2);
// Ensure that the cache entries have been created.
assertThat(CipherSuiteConverter.isJ2OCached(javaCipherSuite, actual1), is(true));
@ -332,12 +339,12 @@ public class CipherSuiteConverterTest {
assertThat(CipherSuiteConverter.isO2JCached(actual1, "SSL", "SSL_" + javaCipherSuite.substring(4)), is(true));
assertThat(CipherSuiteConverter.isO2JCached(actual1, "TLS", "TLS_" + javaCipherSuite.substring(4)), is(true));
final String actual2 = CipherSuiteConverter.toOpenSsl(javaCipherSuite);
assertThat(actual2, is(openSslCipherSuite));
final String actual3 = CipherSuiteConverter.toOpenSsl(javaCipherSuite, false);
assertThat(actual3, is(openSslCipherSuite));
// Test if the returned cipher strings are identical,
// so that the TLS sessions with the same cipher suite do not create many strings.
assertThat(actual1, is(sameInstance(actual2)));
assertThat(actual1, is(sameInstance(actual3)));
}
@Test
@ -373,4 +380,34 @@ public class CipherSuiteConverterTest {
assertThat(tlsActual1, is(sameInstance(tlsActual2)));
assertThat(sslActual1, is(sameInstance(sslActual2)));
}
@Test
public void testTlsv13Mappings() {
CipherSuiteConverter.clearCache();
assertEquals("TLS_AES_128_GCM_SHA256",
CipherSuiteConverter.toJava("TLS_AES_128_GCM_SHA256", "TLS"));
assertNull(CipherSuiteConverter.toJava("TLS_AES_128_GCM_SHA256", "SSL"));
assertEquals("TLS_AES_256_GCM_SHA384",
CipherSuiteConverter.toJava("TLS_AES_256_GCM_SHA384", "TLS"));
assertNull(CipherSuiteConverter.toJava("TLS_AES_256_GCM_SHA384", "SSL"));
assertEquals("TLS_CHACHA20_POLY1305_SHA256",
CipherSuiteConverter.toJava("TLS_CHACHA20_POLY1305_SHA256", "TLS"));
assertNull(CipherSuiteConverter.toJava("TLS_CHACHA20_POLY1305_SHA256", "SSL"));
// BoringSSL use different cipher naming then OpenSSL so we need to test for both
assertEquals("TLS_AES_128_GCM_SHA256",
CipherSuiteConverter.toOpenSsl("TLS_AES_128_GCM_SHA256", false));
assertEquals("TLS_AES_256_GCM_SHA384",
CipherSuiteConverter.toOpenSsl("TLS_AES_256_GCM_SHA384", false));
assertEquals("TLS_CHACHA20_POLY1305_SHA256",
CipherSuiteConverter.toOpenSsl("TLS_CHACHA20_POLY1305_SHA256", false));
assertEquals("AEAD-AES128-GCM-SHA256",
CipherSuiteConverter.toOpenSsl("TLS_AES_128_GCM_SHA256", true));
assertEquals("AEAD-AES256-GCM-SHA384",
CipherSuiteConverter.toOpenSsl("TLS_AES_256_GCM_SHA384", true));
assertEquals("AEAD-CHACHA20-POLY1305-SHA256",
CipherSuiteConverter.toOpenSsl("TLS_CHACHA20_POLY1305_SHA256", true));
}
}

View File

@ -497,13 +497,16 @@ public abstract class SSLEngineTest {
}
@Test
public void testMutualAuthSameCerts() throws Exception {
public void testMutualAuthSameCerts() throws Throwable {
mySetupMutualAuth(new File(getClass().getResource("test_unencrypted.pem").getFile()),
new File(getClass().getResource("test.crt").getFile()),
null);
runTest(null);
assertTrue(serverLatch.await(2, TimeUnit.SECONDS));
assertNull(serverException);
Throwable cause = serverException;
if (cause != null) {
throw cause;
}
}
@Test
@ -931,9 +934,43 @@ public abstract class SSLEngineTest {
mySetupMutualAuth(crtFile, keyFile, crtFile, keyPassword, crtFile, keyFile, crtFile, keyPassword);
}
private void verifySSLSessionForMutualAuth(SSLSession session, File certFile, String principalName)
throws Exception {
InputStream in = null;
try {
assertEquals(principalName, session.getLocalPrincipal().getName());
assertEquals(principalName, session.getPeerPrincipal().getName());
assertNotNull(session.getId());
assertEquals(protocolCipherCombo.cipher, session.getCipherSuite());
assertEquals(protocolCipherCombo.protocol, session.getProtocol());
assertTrue(session.getApplicationBufferSize() > 0);
assertTrue(session.getCreationTime() > 0);
assertTrue(session.isValid());
assertTrue(session.getLastAccessedTime() > 0);
in = new FileInputStream(certFile);
final byte[] certBytes = SslContext.X509_CERT_FACTORY
.generateCertificate(in).getEncoded();
// Verify session
assertEquals(1, session.getPeerCertificates().length);
assertArrayEquals(certBytes, session.getPeerCertificates()[0].getEncoded());
assertEquals(1, session.getPeerCertificateChain().length);
assertArrayEquals(certBytes, session.getPeerCertificateChain()[0].getEncoded());
assertEquals(1, session.getLocalCertificates().length);
assertArrayEquals(certBytes, session.getLocalCertificates()[0].getEncoded());
} finally {
if (in != null) {
in.close();
}
}
}
private void mySetupMutualAuth(
File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword,
File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword)
File clientTrustCrtFile, File clientKeyFile, final File clientCrtFile, String clientKeyPassword)
throws InterruptedException, SSLException {
serverSslCtx =
SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword)
@ -969,7 +1006,7 @@ public abstract class SSLEngineTest {
ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type));
ChannelPipeline p = ch.pipeline();
SSLEngine engine = wrapEngine(serverSslCtx.newEngine(ch.alloc()));
final SSLEngine engine = wrapEngine(serverSslCtx.newEngine(ch.alloc()));
engine.setUseClientMode(false);
engine.setNeedClientAuth(true);
@ -991,27 +1028,8 @@ public abstract class SSLEngineTest {
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt == SslHandshakeCompletionEvent.SUCCESS) {
try {
InputStream in = new FileInputStream(serverCrtFile);
try {
final byte[] cert = SslContext.X509_CERT_FACTORY
.generateCertificate(in).getEncoded();
// Verify session
SSLSession session = ctx.pipeline().get(SslHandler.class).engine().getSession();
assertEquals(1, session.getPeerCertificates().length);
assertArrayEquals(cert, session.getPeerCertificates()[0].getEncoded());
assertEquals(1, session.getPeerCertificateChain().length);
assertArrayEquals(cert, session.getPeerCertificateChain()[0].getEncoded());
assertEquals(1, session.getLocalCertificates().length);
assertArrayEquals(cert, session.getLocalCertificates()[0].getEncoded());
assertEquals(PRINCIPAL_NAME, session.getLocalPrincipal().getName());
assertEquals(PRINCIPAL_NAME, session.getPeerPrincipal().getName());
} finally {
in.close();
}
verifySSLSessionForMutualAuth(
engine.getSession(), serverCrtFile, PRINCIPAL_NAME);
} catch (Throwable cause) {
serverException = cause;
}
@ -1029,12 +1047,24 @@ public abstract class SSLEngineTest {
protected void initChannel(Channel ch) throws Exception {
ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type));
SslHandler handler = clientSslCtx.newHandler(ch.alloc());
final SslHandler handler = clientSslCtx.newHandler(ch.alloc());
handler.engine().setNeedClientAuth(true);
ChannelPipeline p = ch.pipeline();
p.addLast(handler);
p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch));
p.addLast(new ChannelInboundHandlerAdapter() {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt == SslHandshakeCompletionEvent.SUCCESS) {
try {
verifySSLSessionForMutualAuth(
handler.engine().getSession(), clientCrtFile, PRINCIPAL_NAME);
} catch (Throwable cause) {
clientException = cause;
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause.getCause() instanceof SSLHandshakeException) {
@ -2661,6 +2691,41 @@ public abstract class SSLEngineTest {
}
}
@Test
public void testGetCiphersuite() throws Exception {
clientSslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.sslProvider(sslClientProvider())
.sslContextProvider(clientSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.build();
SelfSignedCertificate ssc = new SelfSignedCertificate();
serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.sslProvider(sslServerProvider())
.sslContextProvider(serverSslContextProvider())
.protocols(protocols())
.ciphers(ciphers())
.build();
SSLEngine clientEngine = null;
SSLEngine serverEngine = null;
try {
clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT));
handshake(clientEngine, serverEngine);
String clientCipher = clientEngine.getSession().getCipherSuite();
String serverCipher = serverEngine.getSession().getCipherSuite();
assertEquals(clientCipher, serverCipher);
assertEquals(protocolCipherCombo.cipher, clientCipher);
} finally {
cleanupClientSslEngine(clientEngine);
cleanupServerSslEngine(serverEngine);
ssc.delete();
}
}
protected SSLEngine wrapEngine(SSLEngine engine) {
return engine;
}