From f908f5d8a1d7769b85198c2d33e9f78296bbe5fe Mon Sep 17 00:00:00 2001 From: Vineet Garg Date: Mon, 24 Aug 2015 02:03:55 +0530 Subject: [PATCH] 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 --- build.gradle | 6 +- res/values/strings.xml | 1 - .../Backends/LanBackend/LanLinkProvider.java | 103 +++++++++--------- src/org/kde/kdeconnect/Device.java | 12 +- .../Helpers/SecurityHelpers/SslHelper.java | 31 +++--- src/org/kde/kdeconnect/NetworkPackage.java | 10 -- 6 files changed, 81 insertions(+), 82 deletions(-) diff --git a/build.gradle b/build.gradle index 3d047dd4..3742376c 100644 --- a/build.gradle +++ b/build.gradle @@ -58,9 +58,9 @@ dependencies { compile 'com.android.support:support-v4:22.2.0' compile 'com.android.support:appcompat-v7:22.2.0' compile 'org.apache.sshd:sshd-core:0.8.0' - compile 'org.bouncycastle:bcpkix-jdk15on:1.52' - compile 'org.bouncycastle:bcprov-jdk15on:1.52' -// compile 'io.netty:netty-handler:4.0.25.Final' // We use a custom build netty in libs directory due to ssl related bug + compile 'com.madgag.spongycastle:pkix:1.52.0.0' + compile 'org.bouncycastle:bcprov-jdk16:1.46' +// 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') androidTestCompile 'org.mockito:mockito-core:1.10.19' diff --git a/res/values/strings.xml b/res/values/strings.xml index f22ada8c..c5e43b67 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -145,6 +145,5 @@ No players found 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. %1$s on %2$s - Use SSL for communication diff --git a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java index fdf72ec0..fc5bff48 100644 --- a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java +++ b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java @@ -21,6 +21,7 @@ package org.kde.kdeconnect.Backends.LanBackend; import android.content.Context; +import android.os.Build; import android.preference.PreferenceManager; import android.support.v4.util.LongSparseArray; 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); - NetworkPackage np2 = NetworkPackage.createIdentityPackage(context); - link.sendPackage(np2,new Device.SendPackageStatusCallback() { - @Override - protected void onSuccess() { - nioLinks.put(channel.hashCode(), link); - nioChannels.put(channel.hashCode(), channel); - 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>() { - @Override - public void operationComplete(Future 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(); - } - }); - } - } + NetworkPackage np2 = NetworkPackage.createIdentityPackage(context); + link.sendPackage(np2,new Device.SendPackageStatusCallback() { + @Override + protected void onSuccess() { + nioLinks.put(channel.hashCode(), link); + nioChannels.put(channel.hashCode(), channel); + 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>() { + @Override + public void operationComplete(Future 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 { - addLink(identityPackage, link); + } 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(); + } + }); + } + } + } + }); + } else { + addLink(identityPackage, link); + } - } + } - @Override - protected void onFailure(Throwable e) { - Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back"); - } - }); + @Override + protected void onFailure(Throwable e) { + Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back"); + } + }); } @@ -423,6 +424,10 @@ public class LanLinkProvider extends BaseLinkProvider { clientGroup = new NioEventLoopGroup(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return; + } + bossGroup = new NioEventLoopGroup(1); workerGroup = new NioEventLoopGroup(); try{ diff --git a/src/org/kde/kdeconnect/Device.java b/src/org/kde/kdeconnect/Device.java index f5d2e54f..f2bea68e 100644 --- a/src/org/kde/kdeconnect/Device.java +++ b/src/org/kde/kdeconnect/Device.java @@ -35,20 +35,22 @@ import android.support.v4.app.NotificationCompat; import android.util.Base64; 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.BasePairingHandler; import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.UserInterface.PairActivity; 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.PrivateKey; 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.X509EncodedKeySpec; import java.util.ArrayList; @@ -63,7 +65,7 @@ public class Device implements BaseLink.PackageReceiver { private final String deviceId; private String name; public PublicKey publicKey; - public X509Certificate certificate; + public Certificate certificate; private int notificationId; private int protocolVersion; diff --git a/src/org/kde/kdeconnect/Helpers/SecurityHelpers/SslHelper.java b/src/org/kde/kdeconnect/Helpers/SecurityHelpers/SslHelper.java index 60e88151..c18d2597 100644 --- a/src/org/kde/kdeconnect/Helpers/SecurityHelpers/SslHelper.java +++ b/src/org/kde/kdeconnect/Helpers/SecurityHelpers/SslHelper.java @@ -27,15 +27,15 @@ import android.provider.Settings; import android.util.Base64; import android.util.Log; -import org.bouncycastle.asn1.x500.X500NameBuilder; -import org.bouncycastle.asn1.x500.style.BCStyle; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.spongycastle.asn1.x500.X500NameBuilder; +import org.spongycastle.asn1.x500.style.BCStyle; +import org.spongycastle.cert.X509CertificateHolder; +import org.spongycastle.cert.X509v3CertificateBuilder; +import org.spongycastle.cert.jcajce.JcaX509CertificateConverter; +import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.operator.ContentSigner; +import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; import java.math.BigInteger; import java.security.KeyStore; @@ -43,6 +43,7 @@ import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Calendar; import java.util.Date; @@ -141,7 +142,7 @@ public class SslHelper { // Setup keystore KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 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 if (remoteDeviceCertificate != null){ keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate); @@ -198,6 +199,11 @@ public class SslHelper { SSLContext tlsContext = getSslContext(context, deviceId, isDeviceTrusted); 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){ sslEngine.setUseClientMode(true); }else{ @@ -209,9 +215,6 @@ public class SslHelper { } } -// for (String cipher : sslEngine.getEnabledCipherSuites()) { -// Log.e("Enabled cipher", cipher); -// } return sslEngine; }catch (Exception e){ e.printStackTrace(); @@ -220,7 +223,7 @@ public class SslHelper { return null; } - public static String getCertificateHash(X509Certificate certificate) { + public static String getCertificateHash(Certificate certificate) { try { byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded()); Formatter formatter = new Formatter(); diff --git a/src/org/kde/kdeconnect/NetworkPackage.java b/src/org/kde/kdeconnect/NetworkPackage.java index 14cdf660..12fc32b2 100644 --- a/src/org/kde/kdeconnect/NetworkPackage.java +++ b/src/org/kde/kdeconnect/NetworkPackage.java @@ -21,29 +21,19 @@ package org.kde.kdeconnect; import android.content.Context; -import android.content.SharedPreferences; -import android.os.Build; import android.preference.PreferenceManager; import android.provider.Settings; -import android.util.Base64; import android.util.Log; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.UserInterface.MainSettingsActivity; import java.io.ByteArrayInputStream; 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 javax.crypto.Cipher; - public class NetworkPackage { public final static int ProtocolVersion = 6;