I upgraded spring-integration-sftp from 5.2.3 RELEASE to v6.1.2 and injected a sshclient into DefaultSftpSessionFactory during session creation. I got "SshException: Server key did not validate". I believe this is known_hosts issue because if serverKeyVerified is set as AcceptAllServerKeyVerifier.INSTANCE (not using known_hosts), the app does not use known_hosts and works without error. The known_hosts file (/known_hosts/sfeg-public-key) is the same as the old working version, so something is wrong in coding? thanks!
Private key: /known_hosts/sfeg-private-key (1678 bytes)
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAosLaHA9mGBZrISlSp4u5xYsHAoa/jnqt1hUCmVBccfywTZiI
yvkE7y+QHhU1oYpCyAOXSojIIpTnOYhUsJHuB9hyLsnmNWWr2pnydyayRuj9gvsO
<...... ommitt 21 lines .............>
3rLna1UdXUVuO/KctuYdom5Ii2BAdIba7FRMRH9OuEkHbb3CNXADKkAbs7eTCNcY
i+4GBBKBJjP8EXdtIwc3Wf0OlPf4O2i0hQojYf8WUsHwWrZJ5LqMiw==
-----END RSA PRIVATE KEY-----
Known Hosts: /known_hosts/sfeg-public-key
sfeg.test.ag.test.group.ca,172.64.41.187 ssh-rsa AAAAB3NzaC<....ommitted....>th0Oqdc=
Coding (some were from spring-intergration-sftp-6.1.2 org.springframework.integration.sftp.session.doInitInnerClient()):
@Bean
public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() throws InvalidConfigException, IOException {
SshClient sshClient = SshClient.setUpDefaultClient();
sshClient.setKeyExchangeFactories(NamedFactory.setUpTransformedFactories(
false,
BuiltinDHFactories.VALUES,
ClientBuilder.DH2KEX
));
boolean isAllowUnknownKeys = properties.isAllowUnknownKeys();
sshClient.setSignatureFactories(new ArrayList<>(BuiltinSignatures.VALUES));
ServerKeyVerifier serverKeyVerifier =
isAllowUnknownKeys ? AcceptAllServerKeyVerifier.INSTANCE : RejectAllServerKeyVerifier.INSTANCE;
if (!isAllowUnknownKeys) {
String knownHostFileStr = properties.getKnownHostFile();
if (StringUtils.isBlank(knownHostFileStr))
throw new KnownHostFileNotDefinedException("Must define known_hosts file when allow-unknown-keys is false. ");
File knownHostFile = new File(knownHostFileStr);
if (!knownHostFile.exists())
throw new KnownHostFileNotFoundException("Cannot find known_hosts file when allow-unknown-keys is false.");
logger.info("SFTP Known Hosts");
// properties.getKnownHostFile() -> "/known_hosts/sfeg-public-key"
Resource resource = resourceLoader.getResource("file:"+properties.getKnownHostFile());
logger.info("SFTP Known Hosts: length = {}", resource.contentLength());
logger.info("SFTP Known Hosts: content = {}", resource.getContentAsString(Charset.defaultCharset()));
serverKeyVerifier = new ResourceKnownHostsServerKeyVerifier(resource);
}
sshClient.setServerKeyVerifier(serverKeyVerifier);
sshClient.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(properties.getPassword()));
if (properties.getSshPrivateKey() != null) {
if(!(new File(properties.getSshPrivateKey()).exists()))
throw new KnownHostFileNotDefinedException("Cannot find known_hosts file private key file. ");
logger.info("SFTP Configuration: setPrivateKey");
Resource resource = resourceLoader.getResource("file:"+properties.getSshPrivateKey());;
logger.info("SFTP Configuration: privateKey - length {}", resource.contentLength());
logger.info("SFTP Configuration: privateKey - content {}", resource.getContentAsString(Charset.defaultCharset()));
IoResource<Resource> privateKeyResource =
new AbstractIoResource<>(Resource.class, resource) {
@Override
public InputStream openInputStream() throws IOException {
return getResourceValue().getInputStream();
}
};
try {
Collection<KeyPair> keys =
SecurityUtils.getKeyPairResourceParser()
.loadKeyPairs(null, privateKeyResource,
FilePasswordProvider.of(properties.getSshPrivatePassphrase()));
sshClient.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(keys));
}
catch (GeneralSecurityException ex) {
throw new IOException("Cannot load private key: " + resource.getFilename(), ex);
}
}
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(sshClient, true);
factory.setHost(properties.getHost());
factory.setPort(properties.getPort());
factory.setUser(properties.getUsername());
CachingSessionFactory<SftpClient.DirEntry> cachingSessionFactory = new CachingSessionFactory<>(factory);
this.properties.getServerAliveInterval().ifPresent(timeout -> cachingSessionFactory.setSessionWaitTimeout(timeout));
return cachingSessionFactory;
}
Logging:
2024-01-30 22:24:00.197 DEBUG 1 --- []-nio2-thread-2] [,] o.a.s.client.session.ClientSessionImpl : encode(ClientSessionImpl[[email protected]/142.34.91.187:22]) packet #1 sending command=30[30] len=133
2024-01-30 22:24:00.197 DEBUG 1 --- []-nio2-thread-2] [,] o.a.sshd.common.io.nio2.Nio2Session : writeBuffer(Nio2Session[local=/10.97.38.254:44294, remote=sfeg.test.ag..ca/142.34.91.187:22]) writing 152 bytes
2024-01-30 22:24:00.206 DEBUG 1 --- []-nio2-thread-1] [,] o.a.s.client.session.ClientSessionImpl : doHandleMessage(ClientSessionImpl[[email protected]/142.34.91.187:22]) process #1 31
2024-01-30 22:24:00.207 DEBUG 1 --- []-nio2-thread-1] [,] org.apache.sshd.client.kex.DHGClient : next(DHGClient[diffie-hellman-group1-sha1])[ClientSessionImpl[[email protected]/142.34.91.187:22]] process command=SSH_MSG_KEXDH_REPLY
2024-01-30 22:24:00.282 DEBUG 1 --- []-nio2-thread-1] [,] o.a.s.client.session.ClientSessionImpl : setServerKey(ClientSessionImpl[[email protected]/142.34.91.187:22]) keyType=ssh-dss, digest=SHA256:20q6x+yl2wYUlt7cFEqziDKLra8z9v12jTxFscOE7Uc
2024-01-30 22:24:00.283 DEBUG 1 --- []-nio2-thread-1] [,] o.a.s.client.session.ClientSessionImpl : handleKexMessage(ClientSessionImpl[[email protected]/142.34.91.187:22])[diffie-hellman-group1-sha1] KEX processing complete after cmd=31
2024-01-30 22:24:00.289 DEBUG 1 --- []-nio2-thread-1] [,] o.a.s.client.session.ClientSessionImpl : checkKeys(ClientSessionImpl[[email protected]/142.34.91.187:22]) key=ssh-dss-SHA256:20q6x+yl2wYUlt7cFEqziDKLra8z9v12jTxFscOE7Uc, verified=false
2024-01-30 22:24:00.290 DEBUG 1 --- []-nio2-thread-1] [,] o.a.sshd.common.io.nio2.Nio2Session : handleReadCycleFailure(Nio2Session[local=/10.97.38.254:44294, remote=sfeg.test.ag..ca/142.34.91.187:22]) SshException after 91798977 nanos at read cycle=3: Server key did not validate
2024-01-30 22:24:00.290 DEBUG 1 --- []-nio2-thread-1] [,] o.a.sshd.common.io.nio2.Nio2Session : exceptionCaught(Nio2Session[local=/10.97.38.254:44294, remote=sfeg.test.ag..ca/142.34.91.187:22]) caught SshException[Server key did not validate] - calling handler
2024-01-30 22:24:00.290 DEBUG 1 --- []-nio2-thread-1] [,] o.a.s.client.session.ClientSessionImpl : signalAuthFailure(ClientSessionImpl[[email protected]/142.34.91.187:22]) type=SshException, signalled=true, first=false: Server key did not validate
2024-01-30 22:24:00.291 WARN 1 --- []-nio2-thread-1] [,] o.a.s.client.session.ClientSessionImpl : exceptionCaught(ClientSessionImpl[[email protected]/142.34.91.187:22])[state=Opened] SshException: Server key did not validate
org.apache.sshd.common.SshException: Server key did not validate
at org.apache.sshd.client.session.AbstractClientSession.checkKeys(AbstractClientSession.java:629)
at org.apache.sshd.common.session.helpers.AbstractSession.handleKexMessage(AbstractSession.java:726)
at org.apache.sshd.common.session.helpers.AbstractSession.doHandleMessage(AbstractSession.java:590)
at org.apache.sshd.common.session.helpers.AbstractSession.lambda$handleMessage$0(AbstractSession.java:522)
at org.apache.sshd.common.util.threads.ThreadUtils.runAsInternal(ThreadUtils.java:68)
at org.apache.sshd.common.session.helpers.AbstractSession.handleMessage(AbstractSession.java:521)
at org.apache.sshd.common.session.helpers.AbstractSession.decode(AbstractSession.java:1639)
at org.apache.sshd.common.session.helpers.AbstractSession.messageReceived(AbstractSession.java:482)
at org.apache.sshd.common.session.helpers.AbstractSessionIoHandler.messageReceived(AbstractSessionIoHandler.java:64)
at org.apache.sshd.common.io.nio2.Nio2Session.handleReadCycleCompletion(Nio2Session.java:407)
at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:380)
at org.apache.sshd.common.io.nio2.Nio2Session$1.onCompleted(Nio2Session.java:375)
at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.lambda$completed$0(Nio2CompletionHandler.java:38)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at org.apache.sshd.common.io.nio2.Nio2CompletionHandler.completed(Nio2CompletionHandler.java:37)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
at java.base/sun.nio.ch.Invoker$2.run(Unknown Source)
at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)