2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-30 13:47:41 +00:00

Added spongy castle back, to support certificate generation on older devices which is not possible using bouncy castle due to same class name conflict

Fixed TLS version to TLSv1, this saves a connection failure due to TLSv1.2 hello message from devices with latest android versions

If device with new android version enquires about certificate from device with old version (<14) using setNeedClientAuth, connection fails on older device due to variation in certificate request code. So disabled server loop on older devices and using reverse connection hack for successful connection. But due to this, they can't connect to devices with similar android vesion since server is disabled on both and for a successful connection one should be server and one should be client
This commit is contained in:
Vineet Garg
2015-08-24 02:03:55 +05:30
parent c4a27255a7
commit f908f5d8a1
6 changed files with 81 additions and 82 deletions

View File

@@ -58,9 +58,9 @@ dependencies {
compile 'com.android.support:support-v4:22.2.0' compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.0' compile 'com.android.support:appcompat-v7:22.2.0'
compile 'org.apache.sshd:sshd-core:0.8.0' compile 'org.apache.sshd:sshd-core:0.8.0'
compile 'org.bouncycastle:bcpkix-jdk15on:1.52' compile 'com.madgag.spongycastle:pkix:1.52.0.0'
compile 'org.bouncycastle:bcprov-jdk15on:1.52' compile 'org.bouncycastle:bcprov-jdk16:1.46'
// compile 'io.netty:netty-handler:4.0.25.Final' // We use a custom build netty in libs directory due to ssl related bug // compile 'io.netty:netty-handler:4.0.30.Final' // We use a custom build netty in libs directory due to ssl related bug
compile fileTree(include: '*.jar', dir: 'libs') compile fileTree(include: '*.jar', dir: 'libs')
androidTestCompile 'org.mockito:mockito-core:1.10.19' androidTestCompile 'org.mockito:mockito-core:1.10.19'

View File

@@ -145,6 +145,5 @@
<string name="no_players_connected">No players found</string> <string name="no_players_connected">No players found</string>
<string name="custom_dev_list_help">Use this option only if your device is not automatically detected. Enter IP address or hostname below and touch the button to add it to the list. Touch an existing item to remove it from the list.</string> <string name="custom_dev_list_help">Use this option only if your device is not automatically detected. Enter IP address or hostname below and touch the button to add it to the list. Touch an existing item to remove it from the list.</string>
<string name="mpris_player_on_device">%1$s on %2$s</string> <string name="mpris_player_on_device">%1$s on %2$s</string>
<string name="use_ssl">Use SSL for communication</string>
</resources> </resources>

View File

@@ -21,6 +21,7 @@
package org.kde.kdeconnect.Backends.LanBackend; package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import android.util.Base64; import android.util.Base64;
@@ -289,60 +290,60 @@ public class LanLinkProvider extends BaseLinkProvider {
} }
final LanLink link = new LanLink(context, channel, identityPackage.getString("deviceId"), LanLinkProvider.this); final LanLink link = new LanLink(context, channel, identityPackage.getString("deviceId"), LanLinkProvider.this);
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context); NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
link.sendPackage(np2,new Device.SendPackageStatusCallback() { link.sendPackage(np2,new Device.SendPackageStatusCallback() {
@Override @Override
protected void onSuccess() { protected void onSuccess() {
nioLinks.put(channel.hashCode(), link); nioLinks.put(channel.hashCode(), link);
nioChannels.put(channel.hashCode(), channel); nioChannels.put(channel.hashCode(), channel);
Log.i("KDE/LanLinkProvider", "nioLinks.size(): " + nioLinks.size()); Log.i("KDE/LanLinkProvider", "nioLinks.size(): " + nioLinks.size());
// If ssl handler is in channel, add link after handshake is completed
final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
if (sslHandler != null) {
sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Channel>>() {
@Override
public void operationComplete(Future<? super Channel> future) throws Exception {
if (future.isSuccess()) {
try {
Log.i("KDE/LanLinkProvider", "Handshake successfully completed with " + identityPackage.getString("deviceName") + ", session secured with " + sslHandler.engine().getSession().getCipherSuite());
Certificate certificate = sslHandler.engine().getSession().getPeerCertificates()[0];
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
link.setOnSsl(true);
addLink(identityPackage, link);
} catch (Exception e){
e.printStackTrace();
}
} else {
// Unpair if handshake failed
// Any exception or handshake exception ?
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPackage.getString("deviceName"));
future.cause().printStackTrace();
if (future.cause() instanceof SSLHandshakeException) {
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(identityPackage.getString("deviceId"));
if (device == null) return;
device.unpair();
}
});
}
}
// If ssl handler is in channel, add link after handshake is completed
final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
if (sslHandler != null) {
sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Channel>>() {
@Override
public void operationComplete(Future<? super Channel> future) throws Exception {
if (future.isSuccess()) {
try {
Log.i("KDE/LanLinkProvider", "Handshake successfully completed with " + identityPackage.getString("deviceName") + ", session secured with " + sslHandler.engine().getSession().getCipherSuite());
Certificate certificate = sslHandler.engine().getSession().getPeerCertificates()[0];
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
link.setOnSsl(true);
addLink(identityPackage, link);
} catch (Exception e){
e.printStackTrace();
} }
}); } else {
} else { // Unpair if handshake failed
addLink(identityPackage, link); // Any exception or handshake exception ?
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPackage.getString("deviceName"));
future.cause().printStackTrace();
if (future.cause() instanceof SSLHandshakeException) {
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(identityPackage.getString("deviceId"));
if (device == null) return;
device.unpair();
}
});
}
}
} }
});
} else {
addLink(identityPackage, link);
}
} }
@Override @Override
protected void onFailure(Throwable e) { protected void onFailure(Throwable e) {
Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back"); Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back");
} }
}); });
} }
@@ -423,6 +424,10 @@ public class LanLinkProvider extends BaseLinkProvider {
clientGroup = new NioEventLoopGroup(); clientGroup = new NioEventLoopGroup();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return;
}
bossGroup = new NioEventLoopGroup(1); bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup();
try{ try{

View File

@@ -35,20 +35,22 @@ import android.support.v4.app.NotificationCompat;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.PairActivity; import org.kde.kdeconnect.UserInterface.PairActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import java.io.ByteArrayInputStream;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.X509Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
@@ -63,7 +65,7 @@ public class Device implements BaseLink.PackageReceiver {
private final String deviceId; private final String deviceId;
private String name; private String name;
public PublicKey publicKey; public PublicKey publicKey;
public X509Certificate certificate; public Certificate certificate;
private int notificationId; private int notificationId;
private int protocolVersion; private int protocolVersion;

View File

@@ -27,15 +27,15 @@ import android.provider.Settings;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.bouncycastle.asn1.x500.X500NameBuilder; import org.spongycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle; import org.spongycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509CertificateHolder; import org.spongycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder; import org.spongycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner; import org.spongycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.KeyStore; import java.security.KeyStore;
@@ -43,6 +43,7 @@ import java.security.MessageDigest;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@@ -141,7 +142,7 @@ public class SslHelper {
// Setup keystore // Setup keystore
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null); keyStore.load(null, null);
keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new java.security.cert.Certificate[]{certificate}); keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new Certificate[]{certificate});
// Set certificate if device trusted // Set certificate if device trusted
if (remoteDeviceCertificate != null){ if (remoteDeviceCertificate != null){
keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate); keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate);
@@ -198,6 +199,11 @@ public class SslHelper {
SSLContext tlsContext = getSslContext(context, deviceId, isDeviceTrusted); SSLContext tlsContext = getSslContext(context, deviceId, isDeviceTrusted);
SSLEngine sslEngine = tlsContext.createSSLEngine(); SSLEngine sslEngine = tlsContext.createSSLEngine();
// We do not support TLSv1.2 as of now, because only Android with version greater 20 that support it
sslEngine.setEnabledProtocols(new String[]{
"TLSv1"
});
if (sslMode == SslMode.Client){ if (sslMode == SslMode.Client){
sslEngine.setUseClientMode(true); sslEngine.setUseClientMode(true);
}else{ }else{
@@ -209,9 +215,6 @@ public class SslHelper {
} }
} }
// for (String cipher : sslEngine.getEnabledCipherSuites()) {
// Log.e("Enabled cipher", cipher);
// }
return sslEngine; return sslEngine;
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.printStackTrace();
@@ -220,7 +223,7 @@ public class SslHelper {
return null; return null;
} }
public static String getCertificateHash(X509Certificate certificate) { public static String getCertificateHash(Certificate certificate) {
try { try {
byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded()); byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded());
Formatter formatter = new Formatter(); Formatter formatter = new Formatter();

View File

@@ -21,29 +21,19 @@
package org.kde.kdeconnect; package org.kde.kdeconnect;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.Settings; import android.provider.Settings;
import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.UserInterface.MainSettingsActivity; import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList; import java.util.ArrayList;
import javax.crypto.Cipher;
public class NetworkPackage { public class NetworkPackage {
public final static int ProtocolVersion = 6; public final static int ProtocolVersion = 6;