use conscrypt as security provider to provide tls 1.3 and modern cyphers on old androids

This commit is contained in:
Daniel Gultsch 2018-09-21 16:33:07 +02:00
parent 1985f6bdec
commit 6637d7056e
5 changed files with 61 additions and 59 deletions

View file

@ -52,6 +52,7 @@ dependencies {
implementation 'rocks.xmpp:xmpp-addr:0.8.0' implementation 'rocks.xmpp:xmpp-addr:0.8.0'
implementation 'org.osmdroid:osmdroid-android:6.0.1' implementation 'org.osmdroid:osmdroid-android:6.0.1'
implementation 'org.hsluv:hsluv:0.2' implementation 'org.hsluv:hsluv:0.2'
implementation 'org.conscrypt:conscrypt-android:1.3.0'
} }
ext { ext {

View file

@ -40,12 +40,14 @@ import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
import android.util.Pair; import android.util.Pair;
import org.conscrypt.Conscrypt;
import org.openintents.openpgp.IOpenPgpService2; import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.net.URL; import java.net.URL;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
@ -955,7 +957,7 @@ public class XmppConnectionService extends Service {
public void onCreate() { public void onCreate() {
OmemoSetting.load(this); OmemoSetting.load(this);
ExceptionHelper.init(getApplicationContext()); ExceptionHelper.init(getApplicationContext());
PRNGFixes.apply(); Security.insertProviderAt(Conscrypt.newProvider(), 1);
Resolver.init(this); Resolver.init(this);
this.mRandom = new SecureRandom(); this.mRandom = new SecureRandom();
updateMemorizingTrustmanager(); updateMemorizingTrustmanager();

View file

@ -1,6 +1,6 @@
package eu.siacs.conversations.utils; package eu.siacs.conversations.utils;
import android.os.Build; import android.util.Log;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -9,65 +9,64 @@ import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
public class SSLSocketHelper { public class SSLSocketHelper {
public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException { public static void setSecurity(final SSLSocket sslSocket) {
final String[] supportProtocols; final String[] supportProtocols;
final Collection<String> supportedProtocols = new LinkedList<>( final Collection<String> supportedProtocols = new LinkedList<>(
Arrays.asList(sslSocket.getSupportedProtocols())); Arrays.asList(sslSocket.getSupportedProtocols()));
supportedProtocols.remove("SSLv3"); supportedProtocols.remove("SSLv3");
supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]); supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
sslSocket.setEnabledProtocols(supportProtocols); sslSocket.setEnabledProtocols(supportProtocols);
final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
sslSocket.getSupportedCipherSuites()); sslSocket.getSupportedCipherSuites());
if (cipherSuites.length > 0) { if (cipherSuites.length > 0) {
sslSocket.setEnabledCipherSuites(cipherSuites); sslSocket.setEnabledCipherSuites(cipherSuites);
} }
} }
public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) { 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) { if (factory instanceof android.net.SSLCertificateSocketFactory) {
((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname); ((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 setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) { public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
try { try {
if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 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? // can't call directly because of @hide?
//((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")}); //((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")}}); android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
} else { } else {
final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class); final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
// the concatenation of 8-bit, length prefixed protocol names, just one in our case... // 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 // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8"); final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1]; final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length); System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
method.invoke(socket, new Object[]{lengthPrefixedProtocols}); method.invoke(socket, new Object[]{lengthPrefixedProtocols});
} }
} catch (Throwable e) { } catch (Throwable e) {
// ignore any error, we just can't set the alpn protocol... // ignore any error, we just can't set the alpn protocol...
} }
} }
public static SSLContext getSSLContext() throws NoSuchAlgorithmException { public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { return SSLContext.getInstance("TLSv1.3");
return SSLContext.getInstance("TLSv1.2"); }
} else {
return SSLContext.getInstance("TLS"); 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());
}
} }

View file

@ -16,7 +16,7 @@ public class TLSSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory internalSSLSocketFactory; private final SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory(X509TrustManager[] trustManager, SecureRandom random) throws KeyManagementException, NoSuchAlgorithmException { public TLSSocketFactory(X509TrustManager[] trustManager, SecureRandom random) throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS"); SSLContext context = SSLSocketHelper.getSSLContext();
context.init(null, trustManager, random); context.init(null, trustManager, random);
this.internalSSLSocketFactory = context.getSocketFactory(); this.internalSSLSocketFactory = context.getSocketFactory();
} }
@ -58,11 +58,7 @@ public class TLSSocketFactory extends SSLSocketFactory {
private static Socket enableTLSOnSocket(Socket socket) { private static Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) { if(socket != null && (socket instanceof SSLSocket)) {
try { SSLSocketHelper.setSecurity((SSLSocket) socket);
SSLSocketHelper.setSecurity((SSLSocket) socket);
} catch (NoSuchAlgorithmException e) {
//ignoring
}
} }
return socket; return socket;
} }

View file

@ -455,6 +455,9 @@ public class XmppConnection implements Runnable {
if (Thread.currentThread().isInterrupted()) { if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException(); throw new InterruptedException();
} }
if (socket instanceof SSLSocket) {
SSLSocketHelper.log(account, (SSLSocket) socket);
}
return tag != null && tag.isStart("stream"); return tag != null && tag.isStart("stream");
} }
@ -852,6 +855,7 @@ public class XmppConnection implements Runnable {
features.encryptionEnabled = true; features.encryptionEnabled = true;
final Tag tag = tagReader.readTag(); final Tag tag = tagReader.readTag();
if (tag != null && tag.isStart("stream")) { if (tag != null && tag.isStart("stream")) {
SSLSocketHelper.log(account, sslSocket);
processStream(); processStream();
} else { } else {
throw new IOException("server didn't restart stream after STARTTLS"); throw new IOException("server didn't restart stream after STARTTLS");