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:
parent
11ec7d892e
commit
d1654484c1
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user