From 359a020a3ddd87cabe9a11b49786ad7d72339110 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 23 Oct 2023 21:23:18 +0200 Subject: [PATCH 1/7] Refactor for static access. --- .../sftp/auth/SFTPAgentAuthentication.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java index f7dd92c4a3e..941f2881a0d 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java @@ -84,9 +84,9 @@ public Boolean authenticate(final Host bookmark, final LoginCallback prompt, fin if(log.isWarnEnabled()) { log.warn(String.format("Only read specific key %s from SSH agent with IdentitiesOnly configuration", identity)); } - identities = this.isPrivateKey(identity) ? - this.identityFromPrivateKey(identity) : - this.identityFromPublicKey(identity); + identities = Collections.singleton(isPrivateKey(identity) ? + identityFromPrivateKey(identity) : + identityFromPublicKey(identity)); } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); @@ -143,29 +143,29 @@ protected Collection filter(final Credentials credentials, final Colle return identities; } - private boolean isPrivateKey(final Local identity) throws AccessDeniedException, IOException { + private static boolean isPrivateKey(final Local identity) throws AccessDeniedException, IOException { final KeyFormat format = KeyProviderUtil.detectKeyFileFormat( new InputStreamReader(identity.getInputStream()), true); return format != KeyFormat.Unknown; } - private Collection identityFromPrivateKey(final Local identity) throws IOException, AccessDeniedException { + private static Identity identityFromPrivateKey(final Local identity) throws IOException, AccessDeniedException { final File pubKey = OpenSSHKeyFileUtil.getPublicKeyFile(new File(identity.getAbsolute())); if(pubKey != null) { - return this.identityFromPublicKey(LocalFactory.get(pubKey.getAbsolutePath())); + return identityFromPublicKey(LocalFactory.get(pubKey.getAbsolutePath())); } log.warn(String.format("Unable to find public key file for identity %s", identity)); - return Collections.emptyList(); + return null; } - private Collection identityFromPublicKey(final Local identity) throws IOException, AccessDeniedException { + private static Identity identityFromPublicKey(final Local identity) throws IOException, AccessDeniedException { final List lines = IOUtils.readLines(identity.getInputStream(), Charset.defaultCharset()); for(String line : lines) { final String keydata = line.trim(); if(StringUtils.isNotBlank(keydata)) { String[] parts = keydata.split("\\s+"); if(parts.length >= 2) { - return Collections.singletonList(new CustomIdentity(Base64.decodeBase64(parts[1]))); + return new CustomIdentity(Base64.decodeBase64(parts[1])); } } } From 66e947eed36e7659a65a1d8f41eab97e0414accd Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 23 Oct 2023 21:31:05 +0200 Subject: [PATCH 2/7] Refactor to not throw failure when unable to read public key. --- .../sftp/auth/SFTPAgentAuthentication.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java index 941f2881a0d..874f1617865 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPAgentAuthentication.java @@ -80,13 +80,20 @@ public Boolean authenticate(final Host bookmark, final LoginCallback prompt, fin final Credentials configuration = new OpenSSHCredentialsConfigurator().configure(bookmark); if(configuration.isPublicKeyAuthentication()) { try { - final Local identity = configuration.getIdentity(); + final Local setting = configuration.getIdentity(); if(log.isWarnEnabled()) { - log.warn(String.format("Only read specific key %s from SSH agent with IdentitiesOnly configuration", identity)); + log.warn(String.format("Only read specific key %s from SSH agent with IdentitiesOnly configuration", setting)); + } + final Identity identity = isPrivateKey(setting) ? + identityFromPrivateKey(setting) : + identityFromPublicKey(setting); + if(identity != null) { + identities = Collections.singleton(identity); + } + else { + log.warn(String.format("Missing public key for %s", setting)); + identities = Collections.emptyList(); } - identities = Collections.singleton(isPrivateKey(identity) ? - identityFromPrivateKey(identity) : - identityFromPublicKey(identity)); } catch(IOException e) { throw new DefaultIOExceptionMappingService().map(e); @@ -143,13 +150,13 @@ protected Collection filter(final Credentials credentials, final Colle return identities; } - private static boolean isPrivateKey(final Local identity) throws AccessDeniedException, IOException { + static boolean isPrivateKey(final Local identity) throws AccessDeniedException, IOException { final KeyFormat format = KeyProviderUtil.detectKeyFileFormat( new InputStreamReader(identity.getInputStream()), true); return format != KeyFormat.Unknown; } - private static Identity identityFromPrivateKey(final Local identity) throws IOException, AccessDeniedException { + static Identity identityFromPrivateKey(final Local identity) throws IOException, AccessDeniedException { final File pubKey = OpenSSHKeyFileUtil.getPublicKeyFile(new File(identity.getAbsolute())); if(pubKey != null) { return identityFromPublicKey(LocalFactory.get(pubKey.getAbsolutePath())); @@ -158,7 +165,7 @@ private static Identity identityFromPrivateKey(final Local identity) throws IOEx return null; } - private static Identity identityFromPublicKey(final Local identity) throws IOException, AccessDeniedException { + static Identity identityFromPublicKey(final Local identity) throws IOException, AccessDeniedException { final List lines = IOUtils.readLines(identity.getInputStream(), Charset.defaultCharset()); for(String line : lines) { final String keydata = line.trim(); @@ -169,6 +176,7 @@ private static Identity identityFromPublicKey(final Local identity) throws IOExc } } } - throw new IOException(String.format("Failure reading public key %s", identity)); + log.warn(String.format("Failure reading public key %s", identity)); + return null; } } \ No newline at end of file From fcfe76124934bd12a80b0d60165125512ebbb7cd Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 23 Oct 2023 21:53:33 +0200 Subject: [PATCH 3/7] Fix #15183. --- .../auth/SFTPPublicKeyAuthentication.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java index e8307a75f7f..7ed512cfaa3 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java @@ -19,6 +19,7 @@ import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; import ch.cyberduck.core.Local; +import ch.cyberduck.core.LocalFactory; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.LoginOptions; @@ -32,11 +33,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicBoolean; +import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyFileUtil; import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider; @@ -65,32 +68,39 @@ public Boolean authenticate(final Host bookmark, final LoginCallback prompt, fin if(log.isDebugEnabled()) { log.debug(String.format("Login using public key authentication with credentials %s", credentials)); } - final Local identity = credentials.getIdentity(); + final Local privKey = credentials.getIdentity(); + final Local pubKey; final FileKeyProvider provider; final AtomicBoolean canceled = new AtomicBoolean(); try { final KeyFormat format = KeyProviderUtil.detectKeyFileFormat( - new InputStreamReader(identity.getInputStream(), StandardCharsets.UTF_8), true); + new InputStreamReader(privKey.getInputStream(), StandardCharsets.UTF_8), true); if(log.isInfoEnabled()) { - log.info(String.format("Reading private key %s with key format %s", identity, format)); + log.info(String.format("Reading private key %s with key format %s", privKey, format)); } switch(format) { case PKCS8: provider = new PKCS8KeyFile.Factory().create(); + pubKey = null; break; case OpenSSH: provider = new OpenSSHKeyFile.Factory().create(); + pubKey = LocalFactory.get(OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute())).getAbsolutePath()); break; case OpenSSHv1: provider = new OpenSSHKeyV1KeyFile.Factory().create(); + pubKey = LocalFactory.get(OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute())).getAbsolutePath()); break; case PuTTY: provider = new PuTTYKeyFile.Factory().create(); + pubKey = null; break; default: - throw new InteroperabilityException(String.format("Unknown key format for file %s", identity.getName())); + throw new InteroperabilityException(String.format("Unknown key format for file %s", privKey.getName())); } - provider.init(new InputStreamReader(identity.getInputStream(), StandardCharsets.UTF_8), new PasswordFinder() { + provider.init(new InputStreamReader(privKey.getInputStream(), StandardCharsets.UTF_8), + pubKey != null ? new InputStreamReader(pubKey.getInputStream(), StandardCharsets.UTF_8) : null, + new PasswordFinder() { @Override public char[] reqPassword(Resource resource) { if(StringUtils.isEmpty(credentials.getIdentityPassphrase())) { @@ -100,7 +110,7 @@ public char[] reqPassword(Resource resource) { LocaleFactory.localizedString("Private key password protected", "Credentials"), String.format("%s (%s)", LocaleFactory.localizedString("Enter the passphrase for the private key file", "Credentials"), - identity.getAbbreviatedPath()), + privKey.getAbbreviatedPath()), new LoginOptions() .icon(bookmark.getProtocol().disk()) .user(false).password(true) From 817077f36ada7dc0ee30c651e56a5cf8d7f23c76 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 23 Oct 2023 22:10:37 +0200 Subject: [PATCH 4/7] Add null checks. --- .../auth/SFTPPublicKeyAuthentication.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java index 7ed512cfaa3..7850ace8e62 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/auth/SFTPPublicKeyAuthentication.java @@ -83,14 +83,28 @@ public Boolean authenticate(final Host bookmark, final LoginCallback prompt, fin provider = new PKCS8KeyFile.Factory().create(); pubKey = null; break; - case OpenSSH: + case OpenSSH: { provider = new OpenSSHKeyFile.Factory().create(); - pubKey = LocalFactory.get(OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute())).getAbsolutePath()); + final File f = OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute())); + if(f != null) { + pubKey = LocalFactory.get(f.getAbsolutePath()); + } + else { + pubKey = null; + } break; - case OpenSSHv1: + } + case OpenSSHv1: { provider = new OpenSSHKeyV1KeyFile.Factory().create(); - pubKey = LocalFactory.get(OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute())).getAbsolutePath()); + final File f = OpenSSHKeyFileUtil.getPublicKeyFile(new File(privKey.getAbsolute())); + if(f != null) { + pubKey = LocalFactory.get(f.getAbsolutePath()); + } + else { + pubKey = null; + } break; + } case PuTTY: provider = new PuTTYKeyFile.Factory().create(); pubKey = null; From 9037efe33531de166f427536da8794ab3f5672eb Mon Sep 17 00:00:00 2001 From: David Kocher Date: Wed, 8 May 2024 14:46:55 +0200 Subject: [PATCH 5/7] Update dependency. --- ssh/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssh/pom.xml b/ssh/pom.xml index 20ed47375f4..678289ab0e5 100644 --- a/ssh/pom.xml +++ b/ssh/pom.xml @@ -45,9 +45,9 @@ ${project.version} - com.hierynomus + ch.iterate.ssh sshj - ${sshj-version} + 0.39.0 com.jcraft From 6a7fc57823794a8e31f3c45c67af62ee467f4852 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Tue, 14 May 2024 13:45:22 +0200 Subject: [PATCH 6/7] Fix exclude. --- ssh/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh/pom.xml b/ssh/pom.xml index 678289ab0e5..3cd87eaa403 100644 --- a/ssh/pom.xml +++ b/ssh/pom.xml @@ -60,7 +60,7 @@ ${jsch-agentproxy-version} - net.schmizz + com.hierynomus sshj From 2316312feeb70aa230fa87a8c4c83bb519c62cda Mon Sep 17 00:00:00 2001 From: David Kocher Date: Tue, 14 May 2024 21:19:16 +0200 Subject: [PATCH 7/7] Update dependency. --- ssh/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh/pom.xml b/ssh/pom.xml index 3cd87eaa403..7b3086f1bbb 100644 --- a/ssh/pom.xml +++ b/ssh/pom.xml @@ -47,7 +47,7 @@ ch.iterate.ssh sshj - 0.39.0 + 0.39.1 com.jcraft