mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-28 20:57:42 +00:00
Fail earlier if we don't have a certificate
This commit is contained in:
parent
d0923b845b
commit
a1ccc7b64e
@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.Backends;
|
package org.kde.kdeconnect.Backends;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.kde.kdeconnect.NetworkPacket;
|
import org.kde.kdeconnect.NetworkPacket;
|
||||||
|
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
public abstract class BaseLinkProvider {
|
public abstract class BaseLinkProvider {
|
||||||
@ -15,7 +18,10 @@ public abstract class BaseLinkProvider {
|
|||||||
private final CopyOnWriteArrayList<ConnectionReceiver> connectionReceivers = new CopyOnWriteArrayList<>();
|
private final CopyOnWriteArrayList<ConnectionReceiver> connectionReceivers = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
public interface ConnectionReceiver {
|
public interface ConnectionReceiver {
|
||||||
void onConnectionReceived(NetworkPacket identityPacket, BaseLink link);
|
void onConnectionReceived(@NonNull final String deviceId,
|
||||||
|
@NonNull final Certificate certificate,
|
||||||
|
@NonNull final NetworkPacket identityPacket,
|
||||||
|
@NonNull final BaseLink link);
|
||||||
void onConnectionLost(BaseLink link);
|
void onConnectionLost(BaseLink link);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,10 +34,13 @@ public abstract class BaseLinkProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//These two should be called when the provider links to a new computer
|
//These two should be called when the provider links to a new computer
|
||||||
protected void connectionAccepted(NetworkPacket identityPacket, BaseLink link) {
|
protected void connectionAccepted(@NonNull final String deviceId,
|
||||||
|
@NonNull final Certificate certificate,
|
||||||
|
@NonNull final NetworkPacket identityPacket,
|
||||||
|
@NonNull final BaseLink link) {
|
||||||
//Log.i("KDE/LinkProvider", "connectionAccepted");
|
//Log.i("KDE/LinkProvider", "connectionAccepted");
|
||||||
for(ConnectionReceiver cr : connectionReceivers) {
|
for(ConnectionReceiver cr : connectionReceivers) {
|
||||||
cr.onConnectionReceived(identityPacket, link);
|
cr.onConnectionReceived(deviceId, certificate, identityPacket, link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected void connectionLost(BaseLink link) {
|
protected void connectionLost(BaseLink link) {
|
||||||
@ -45,8 +54,6 @@ public abstract class BaseLinkProvider {
|
|||||||
public abstract void onStart();
|
public abstract void onStart();
|
||||||
public abstract void onStop();
|
public abstract void onStop();
|
||||||
public abstract void onNetworkChange();
|
public abstract void onNetworkChange();
|
||||||
|
|
||||||
//public abstract int getPriority();
|
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,12 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||||
import org.kde.kdeconnect.NetworkPacket;
|
import org.kde.kdeconnect.NetworkPacket;
|
||||||
|
|
||||||
@ -27,6 +29,8 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -48,8 +52,12 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
|||||||
private ServerRunnable serverRunnable;
|
private ServerRunnable serverRunnable;
|
||||||
private ClientRunnable clientRunnable;
|
private ClientRunnable clientRunnable;
|
||||||
|
|
||||||
private void addLink(NetworkPacket identityPacket, BluetoothLink link) {
|
private void addLink(NetworkPacket identityPacket, BluetoothLink link) throws CertificateException {
|
||||||
String deviceId = identityPacket.getString("deviceId");
|
String deviceId = identityPacket.getString("deviceId");
|
||||||
|
String certificateString = identityPacket.getString("certificate");
|
||||||
|
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
||||||
|
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
|
||||||
|
|
||||||
Log.i("BluetoothLinkProvider", "addLink to " + deviceId);
|
Log.i("BluetoothLinkProvider", "addLink to " + deviceId);
|
||||||
BluetoothLink oldLink = visibleComputers.get(deviceId);
|
BluetoothLink oldLink = visibleComputers.get(deviceId);
|
||||||
if (oldLink == link) {
|
if (oldLink == link) {
|
||||||
@ -57,7 +65,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
visibleComputers.put(deviceId, link);
|
visibleComputers.put(deviceId, link);
|
||||||
connectionAccepted(identityPacket, link);
|
connectionAccepted(deviceId, certificate, identityPacket, link);
|
||||||
link.startListening();
|
link.startListening();
|
||||||
if (oldLink != null) {
|
if (oldLink != null) {
|
||||||
Log.i("BluetoothLinkProvider", "Removing old connection to same device");
|
Log.i("BluetoothLinkProvider", "Removing old connection to same device");
|
||||||
@ -189,6 +197,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
|||||||
InputStream inputStream = connection.getDefaultInputStream();
|
InputStream inputStream = connection.getDefaultInputStream();
|
||||||
|
|
||||||
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
|
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
|
||||||
|
np.set("certificate", Base64.encodeToString(SslHelper.certificate.getEncoded(), 0));
|
||||||
byte[] message = np.serialize().getBytes(Charsets.UTF_8);
|
byte[] message = np.serialize().getBytes(Charsets.UTF_8);
|
||||||
outputStream.write(message);
|
outputStream.write(message);
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
@ -371,7 +380,11 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
|||||||
link.sendPacket(np2, new Device.SendPacketStatusCallback() {
|
link.sendPacket(np2, new Device.SendPacketStatusCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess() {
|
public void onSuccess() {
|
||||||
|
try {
|
||||||
addLink(identityPacket, link);
|
addLink(identityPacket, link);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -211,9 +211,8 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
|||||||
String mode = clientMode ? "client" : "server";
|
String mode = clientMode ? "client" : "server";
|
||||||
try {
|
try {
|
||||||
Certificate certificate = event.getPeerCertificates()[0];
|
Certificate certificate = event.getPeerCertificates()[0];
|
||||||
identityPacket.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
|
|
||||||
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
|
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
|
||||||
addLink(identityPacket, sslsocket);
|
addLink(deviceId, certificate, identityPacket, sslsocket);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"), e);
|
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"), e);
|
||||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||||
@ -252,14 +251,13 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
|||||||
* {@link Device#addLink(NetworkPacket, BaseLink)} crashes on some devices running Oreo 8.1 (SDK level 27).
|
* {@link Device#addLink(NetworkPacket, BaseLink)} crashes on some devices running Oreo 8.1 (SDK level 27).
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param identityPacket representation of remote device
|
* @param deviceId remote device id
|
||||||
* @param socket a new Socket, which should be used to receive packets from the remote device
|
* @param certificate remote device certificate
|
||||||
* @param connectionOrigin which side started this connection
|
* @param identityPacket identity packet with the remote device's device name, type, protocol version, etc.
|
||||||
|
* @param socket a new Socket, which should be used to send and receive packets from the remote device
|
||||||
* @throws IOException if an exception is thrown by {@link LanLink#reset(SSLSocket, LanLink.ConnectionStarted)}
|
* @throws IOException if an exception is thrown by {@link LanLink#reset(SSLSocket, LanLink.ConnectionStarted)}
|
||||||
*/
|
*/
|
||||||
private void addLink(final NetworkPacket identityPacket, SSLSocket socket) throws IOException {
|
private void addLink(String deviceId, Certificate certificate, final NetworkPacket identityPacket, SSLSocket socket) throws IOException {
|
||||||
|
|
||||||
String deviceId = identityPacket.getString("deviceId");
|
|
||||||
LanLink currentLink = visibleComputers.get(deviceId);
|
LanLink currentLink = visibleComputers.get(deviceId);
|
||||||
if (currentLink != null) {
|
if (currentLink != null) {
|
||||||
//Update old link
|
//Update old link
|
||||||
@ -271,7 +269,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
|||||||
//Let's create the link
|
//Let's create the link
|
||||||
LanLink link = new LanLink(context, deviceId, this, socket);
|
LanLink link = new LanLink(context, deviceId, this, socket);
|
||||||
visibleComputers.put(deviceId, link);
|
visibleComputers.put(deviceId, link);
|
||||||
connectionAccepted(identityPacket, link);
|
connectionAccepted(deviceId, certificate, identityPacket, link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ package org.kde.kdeconnect.Backends.LoopbackBackend;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
|
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||||
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||||
import org.kde.kdeconnect.NetworkPacket;
|
import org.kde.kdeconnect.NetworkPacket;
|
||||||
|
|
||||||
public class LoopbackLinkProvider extends BaseLinkProvider {
|
public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||||
@ -31,14 +33,10 @@ public class LoopbackLinkProvider extends BaseLinkProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void onNetworkChange() {
|
public void onNetworkChange() {
|
||||||
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
|
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
|
||||||
connectionAccepted(np, new LoopbackLink(context, this));
|
String deviceId = DeviceHelper.getDeviceId(context);
|
||||||
|
connectionAccepted(deviceId, SslHelper.certificate, np, new LoopbackLink(context, this));
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
@Override
|
|
||||||
public int getPriority() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "LoopbackLinkProvider";
|
return "LoopbackLinkProvider";
|
||||||
|
@ -40,6 +40,7 @@ import org.kde.kdeconnect_tp.R;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -130,18 +131,20 @@ public class Device implements BaseLink.PacketReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
|
// Remembered trusted device, we need to wait for a incoming Link to communicate
|
||||||
Device(Context context, String deviceId) {
|
Device(@NonNull Context context, @NonNull String deviceId) throws CertificateException {
|
||||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
Log.i("Device","Loading trusted device");
|
||||||
|
|
||||||
//Log.e("Device","Constructor A");
|
|
||||||
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
|
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||||
|
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.Paired);
|
||||||
|
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.name = settings.getString("deviceName", context.getString(R.string.unknown_device));
|
this.name = settings.getString("deviceName", context.getString(R.string.unknown_device));
|
||||||
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.Paired);
|
this.protocolVersion = 0; //We don't know it yet
|
||||||
this.protocolVersion = DeviceHelper.ProtocolVersion; //We don't know it yet
|
|
||||||
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
|
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
|
||||||
|
this.certificate = SslHelper.getDeviceCertificate(context, deviceId);
|
||||||
|
|
||||||
//Assume every plugin is supported until addLink is called and we can get the actual list
|
//Assume every plugin is supported until addLink is called and we can get the actual list
|
||||||
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
|
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
|
||||||
@ -150,21 +153,24 @@ public class Device implements BaseLink.PacketReceiver {
|
|||||||
//reloadPluginsFromSettings();
|
//reloadPluginsFromSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
// Device known via an incoming connection sent to us via a Link, we don't trust it yet
|
||||||
Device(Context context, NetworkPacket np, BaseLink dl) {
|
Device(@NonNull Context context, @NonNull String deviceId, @NonNull Certificate certificate, @NonNull NetworkPacket identityPacket, @NonNull BaseLink dl) {
|
||||||
|
Log.i("Device","Creating untrusted device");
|
||||||
//Log.e("Device","Constructor B");
|
|
||||||
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.deviceId = np.getString("deviceId");
|
|
||||||
this.name = context.getString(R.string.unknown_device); //We read it in addLink
|
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||||
this.pairingHandler= new PairingHandler(this, pairingCallback, PairingHandler.PairState.NotPaired);
|
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.NotPaired);
|
||||||
this.protocolVersion = 0;
|
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.certificate = certificate;
|
||||||
|
|
||||||
|
// The following properties are read from the identityPacket in addLink since they can change in future identity packets
|
||||||
|
this.name = context.getString(R.string.unknown_device);
|
||||||
this.deviceType = DeviceType.Computer;
|
this.deviceType = DeviceType.Computer;
|
||||||
|
this.protocolVersion = 0;
|
||||||
|
|
||||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
addLink(identityPacket, dl);
|
||||||
|
|
||||||
addLink(np, dl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -255,12 +261,8 @@ public class Device implements BaseLink.PacketReceiver {
|
|||||||
try {
|
try {
|
||||||
String encodedCertificate = Base64.encodeToString(certificate.getEncoded(), 0);
|
String encodedCertificate = Base64.encodeToString(certificate.getEncoded(), 0);
|
||||||
editor.putString("certificate", encodedCertificate);
|
editor.putString("certificate", encodedCertificate);
|
||||||
} catch (NullPointerException n) {
|
} catch(CertificateEncodingException e) {
|
||||||
Log.w("PairingHandler", "Certificate is null, remote device does not support SSL", n);
|
throw new RuntimeException(e);
|
||||||
return;
|
|
||||||
} catch (CertificateEncodingException c) {
|
|
||||||
Log.e("PairingHandler", "Error encoding certificate", c);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
editor.putString("deviceName", name);
|
editor.putString("deviceName", name);
|
||||||
editor.putString("deviceType", deviceType.toString());
|
editor.putString("deviceType", deviceType.toString());
|
||||||
@ -384,19 +386,6 @@ public class Device implements BaseLink.PacketReceiver {
|
|||||||
this.deviceType = DeviceType.FromString(identityPacket.getString("deviceType", "desktop"));
|
this.deviceType = DeviceType.FromString(identityPacket.getString("deviceType", "desktop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identityPacket.has("certificate")) {
|
|
||||||
String certificateString = identityPacket.getString("certificate");
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
|
||||||
certificate = SslHelper.parseCertificate(certificateBytes);
|
|
||||||
Log.i("KDE/Device", "Got certificate ");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("KDE/Device", "Error getting certificate", e);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i("KDE/Device", "addLink " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
|
Log.i("KDE/Device", "addLink " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
|
||||||
|
|
||||||
Set<String> outgoingCapabilities = identityPacket.getStringSet("outgoingCapabilities", null);
|
Set<String> outgoingCapabilities = identityPacket.getStringSet("outgoingCapabilities", null);
|
||||||
|
@ -176,26 +176,29 @@ public class SslHelper {
|
|||||||
return !cert.isEmpty();
|
return !cert.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SSLContext getSslContext(Context context, String deviceId, boolean isDeviceTrusted) {
|
/**
|
||||||
|
* Returns the stored certificate for a trusted device
|
||||||
|
**/
|
||||||
|
public static Certificate getDeviceCertificate(Context context, String deviceId) throws CertificateException {
|
||||||
|
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||||
|
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
|
||||||
|
return parseCertificate(certificateBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SSLContext getSslContextForDevice(Context context, String deviceId, boolean isDeviceTrusted) {
|
||||||
//TODO: Cache
|
//TODO: Cache
|
||||||
try {
|
try {
|
||||||
// Get device private key
|
// Get device private key
|
||||||
PrivateKey privateKey = RsaHelper.getPrivateKey(context);
|
PrivateKey privateKey = RsaHelper.getPrivateKey(context);
|
||||||
|
|
||||||
// Get remote device certificate if trusted
|
|
||||||
Certificate remoteDeviceCertificate = null;
|
|
||||||
if (isDeviceTrusted) {
|
|
||||||
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
|
||||||
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
|
|
||||||
remoteDeviceCertificate = parseCertificate(certificateBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 Certificate[]{certificate});
|
keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new Certificate[]{certificate});
|
||||||
// Set certificate if device trusted
|
|
||||||
if (remoteDeviceCertificate != null) {
|
// Add device certificate if device trusted
|
||||||
|
if (isDeviceTrusted) {
|
||||||
|
Certificate remoteDeviceCertificate = getDeviceCertificate(context, deviceId);
|
||||||
keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate);
|
keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +242,7 @@ public class SslHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static SSLSocket convertToSslSocket(Context context, Socket socket, String deviceId, boolean isDeviceTrusted, boolean clientMode) throws IOException {
|
public static SSLSocket convertToSslSocket(Context context, Socket socket, String deviceId, boolean isDeviceTrusted, boolean clientMode) throws IOException {
|
||||||
SSLSocketFactory sslsocketFactory = SslHelper.getSslContext(context, deviceId, isDeviceTrusted).getSocketFactory();
|
SSLSocketFactory sslsocketFactory = SslHelper.getSslContextForDevice(context, deviceId, isDeviceTrusted).getSocketFactory();
|
||||||
SSLSocket sslsocket = (SSLSocket) sslsocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
|
SSLSocket sslsocket = (SSLSocket) sslsocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
|
||||||
SslHelper.configureSslSocket(sslsocket, isDeviceTrusted, clientMode);
|
SslHelper.configureSslSocket(sslsocket, isDeviceTrusted, clientMode);
|
||||||
return sslsocket;
|
return sslsocket;
|
||||||
|
@ -5,6 +5,8 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.kde.kdeconnect.Backends.BaseLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||||
@ -17,6 +19,8 @@ import org.kde.kdeconnect.Plugins.PluginFactory;
|
|||||||
import org.kde.kdeconnect.UserInterface.PairingHandler;
|
import org.kde.kdeconnect.UserInterface.PairingHandler;
|
||||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||||
|
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -105,9 +109,14 @@ public class KdeConnect extends Application {
|
|||||||
for (String deviceId : trustedDevices) {
|
for (String deviceId : trustedDevices) {
|
||||||
//Log.e("BackgroundService", "Loading device "+deviceId);
|
//Log.e("BackgroundService", "Loading device "+deviceId);
|
||||||
if (preferences.getBoolean(deviceId, false)) {
|
if (preferences.getBoolean(deviceId, false)) {
|
||||||
|
try {
|
||||||
Device device = new Device(this, deviceId);
|
Device device = new Device(this, deviceId);
|
||||||
devices.put(deviceId, device);
|
devices.put(deviceId, device);
|
||||||
device.addPairingCallback(devicePairingCallback);
|
device.addPairingCallback(devicePairingCallback);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
Log.e("KdeConnect", "Could not load trusted device, certificate not valid: " + deviceId);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,15 +145,17 @@ public class KdeConnect extends Application {
|
|||||||
|
|
||||||
private final BaseLinkProvider.ConnectionReceiver connectionListener = new BaseLinkProvider.ConnectionReceiver() {
|
private final BaseLinkProvider.ConnectionReceiver connectionListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionReceived(final NetworkPacket identityPacket, final BaseLink link) {
|
public void onConnectionReceived(@NonNull final String deviceId,
|
||||||
String deviceId = identityPacket.getString("deviceId");
|
@NonNull final Certificate certificate,
|
||||||
|
@NonNull final NetworkPacket identityPacket,
|
||||||
|
@NonNull final BaseLink link) {
|
||||||
Device device = devices.get(deviceId);
|
Device device = devices.get(deviceId);
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
Log.i("KDE/Application", "addLink, known device: " + deviceId);
|
Log.i("KDE/Application", "addLink, known device: " + deviceId);
|
||||||
device.addLink(identityPacket, link);
|
device.addLink(identityPacket, link);
|
||||||
} else {
|
} else {
|
||||||
Log.i("KDE/Application", "addLink,unknown device: " + deviceId);
|
Log.i("KDE/Application", "addLink,unknown device: " + deviceId);
|
||||||
device = new Device(KdeConnect.this, identityPacket, link);
|
device = new Device(KdeConnect.this, deviceId, certificate, identityPacket, link);
|
||||||
devices.put(deviceId, device);
|
devices.put(deviceId, device);
|
||||||
device.addPairingCallback(devicePairingCallback);
|
device.addPairingCallback(devicePairingCallback);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import org.kde.kdeconnect.Backends.LanBackend.LanLink;
|
|||||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
|
||||||
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||||
import org.kde.kdeconnect.UserInterface.PairingHandler;
|
import org.kde.kdeconnect.UserInterface.PairingHandler;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.powermock.api.mockito.PowerMockito;
|
import org.powermock.api.mockito.PowerMockito;
|
||||||
@ -42,6 +43,9 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@PrepareForTest({Base64.class, Log.class, PreferenceManager.class, ContextCompat.class})
|
@PrepareForTest({Base64.class, Log.class, PreferenceManager.class, ContextCompat.class})
|
||||||
@ -56,16 +60,22 @@ public class DeviceTest {
|
|||||||
|
|
||||||
String deviceId = "testDevice";
|
String deviceId = "testDevice";
|
||||||
String name = "Test Device";
|
String name = "Test Device";
|
||||||
|
String encodedCertificate = "MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2\n" +
|
||||||
KeyPair keyPair;
|
"NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u\n" +
|
||||||
try {
|
"bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5\n" +
|
||||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
"MV9mMDY0XzQ3OGVfYmU4Y18xOTM1ZDc1NDRkNTRfMQwwCgYDVQQKDANLREUxFDASBgNVBAsMC0tk\n" +
|
||||||
keyGen.initialize(2048);
|
"ZSBjb25uZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzH9GxS1lctpwYdSGAoPH\n" +
|
||||||
keyPair = keyGen.genKeyPair();
|
"ws+MnVaL0PVDCuzrpxzXc+bChR87xofhQIesLPLZEcmUJ1MlEJ6jx4W+gVhvY2tUN7SoiKKbnq8s\n" +
|
||||||
} catch (Exception e) {
|
"WjI5ovs5yML3C1zPbOSJAdK613FcdkK+UGd/9dQk54gIozinC58iyTAChVVpB3pAF38EPxwKkuo2\n" +
|
||||||
Log.e("KDE/initializeRsaKeys", "Exception", e);
|
"qTzwk24d6PRxz1skkzwEphUQQzGboyHsAlJHN1MzM2/yFGB4l8iUua2d3ETyfy/xFEh/SwtGtXE5\n" +
|
||||||
return;
|
"KLz4cpb0fxjeYQZVruBKxzE07kgDO3zOhmP3LJ/KSPHWYImd1DWmpY9iDvoXr6+V7FAnRloaEIyg\n" +
|
||||||
}
|
"7WwdlSCpo3TXVuIjLwIDAQABozIwMDAdBgNVHQ4EFgQUwmbHo8YbiR463GRKSLL3eIKyvDkwDwYD\n" +
|
||||||
|
"VR0TAQH/BAUwAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAydijH3rbnvpBDB/30w2PCGMT7O0N/XYM\n" +
|
||||||
|
"wBtUidqa4NFumJrNrccx5Ehp4UP66BfP61HW8h2U/EekYfOsZyyWd4KnsDD6ycR8h/WvpK3BC2cn\n" +
|
||||||
|
"I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r\n" +
|
||||||
|
"/urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl\n" +
|
||||||
|
"yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg\n" +
|
||||||
|
"7n+KOQ==";
|
||||||
|
|
||||||
this.context = Mockito.mock(Context.class);
|
this.context = Mockito.mock(Context.class);
|
||||||
|
|
||||||
@ -80,7 +90,7 @@ public class DeviceTest {
|
|||||||
SharedPreferences.Editor editor = deviceSettings.edit();
|
SharedPreferences.Editor editor = deviceSettings.edit();
|
||||||
editor.putString("deviceName", name);
|
editor.putString("deviceName", name);
|
||||||
editor.putString("deviceType", Device.DeviceType.Phone.toString());
|
editor.putString("deviceType", Device.DeviceType.Phone.toString());
|
||||||
editor.putString("publicKey", Base64.encodeToString(keyPair.getPublic().getEncoded(), 0).trim() + "\n");
|
editor.putString("certificate", encodedCertificate);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
Mockito.when(context.getSharedPreferences(eq(deviceId), eq(Context.MODE_PRIVATE))).thenReturn(deviceSettings);
|
Mockito.when(context.getSharedPreferences(eq(deviceId), eq(Context.MODE_PRIVATE))).thenReturn(deviceSettings);
|
||||||
|
|
||||||
@ -115,23 +125,25 @@ public class DeviceTest {
|
|||||||
|
|
||||||
// Basic paired device testing
|
// Basic paired device testing
|
||||||
@Test
|
@Test
|
||||||
public void testDevice() {
|
public void testDevice() throws CertificateException {
|
||||||
Device device = new Device(context, "testDevice");
|
Device device = new Device(context, "testDevice");
|
||||||
|
|
||||||
assertEquals(device.getDeviceId(), "testDevice");
|
assertEquals(device.getDeviceId(), "testDevice");
|
||||||
assertEquals(device.getDeviceType(), Device.DeviceType.Phone);
|
assertEquals(device.getDeviceType(), Device.DeviceType.Phone);
|
||||||
assertEquals(device.getName(), "Test Device");
|
assertEquals(device.getName(), "Test Device");
|
||||||
assertTrue(device.isPaired());
|
assertTrue(device.isPaired());
|
||||||
|
assertNotNull(device.certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPairingDoneWithCertificate() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
public void testPairingDone() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, CertificateException {
|
||||||
|
|
||||||
NetworkPacket fakeNetworkPacket = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY);
|
NetworkPacket fakeNetworkPacket = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY);
|
||||||
fakeNetworkPacket.set("deviceId", "unpairedTestDevice");
|
String deviceId = "unpairedTestDevice";
|
||||||
|
fakeNetworkPacket.set("deviceId", deviceId);
|
||||||
fakeNetworkPacket.set("deviceName", "Unpaired Test Device");
|
fakeNetworkPacket.set("deviceName", "Unpaired Test Device");
|
||||||
fakeNetworkPacket.set("protocolVersion", DeviceHelper.ProtocolVersion);
|
fakeNetworkPacket.set("protocolVersion", DeviceHelper.ProtocolVersion);
|
||||||
fakeNetworkPacket.set("deviceType", Device.DeviceType.Phone.toString());
|
fakeNetworkPacket.set("deviceType", Device.DeviceType.Phone.toString());
|
||||||
fakeNetworkPacket.set("certificate",
|
String certificateString =
|
||||||
"MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2\n" +
|
"MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2\n" +
|
||||||
"NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u\n" +
|
"NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u\n" +
|
||||||
"bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5\n" +
|
"bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5\n" +
|
||||||
@ -147,16 +159,18 @@ public class DeviceTest {
|
|||||||
"I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r\n" +
|
"I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r\n" +
|
||||||
"/urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl\n" +
|
"/urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl\n" +
|
||||||
"yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg\n" +
|
"yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg\n" +
|
||||||
"7n+KOQ==");
|
"7n+KOQ==";
|
||||||
|
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
||||||
|
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
|
||||||
|
|
||||||
LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class);
|
LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class);
|
||||||
Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider");
|
Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider");
|
||||||
LanLink link = Mockito.mock(LanLink.class);
|
LanLink link = Mockito.mock(LanLink.class);
|
||||||
Mockito.when(link.getLinkProvider()).thenReturn(linkProvider);
|
Mockito.when(link.getLinkProvider()).thenReturn(linkProvider);
|
||||||
Device device = new Device(context, fakeNetworkPacket, link);
|
Device device = new Device(context, deviceId, certificate, fakeNetworkPacket, link);
|
||||||
|
|
||||||
assertNotNull(device);
|
assertNotNull(device);
|
||||||
assertEquals(device.getDeviceId(), "unpairedTestDevice");
|
assertEquals(device.getDeviceId(), deviceId);
|
||||||
assertEquals(device.getName(), "Unpaired Test Device");
|
assertEquals(device.getName(), "Unpaired Test Device");
|
||||||
assertEquals(device.getDeviceType(), Device.DeviceType.Phone);
|
assertEquals(device.getDeviceType(), Device.DeviceType.Phone);
|
||||||
assertNotNull(device.certificate);
|
assertNotNull(device.certificate);
|
||||||
@ -180,7 +194,7 @@ public class DeviceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnpair() {
|
public void testUnpair() throws CertificateException {
|
||||||
PairingHandler.PairingCallback pairingCallback = Mockito.mock(PairingHandler.PairingCallback.class);
|
PairingHandler.PairingCallback pairingCallback = Mockito.mock(PairingHandler.PairingCallback.class);
|
||||||
Device device = new Device(context, "testDevice");
|
Device device = new Device(context, "testDevice");
|
||||||
device.addPairingCallback(pairingCallback);
|
device.addPairingCallback(pairingCallback);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user