Correctly include TLS1.3 ciphers in the enabled ciphersuites when using BoringSSL (#10388)
Motivation: BoringSSL behaves differently then OpenSSL and not include any TLS1.3 ciphers in the returned array when calling SSL_get_ciphers(...). This is due the fact that it also not allow to explicit configure which are supported and which not for TLS1.3. To mimic the behaviour expected by the SSLEngine API we should workaround this. Modifications: - Add a unit test that verifies enabled protocols / ciphers - Add special handling for BoringSSL and tls1.3 Result: Make behaviour consistent
This commit is contained in:
parent
523dc5c269
commit
cbe238a95b
@ -26,6 +26,7 @@ import io.netty.internal.tcnative.SSLContext;
|
|||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
|
import io.netty.util.internal.EmptyArrays;
|
||||||
import io.netty.util.internal.NativeLibraryLoader;
|
import io.netty.util.internal.NativeLibraryLoader;
|
||||||
import io.netty.util.internal.PlatformDependent;
|
import io.netty.util.internal.PlatformDependent;
|
||||||
import io.netty.util.internal.SystemPropertyUtil;
|
import io.netty.util.internal.SystemPropertyUtil;
|
||||||
@ -62,6 +63,7 @@ public final class OpenSsl {
|
|||||||
private static final boolean TLSV13_SUPPORTED;
|
private static final boolean TLSV13_SUPPORTED;
|
||||||
private static final boolean IS_BORINGSSL;
|
private static final boolean IS_BORINGSSL;
|
||||||
static final Set<String> SUPPORTED_PROTOCOLS_SET;
|
static final Set<String> SUPPORTED_PROTOCOLS_SET;
|
||||||
|
static final String[] EXTRA_SUPPORTED_TLS_1_3_CIPHERS;
|
||||||
|
|
||||||
// self-signed certificate for netty.io and the matching private-key
|
// self-signed certificate for netty.io and the matching private-key
|
||||||
private static final String CERT = "-----BEGIN CERTIFICATE-----\n" +
|
private static final String CERT = "-----BEGIN CERTIFICATE-----\n" +
|
||||||
@ -177,6 +179,13 @@ public final class OpenSsl {
|
|||||||
boolean tlsv13Supported = false;
|
boolean tlsv13Supported = false;
|
||||||
|
|
||||||
IS_BORINGSSL = "BoringSSL".equals(versionString());
|
IS_BORINGSSL = "BoringSSL".equals(versionString());
|
||||||
|
if (IS_BORINGSSL) {
|
||||||
|
EXTRA_SUPPORTED_TLS_1_3_CIPHERS = new String [] { "TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384" ,
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256" };
|
||||||
|
} else {
|
||||||
|
EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
||||||
@ -222,10 +231,8 @@ public final class OpenSsl {
|
|||||||
if (IS_BORINGSSL) {
|
if (IS_BORINGSSL) {
|
||||||
// Currently BoringSSL does not include these when calling SSL.getCiphers() even when these
|
// Currently BoringSSL does not include these when calling SSL.getCiphers() even when these
|
||||||
// are supported.
|
// are supported.
|
||||||
|
Collections.addAll(availableOpenSslCipherSuites, EXTRA_SUPPORTED_TLS_1_3_CIPHERS);
|
||||||
Collections.addAll(availableOpenSslCipherSuites,
|
Collections.addAll(availableOpenSslCipherSuites,
|
||||||
"TLS_AES_128_GCM_SHA256",
|
|
||||||
"TLS_AES_256_GCM_SHA384" ,
|
|
||||||
"TLS_CHACHA20_POLY1305_SHA256",
|
|
||||||
"AEAD-AES128-GCM-SHA256",
|
"AEAD-AES128-GCM-SHA256",
|
||||||
"AEAD-AES256-GCM-SHA384",
|
"AEAD-AES256-GCM-SHA384",
|
||||||
"AEAD-CHACHA20-POLY1305-SHA256");
|
"AEAD-CHACHA20-POLY1305-SHA256");
|
||||||
@ -372,6 +379,7 @@ public final class OpenSsl {
|
|||||||
SUPPORTS_OCSP = false;
|
SUPPORTS_OCSP = false;
|
||||||
TLSV13_SUPPORTED = false;
|
TLSV13_SUPPORTED = false;
|
||||||
IS_BORINGSSL = false;
|
IS_BORINGSSL = false;
|
||||||
|
EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +72,6 @@ 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 io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES;
|
|
||||||
import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
import static java.lang.Integer.MAX_VALUE;
|
import static java.lang.Integer.MAX_VALUE;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
@ -1490,10 +1488,17 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String[] getEnabledCipherSuites() {
|
public final String[] getEnabledCipherSuites() {
|
||||||
|
final String[] extraCiphers;
|
||||||
final String[] enabled;
|
final String[] enabled;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!isDestroyed()) {
|
if (!isDestroyed()) {
|
||||||
enabled = SSL.getCiphers(ssl);
|
enabled = SSL.getCiphers(ssl);
|
||||||
|
int opts = SSL.getOptions(ssl);
|
||||||
|
if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, PROTOCOL_TLS_V1_3)) {
|
||||||
|
extraCiphers = OpenSsl.EXTRA_SUPPORTED_TLS_1_3_CIPHERS;
|
||||||
|
} else {
|
||||||
|
extraCiphers = EmptyArrays.EMPTY_STRINGS;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return EmptyArrays.EMPTY_STRINGS;
|
return EmptyArrays.EMPTY_STRINGS;
|
||||||
}
|
}
|
||||||
@ -1501,7 +1506,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
if (enabled == null) {
|
if (enabled == null) {
|
||||||
return EmptyArrays.EMPTY_STRINGS;
|
return EmptyArrays.EMPTY_STRINGS;
|
||||||
} else {
|
} else {
|
||||||
List<String> enabledList = new ArrayList<String>();
|
Set<String> enabledSet = new LinkedHashSet<String>(enabled.length + extraCiphers.length);
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
for (int i = 0; i < enabled.length; i++) {
|
for (int i = 0; i < enabled.length; i++) {
|
||||||
String mapped = toJavaCipherSuite(enabled[i]);
|
String mapped = toJavaCipherSuite(enabled[i]);
|
||||||
@ -1509,10 +1514,11 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
if (!OpenSsl.isTlsv13Supported() && SslUtils.isTLSv13Cipher(cipher)) {
|
if (!OpenSsl.isTlsv13Supported() && SslUtils.isTLSv13Cipher(cipher)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
enabledList.add(cipher);
|
enabledSet.add(cipher);
|
||||||
}
|
}
|
||||||
|
Collections.addAll(enabledSet, extraCiphers);
|
||||||
}
|
}
|
||||||
return enabledList.toArray(new String[0]);
|
return enabledSet.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2254,8 +2260,8 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
byte[][] chain = SSL.getPeerCertChain(ssl);
|
byte[][] chain = SSL.getPeerCertChain(ssl);
|
||||||
if (clientMode) {
|
if (clientMode) {
|
||||||
if (isEmpty(chain)) {
|
if (isEmpty(chain)) {
|
||||||
peerCerts = EMPTY_CERTIFICATES;
|
peerCerts = EmptyArrays.EMPTY_CERTIFICATES;
|
||||||
x509PeerCerts = EMPTY_JAVAX_X509_CERTIFICATES;
|
x509PeerCerts = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
||||||
} else {
|
} else {
|
||||||
peerCerts = new Certificate[chain.length];
|
peerCerts = new Certificate[chain.length];
|
||||||
x509PeerCerts = new X509Certificate[chain.length];
|
x509PeerCerts = new X509Certificate[chain.length];
|
||||||
@ -2269,8 +2275,8 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
|||||||
// See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
|
// See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
|
||||||
byte[] clientCert = SSL.getPeerCertificate(ssl);
|
byte[] clientCert = SSL.getPeerCertificate(ssl);
|
||||||
if (isEmpty(clientCert)) {
|
if (isEmpty(clientCert)) {
|
||||||
peerCerts = EMPTY_CERTIFICATES;
|
peerCerts = EmptyArrays.EMPTY_CERTIFICATES;
|
||||||
x509PeerCerts = EMPTY_JAVAX_X509_CERTIFICATES;
|
x509PeerCerts = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
||||||
} else {
|
} else {
|
||||||
if (isEmpty(chain)) {
|
if (isEmpty(chain)) {
|
||||||
peerCerts = new Certificate[] {new OpenSslX509Certificate(clientCert)};
|
peerCerts = new Certificate[] {new OpenSslX509Certificate(clientCert)};
|
||||||
|
@ -3427,6 +3427,54 @@ public abstract class SSLEngineTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEnabledProtocolsAndCiphers() throws Exception {
|
||||||
|
clientSslCtx = wrapContext(SslContextBuilder.forClient()
|
||||||
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.sslProvider(sslClientProvider())
|
||||||
|
.sslContextProvider(clientSslContextProvider())
|
||||||
|
.protocols(protocols())
|
||||||
|
.ciphers(ciphers())
|
||||||
|
.build());
|
||||||
|
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||||
|
serverSslCtx = wrapContext(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);
|
||||||
|
assertEnabledProtocolsAndCipherSuites(clientEngine);
|
||||||
|
assertEnabledProtocolsAndCipherSuites(serverEngine);
|
||||||
|
} finally {
|
||||||
|
cleanupClientSslEngine(clientEngine);
|
||||||
|
cleanupServerSslEngine(serverEngine);
|
||||||
|
ssc.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertEnabledProtocolsAndCipherSuites(SSLEngine engine) {
|
||||||
|
String protocol = engine.getSession().getProtocol();
|
||||||
|
String cipherSuite = engine.getSession().getCipherSuite();
|
||||||
|
|
||||||
|
assertArrayContains(protocol, engine.getEnabledProtocols());
|
||||||
|
assertArrayContains(cipherSuite, engine.getEnabledCipherSuites());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertArrayContains(String expected, String[] array) {
|
||||||
|
for (String value: array) {
|
||||||
|
if (expected.equals(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("Array did not contain '" + expected + "':" + Arrays.toString(array));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMasterKeyLogging() throws Exception {
|
public void testMasterKeyLogging() throws Exception {
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user