use conscrypt as security provider to provide tls 1.3 and modern cyphers on old androids
This commit is contained in:
parent
1985f6bdec
commit
6637d7056e
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in a new issue