~singpolyma/cheogram-android

6637d7056ef4bacf5a6f023f19f3fea10f52a727 — Daniel Gultsch 6 years ago 1985f6b
use conscrypt as security provider to provide tls 1.3 and modern cyphers on old androids
M build.gradle => build.gradle +1 -0
@@ 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 {

M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java => src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +3 -1
@@ 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();

M src/main/java/eu/siacs/conversations/utils/SSLSocketHelper.java => src/main/java/eu/siacs/conversations/utils/SSLSocketHelper.java +51 -52
@@ 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<String> 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<String> 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.net.SSLCertificateSocketFactory) factory).setHostname(socket, 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) {
			((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) {
        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 {
        return SSLContext.getInstance("TLSv1.3");
    }

	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 void log(Account account, SSLSocket socket) {
        SSLSession session = socket.getSession();
        Log.d(Config.LOGTAG,account.getJid().asBareJid()+": protocol="+session.getProtocol()+" cipher="+session.getCipherSuite());
    }
}

M src/main/java/eu/siacs/conversations/utils/TLSSocketFactory.java => src/main/java/eu/siacs/conversations/utils/TLSSocketFactory.java +2 -6
@@ 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;
    }

M src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java => src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +4 -0
@@ 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");