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} +