diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml
index c86927d33..705138204 100644
--- a/main/buildkit/pom.xml
+++ b/main/buildkit/pom.xml
@@ -67,7 +67,7 @@
${project.build.directory}/libs
linux,mac,win
- dbus-java,secret-service,hkdf,java-utils
+ dbus-java,secret-service,kdewallet,hkdf,java-utils
@@ -83,14 +83,14 @@
- copy-linux-secret-service
+ copy-linux-system-keychain-access
prepare-package
copy-dependencies
${project.build.directory}/linux-libs
- dbus-java,secret-service,hkdf,java-utils
+ dbus-java,secret-service,kdewallet,hkdf,java-utils
diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml
index fd2b26c3a..3eaa5c2e7 100644
--- a/main/keychain/pom.xml
+++ b/main/keychain/pom.xml
@@ -24,7 +24,7 @@
org.openjfx
javafx-graphics
-
+
org.apache.commons
@@ -53,6 +53,12 @@
secret-service
+
+
+ org.purejava
+ kdewallet
+
+
org.slf4j
diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java
index 1db94fec6..a6054be2d 100644
--- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java
+++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java
@@ -28,7 +28,7 @@ public abstract class KeychainModule {
@Binds
@IntoSet
- abstract KeychainAccessStrategy bindLinuxSecretServiceKeychainAccess(LinuxSecretServiceKeychainAccess keychainAccessStrategy);
+ abstract KeychainAccessStrategy bindLinuxSystemKeychainAccess(LinuxSystemKeychainAccess keychainAccessStrategy);
@Provides
@Singleton
diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java
new file mode 100644
index 000000000..120bef435
--- /dev/null
+++ b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java
@@ -0,0 +1,121 @@
+package org.cryptomator.keychain;
+
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.kde.KWallet;
+import org.kde.Static;
+import org.purejava.KDEWallet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LinuxKDEWalletKeychainAccessImpl implements KeychainAccessStrategy {
+
+ private final Logger log = LoggerFactory.getLogger(LinuxKDEWalletKeychainAccessImpl.class);
+
+ private final String FOLDER_NAME = "Cryptomator";
+ private final String APP_NAME = "Cryptomator";
+ private DBusConnection connection;
+ private KDEWallet wallet;
+ private int handle = -1;
+
+ public LinuxKDEWalletKeychainAccessImpl() {
+ try {
+ connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION);
+ } catch (DBusException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean isSupported() {
+ try {
+ wallet = new KDEWallet(connection);
+ return wallet.isEnabled();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
+ try {
+ if (walletIsOpen() &&
+ !(wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME)
+ && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1)
+ && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) {
+ log.debug("Passphrase successfully stored.");
+ } else {
+ log.debug("Passphrase was not stored.");
+ }
+ } catch (Exception e) {
+ log.error(e.toString(), e.getCause());
+ throw new KeychainAccessException(e);
+ }
+ }
+
+ @Override
+ public char[] loadPassphrase(String key) throws KeychainAccessException {
+ String password = "";
+ try {
+ if (walletIsOpen()) {
+ password = wallet.readPassword(handle, FOLDER_NAME, key, APP_NAME);
+ log.debug("loadPassphrase: wallet is open.");
+ } else {
+ log.debug("loadPassphrase: wallet is closed.");
+ }
+ return (password.equals("")) ? null : password.toCharArray();
+ } catch (Exception e) {
+ throw new KeychainAccessException(e);
+ }
+ }
+
+ @Override
+ public void deletePassphrase(String key) throws KeychainAccessException {
+ try {
+ if (walletIsOpen()
+ && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME)
+ && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1
+ && wallet.removeEntry(handle, FOLDER_NAME, key, APP_NAME) == 0) {
+ log.debug("Passphrase successfully deleted.");
+ } else {
+ log.debug("Passphrase was not deleted.");
+ }
+ } catch (Exception e) {
+ throw new KeychainAccessException(e);
+ }
+ }
+
+ @Override
+ public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
+ try {
+ if (walletIsOpen()
+ && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME)
+ && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1
+ && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) {
+ log.debug("Passphrase successfully changed.");
+ } else {
+ log.debug("Passphrase could not be changed.");
+ }
+ } catch (Exception e) {
+ throw new KeychainAccessException(e);
+ }
+ }
+
+ private boolean walletIsOpen() throws KeychainAccessException {
+ try {
+ if (wallet.isOpen(Static.DEFAULT_WALLET)) {
+ // This is needed due to KeechainManager loading the passphase directly
+ if (handle == -1) handle = wallet.open(Static.DEFAULT_WALLET, 0, APP_NAME);
+ return true;
+ }
+ wallet.openAsync(Static.DEFAULT_WALLET, 0, APP_NAME, false);
+ wallet.getSignalHandler().await(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5, () -> null);
+ handle = wallet.getSignalHandler().getLastHandledSignal(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5).handle;
+ log.debug("Wallet successfully initialized.");
+ return handle != -1;
+ } catch (Exception e) {
+ throw new KeychainAccessException(e);
+ }
+ }
+}
diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java
similarity index 66%
rename from main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java
rename to main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java
index f11bdbd45..e75d387cb 100644
--- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccess.java
+++ b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java
@@ -7,24 +7,28 @@ import javax.inject.Singleton;
import java.util.Optional;
/**
- * A facade to LinuxSecretServiceKeychainAccessImpl that doesn't depend on libraries that are unavailable on Mac and Windows.
+ * A facade to LinuxSecretServiceKeychainAccessImpl and LinuxKDEWalletKeychainAccessImpl
+ * that depend on libraries that are unavailable on Mac and Windows.
*/
@Singleton
-public class LinuxSecretServiceKeychainAccess implements KeychainAccessStrategy {
+public class LinuxSystemKeychainAccess implements KeychainAccessStrategy {
- // the actual implementation is hidden in this delegate object which is loaded via reflection,
+ // the actual implementation is hidden in this delegate objects which are loaded via reflection,
// as it depends on libraries that aren't necessarily available:
private final Optional delegate;
@Inject
- public LinuxSecretServiceKeychainAccess() {
- this.delegate = constructGnomeKeyringKeychainAccess();
+ public LinuxSystemKeychainAccess() {
+ this.delegate = constructKeychainAccess();
}
- private static Optional constructGnomeKeyringKeychainAccess() {
- try {
+ private static Optional constructKeychainAccess() {
+ try { // is gnome-keyring or kwallet installed?
Class> clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl");
KeychainAccessStrategy instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance();
+ if (instance.isSupported()) return Optional.of(instance);
+ clazz = Class.forName("org.cryptomator.keychain.LinuxKDEWalletKeychainAccessImpl");
+ instance = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance();
return Optional.of(instance);
} catch (Exception e) {
return Optional.empty();
diff --git a/main/pom.xml b/main/pom.xml
index e08f71378..8702b89d9 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -34,6 +34,7 @@
14
3.11
1.1.0
+ 1.0.1
3.10.3
2.1.0
29.0-jre
@@ -168,6 +169,11 @@
secret-service
${secret-service.version}
+
+ org.purejava
+ kdewallet
+ ${kdewallet.version}
+