From 6637d7056ef4bacf5a6f023f19f3fea10f52a727 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 21 Sep 2018 16:33:07 +0200 Subject: [PATCH] use conscrypt as security provider to provide tls 1.3 and modern cyphers on old androids --- build.gradle | 1 + .../services/XmppConnectionService.java | 4 +- .../conversations/utils/SSLSocketHelper.java | 103 +++++++++--------- .../conversations/utils/TLSSocketFactory.java | 8 +- .../conversations/xmpp/XmppConnection.java | 4 + 5 files changed, 61 insertions(+), 59 deletions(-) diff --git a/build.gradle b/build.gradle index 8a45b468e..ef403b7ca 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,7 @@ dependencies { implementation 'rocks.xmpp:xmpp-addr:0.8.0' implementation 'org.osmdroid:osmdroid-android:6.0.1' implementation 'org.hsluv:hsluv:0.2' + implementation 'org.conscrypt:conscrypt-android:1.3.0' } ext { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 043991f07..ddc9bd6b5 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -40,12 +40,14 @@ import android.util.Log; import android.util.LruCache; import android.util.Pair; +import org.conscrypt.Conscrypt; import org.openintents.openpgp.IOpenPgpService2; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.net.URL; import java.security.SecureRandom; +import java.security.Security; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -955,7 +957,7 @@ public class XmppConnectionService extends Service { public void onCreate() { OmemoSetting.load(this); ExceptionHelper.init(getApplicationContext()); - PRNGFixes.apply(); + Security.insertProviderAt(Conscrypt.newProvider(), 1); Resolver.init(this); this.mRandom = new SecureRandom(); updateMemorizingTrustmanager(); diff --git a/src/main/java/eu/siacs/conversations/utils/SSLSocketHelper.java b/src/main/java/eu/siacs/conversations/utils/SSLSocketHelper.java index 3a8c1c0aa..da899b872 100644 --- a/src/main/java/eu/siacs/conversations/utils/SSLSocketHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/SSLSocketHelper.java @@ -1,6 +1,6 @@ package eu.siacs.conversations.utils; -import android.os.Build; +import android.util.Log; import java.lang.reflect.Method; import java.security.NoSuchAlgorithmException; @@ -9,65 +9,64 @@ import java.util.Collection; import java.util.LinkedList; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; + public class SSLSocketHelper { - public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException { - final String[] supportProtocols; - final Collection supportedProtocols = new LinkedList<>( - Arrays.asList(sslSocket.getSupportedProtocols())); - supportedProtocols.remove("SSLv3"); - supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]); + public static void setSecurity(final SSLSocket sslSocket) { + final String[] supportProtocols; + final Collection supportedProtocols = new LinkedList<>( + Arrays.asList(sslSocket.getSupportedProtocols())); + supportedProtocols.remove("SSLv3"); + supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]); - sslSocket.setEnabledProtocols(supportProtocols); + sslSocket.setEnabledProtocols(supportProtocols); - final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( - sslSocket.getSupportedCipherSuites()); - if (cipherSuites.length > 0) { - sslSocket.setEnabledCipherSuites(cipherSuites); - } - } + final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( + sslSocket.getSupportedCipherSuites()); + if (cipherSuites.length > 0) { + sslSocket.setEnabledCipherSuites(cipherSuites); + } + } - public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) { - if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { - ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname); - } else { - try { - socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname); - } catch (Throwable e) { - // ignore any error, we just can't set the hostname... - } - } - } + public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) { + if (factory instanceof android.net.SSLCertificateSocketFactory) { + ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname); + } + } - public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) { - try { - if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { - // can't call directly because of @hide? - //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")}); - android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}}); - } else { - final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class); - // the concatenation of 8-bit, length prefixed protocol names, just one in our case... - // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 - final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8"); - final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1]; - lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow - System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length); - method.invoke(socket, new Object[]{lengthPrefixedProtocols}); - } - } catch (Throwable e) { - // ignore any error, we just can't set the alpn protocol... - } - } + public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) { + try { + if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + // can't call directly because of @hide? + //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")}); + android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}}); + } else { + final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class); + // the concatenation of 8-bit, length prefixed protocol names, just one in our case... + // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 + final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8"); + final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1]; + lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow + System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length); + method.invoke(socket, new Object[]{lengthPrefixedProtocols}); + } + } catch (Throwable e) { + // ignore any error, we just can't set the alpn protocol... + } + } - public static SSLContext getSSLContext() throws NoSuchAlgorithmException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - return SSLContext.getInstance("TLSv1.2"); - } else { - return SSLContext.getInstance("TLS"); - } - } + public static SSLContext getSSLContext() throws NoSuchAlgorithmException { + return SSLContext.getInstance("TLSv1.3"); + } + + public static void log(Account account, SSLSocket socket) { + SSLSession session = socket.getSession(); + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": protocol="+session.getProtocol()+" cipher="+session.getCipherSuite()); + } } diff --git a/src/main/java/eu/siacs/conversations/utils/TLSSocketFactory.java b/src/main/java/eu/siacs/conversations/utils/TLSSocketFactory.java index 9d8a41113..5ea3fb085 100644 --- a/src/main/java/eu/siacs/conversations/utils/TLSSocketFactory.java +++ b/src/main/java/eu/siacs/conversations/utils/TLSSocketFactory.java @@ -16,7 +16,7 @@ public class TLSSocketFactory extends SSLSocketFactory { private final SSLSocketFactory internalSSLSocketFactory; public TLSSocketFactory(X509TrustManager[] trustManager, SecureRandom random) throws KeyManagementException, NoSuchAlgorithmException { - SSLContext context = SSLContext.getInstance("TLS"); + SSLContext context = SSLSocketHelper.getSSLContext(); context.init(null, trustManager, random); this.internalSSLSocketFactory = context.getSocketFactory(); } @@ -58,11 +58,7 @@ public class TLSSocketFactory extends SSLSocketFactory { private static Socket enableTLSOnSocket(Socket socket) { if(socket != null && (socket instanceof SSLSocket)) { - try { - SSLSocketHelper.setSecurity((SSLSocket) socket); - } catch (NoSuchAlgorithmException e) { - //ignoring - } + SSLSocketHelper.setSecurity((SSLSocket) socket); } return socket; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 8afb08b1f..aa7f95171 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -455,6 +455,9 @@ public class XmppConnection implements Runnable { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } + if (socket instanceof SSLSocket) { + SSLSocketHelper.log(account, (SSLSocket) socket); + } return tag != null && tag.isStart("stream"); } @@ -852,6 +855,7 @@ public class XmppConnection implements Runnable { features.encryptionEnabled = true; final Tag tag = tagReader.readTag(); if (tag != null && tag.isStart("stream")) { + SSLSocketHelper.log(account, sslSocket); processStream(); } else { throw new IOException("server didn't restart stream after STARTTLS");