Skip to content

Commit

Permalink
Merge pull request #15254 from iterate-ch/bugfix/GH-15252
Browse files Browse the repository at this point in the history
Attempt to load public key when available
  • Loading branch information
dkocher committed May 15, 2024
2 parents 216effe + 2316312 commit 1a0604a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 23 deletions.
6 changes: 3 additions & 3 deletions ssh/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.hierynomus</groupId>
<groupId>ch.iterate.ssh</groupId>
<artifactId>sshj</artifactId>
<version>${sshj-version}</version>
<version>0.39.1</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
Expand All @@ -60,7 +60,7 @@
<version>${jsch-agentproxy-version}</version>
<exclusions>
<exclusion>
<groupId>net.schmizz</groupId>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
</exclusion>
</exclusions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = this.isPrivateKey(identity) ?
this.identityFromPrivateKey(identity) :
this.identityFromPublicKey(identity);
}
catch(IOException e) {
throw new DefaultIOExceptionMappingService().map(e);
Expand Down Expand Up @@ -143,32 +150,33 @@ protected Collection<Identity> filter(final Credentials credentials, final Colle
return identities;
}

private 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 Collection<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 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<Identity> identityFromPublicKey(final Local identity) throws IOException, AccessDeniedException {
static Identity identityFromPublicKey(final Local identity) throws IOException, AccessDeniedException {
final List<String> 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]));
}
}
}
throw new IOException(String.format("Failure reading public key %s", identity));
log.warn(String.format("Failure reading public key %s", identity));
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -65,32 +68,53 @@ 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:
case OpenSSH: {
provider = new OpenSSHKeyFile.Factory().create();
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();
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;
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())) {
Expand All @@ -100,7 +124,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)
Expand Down

0 comments on commit 1a0604a

Please sign in to comment.