This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.22.x by this push:
     new a2615d6a58e8 [backport camel-4.22.x] CAMEL-25644: camel-ssh - match 
known_hosts entries by exact host pattern (#28215)
a2615d6a58e8 is described below

commit a2615d6a58e823d12eaa2e80aa6c79da1f65a668
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Oct 21 11:54:28 2026 +0200

    [backport camel-4.22.x] CAMEL-25644: camel-ssh - match known_hosts entries 
by exact host pattern (#28215)
    
    CAMEL-25644: camel-ssh - match known_hosts entries by exact host pattern
    
    Backport of #27821 onto camel-4.22.x.
    
    When knownHostsResource is set, ResourceBasedSSHKeyVerifier parsed
    known_hosts with its own parser. It matched the host field with
    String.contains and used only the first matching line, did not tell
    [host]:port entries from plain ones, skipped comments, markers and CRLF
    lines, had no wildcard, negated or hashed patterns, and looked up an
    endpoint configured by address under its reverse-lookup name.
    
    The verifier now matches entries as OpenSSH does. Each pattern is
    compared, ignoring case, with the host the client connects to (or the
    HostName of the host configuration, resolved once per connection),
    written [host]:port for a port other than 22. Wildcard, negated and
    hashed |1| patterns are supported. Every entry for the host is
    considered, and keys are compared with Apache MINA SSHD. A key in a
    matching @revoked entry, or a key not listed for a host that has
    entries, is rejected whatever failOnUnknownHost says, and a resource or
    line that cannot be read fails every connection.
    
    The upgrade guide note for this branch is on main, in the 4.22.3
    section of the 4.22 guide.
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../org/apache/camel/catalog/components/ssh.json   |   8 +-
 .../apache/camel/catalog/docs/ssh-component.adoc   |  66 ++++
 .../org/apache/camel/component/ssh/ssh.json        |   8 +-
 .../camel-ssh/src/main/docs/ssh-component.adoc     |  66 ++++
 .../component/ssh/ResourceBasedSSHKeyVerifier.java | 342 +++++++++++++------
 .../camel/component/ssh/SshConfiguration.java      |  15 +-
 .../org/apache/camel/component/ssh/SshHelper.java  |  29 +-
 .../ssh/ResourceBasedSSHKeyVerifierTest.java       | 370 +++++++++++++++++++--
 .../ssh/SshComponentKnownHostEdDSATest.java        |  13 +-
 .../ssh/SshComponentKnownHostIPv6Test.java         | 183 ++++++++++
 .../component/ssh/SshComponentKnownHostTest.java   | 146 +++++++-
 .../component/ssh/SshComponentTestSupport.java     |  40 ++-
 .../camel-ssh/src/test/resources/known_hosts_eddsa |   1 -
 .../camel-ssh/src/test/resources/known_hosts_valid |   1 -
 .../component/dsl/SshComponentBuilderFactory.java  |  16 +-
 .../endpoint/dsl/SshEndpointBuilderFactory.java    |  75 +++--
 16 files changed, 1195 insertions(+), 184 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json
index c6be7ef70d99..9eaf248f72ac 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json
@@ -47,11 +47,11 @@
     "certResource": { "index": 19, "kind": "property", "displayName": "Cert 
Resource", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the resource path of the certificate to 
use for Authentication. Will use ResourceHelperKeyPairProvider  [...]
     "certResourcePassword": { "index": 20, "kind": "property", "displayName": 
"Cert Resource Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in loading 
certResource, if certResource is a [...]
     "ciphers": { "index": 21, "kind": "property", "displayName": "Ciphers", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
ciphers in their order of preference." },
-    "failOnUnknownHost": { "index": 22, "kind": "property", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connect [...]
+    "failOnUnknownHost": { "index": 22, "kind": "property", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connect [...]
     "kex": { "index": 23, "kind": "property", "displayName": "Kex", "group": 
"security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported key 
exchange algorithms in their order of preference." },
     "keyPairProvider": { "index": 24, "kind": "property", "displayName": "Key 
Pair Provider", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.sshd.common.keyprovider.KeyPairProvider", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the KeyPairProvider reference to use when 
connecting using Cer [...]
     "keyType": { "index": 25, "kind": "property", "displayName": "Key Type", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the key type to pass to the 
KeyPairProvider as part of authentication. KeyPairProvider.loadKey(...) will be 
passe [...]
-    "knownHostsResource": { "index": 26, "kind": "property", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the SS [...]
+    "knownHostsResource": { "index": 26, "kind": "property", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the SS [...]
     "macs": { "index": 27, "kind": "property", "displayName": "Macs", "group": 
"security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
message authentication code algorithms in their order of preference. The MAC 
algor [...]
     "password": { "index": 28, "kind": "property", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in connecting to 
remote SSH server. Requires keyPairProvider to be set  [...]
     "signatures": { "index": 29, "kind": "property", "displayName": 
"Signatures", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
signature algorithms in their order of preference." },
@@ -102,11 +102,11 @@
     "certResource": { "index": 35, "kind": "parameter", "displayName": "Cert 
Resource", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the resource path of the certificate to 
use for Authentication. Will use ResourceHelperKeyPairProvider [...]
     "certResourcePassword": { "index": 36, "kind": "parameter", "displayName": 
"Cert Resource Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in loading 
certResource, if certResource is  [...]
     "ciphers": { "index": 37, "kind": "parameter", "displayName": "Ciphers", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
ciphers in their order of preference." },
-    "failOnUnknownHost": { "index": 38, "kind": "parameter", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connec [...]
+    "failOnUnknownHost": { "index": 38, "kind": "parameter", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connec [...]
     "kex": { "index": 39, "kind": "parameter", "displayName": "Kex", "group": 
"security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported key 
exchange algorithms in their order of preference." },
     "keyPairProvider": { "index": 40, "kind": "parameter", "displayName": "Key 
Pair Provider", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.sshd.common.keyprovider.KeyPairProvider", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the KeyPairProvider reference to use when 
connecting using Ce [...]
     "keyType": { "index": 41, "kind": "parameter", "displayName": "Key Type", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the key type to pass to the 
KeyPairProvider as part of authentication. KeyPairProvider.loadKey(...) will be 
pass [...]
-    "knownHostsResource": { "index": 42, "kind": "parameter", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the S [...]
+    "knownHostsResource": { "index": 42, "kind": "parameter", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the S [...]
     "macs": { "index": 43, "kind": "parameter", "displayName": "Macs", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
message authentication code algorithms in their order of preference. The MAC 
algo [...]
     "password": { "index": 44, "kind": "parameter", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in connecting to 
remote SSH server. Requires keyPairProvider to be set [...]
     "signatures": { "index": 45, "kind": "parameter", "displayName": 
"Signatures", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
signature algorithms in their order of preference." },
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc
index 698ec5f305da..e1b2e38de453 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc
@@ -163,6 +163,72 @@ YAML::
 An example of using Public Key authentication is provided in
 `examples/camel-example-ssh-security`.
 
+=== Server host key verification
+
+The SSH Component verifies the host key of the SSH server only when the 
`knownHostsResource` option
+is set. It refers to a file in the OpenSSH `known_hosts` format, such as the 
output of `ssh-keyscan`.
+Set `failOnUnknownHost` to `true` as well, so that a server without any entry 
in that file is rejected.
+When it is `false` (the default), such a server is accepted and a warning is 
logged.
+
+The entries are matched as OpenSSH matches them:
+
+* Each host pattern of an entry is compared, ignoring case, with the host name 
or address the client
+connects to: the host configured on the endpoint, or the `HostName` that 
`~/.ssh/config` gives for it.
+It is written `host` for port 22 and `[host]:port` for another port. `*` and 
`?` are wildcards, a
+pattern starting with `!` excludes the hosts it matches, and hashed entries 
are supported. So
+`db.example.com` only applies to port 22, `[db.example.com]:2222` to port 
2222, and `*` to every host
+and port. The host is never resolved: an endpoint configured with an address 
only uses the entries for
+that address, and an IPv6 address such as `::1` is not expanded.
+* The server is accepted when an entry of its host lists the key it presents. 
A host can be listed
+with several keys, one per line.
+* The server is rejected when the key it presents is listed in a matching 
`@revoked` entry, or when
+its host has entries without a marker but none of them lists that key, 
whatever the value of
+`failOnUnknownHost`. That option only decides for a host without such an 
entry: `@revoked` and
+`@cert-authority` entries do not list a host.
+* `@cert-authority` entries (OpenSSH host certificates) are not supported and 
are ignored.
+* A `known_hosts` resource that cannot be read, or that has a line that cannot 
be read (for example
+an unknown `@` marker, a missing key, or a key that is not valid base64), 
makes every connection fail.
+
+The following route verifies the host key of a server listening on port 2222 
against a
+`known_hosts` file that contains the line `[db.example.com]:2222 ssh-ed25519 
AAAAC3NzaC1lZDI1NTE5...`.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:status")
+    
.to("ssh://[email protected]:2222?certResource=classpath:test_rsa&knownHostsResource=file:/etc/camel/known_hosts&failOnUnknownHost=true");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:status"/>
+  <to 
uri="ssh://[email protected]:2222?certResource=classpath:test_rsa&amp;knownHostsResource=file:/etc/camel/known_hosts&amp;failOnUnknownHost=true"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:status
+      steps:
+        - to:
+            uri: ssh://[email protected]:2222
+            parameters:
+              certResource: "classpath:test_rsa"
+              knownHostsResource: "file:/etc/camel/known_hosts"
+              failOnUnknownHost: true
+----
+====
+
 [[SSH-CertificateDependencies]]
 === Certificate Dependencies
 
diff --git 
a/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json
 
b/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json
index c6be7ef70d99..9eaf248f72ac 100644
--- 
a/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json
+++ 
b/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json
@@ -47,11 +47,11 @@
     "certResource": { "index": 19, "kind": "property", "displayName": "Cert 
Resource", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the resource path of the certificate to 
use for Authentication. Will use ResourceHelperKeyPairProvider  [...]
     "certResourcePassword": { "index": 20, "kind": "property", "displayName": 
"Cert Resource Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in loading 
certResource, if certResource is a [...]
     "ciphers": { "index": 21, "kind": "property", "displayName": "Ciphers", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
ciphers in their order of preference." },
-    "failOnUnknownHost": { "index": 22, "kind": "property", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connect [...]
+    "failOnUnknownHost": { "index": 22, "kind": "property", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connect [...]
     "kex": { "index": 23, "kind": "property", "displayName": "Kex", "group": 
"security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported key 
exchange algorithms in their order of preference." },
     "keyPairProvider": { "index": 24, "kind": "property", "displayName": "Key 
Pair Provider", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.sshd.common.keyprovider.KeyPairProvider", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the KeyPairProvider reference to use when 
connecting using Cer [...]
     "keyType": { "index": 25, "kind": "property", "displayName": "Key Type", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the key type to pass to the 
KeyPairProvider as part of authentication. KeyPairProvider.loadKey(...) will be 
passe [...]
-    "knownHostsResource": { "index": 26, "kind": "property", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the SS [...]
+    "knownHostsResource": { "index": 26, "kind": "property", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the SS [...]
     "macs": { "index": 27, "kind": "property", "displayName": "Macs", "group": 
"security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
message authentication code algorithms in their order of preference. The MAC 
algor [...]
     "password": { "index": 28, "kind": "property", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in connecting to 
remote SSH server. Requires keyPairProvider to be set  [...]
     "signatures": { "index": 29, "kind": "property", "displayName": 
"Signatures", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
signature algorithms in their order of preference." },
@@ -102,11 +102,11 @@
     "certResource": { "index": 35, "kind": "parameter", "displayName": "Cert 
Resource", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the resource path of the certificate to 
use for Authentication. Will use ResourceHelperKeyPairProvider [...]
     "certResourcePassword": { "index": 36, "kind": "parameter", "displayName": 
"Cert Resource Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in loading 
certResource, if certResource is  [...]
     "ciphers": { "index": 37, "kind": "parameter", "displayName": "Ciphers", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
ciphers in their order of preference." },
-    "failOnUnknownHost": { "index": 38, "kind": "parameter", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connec [...]
+    "failOnUnknownHost": { "index": 38, "kind": "parameter", "displayName": 
"Fail On Unknown Host", "group": "security", "label": "security", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"insecureValue": "false", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Specifies whether a connec [...]
     "kex": { "index": 39, "kind": "parameter", "displayName": "Kex", "group": 
"security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported key 
exchange algorithms in their order of preference." },
     "keyPairProvider": { "index": 40, "kind": "parameter", "displayName": "Key 
Pair Provider", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.sshd.common.keyprovider.KeyPairProvider", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the KeyPairProvider reference to use when 
connecting using Ce [...]
     "keyType": { "index": 41, "kind": "parameter", "displayName": "Key Type", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the key type to pass to the 
KeyPairProvider as part of authentication. KeyPairProvider.loadKey(...) will be 
pass [...]
-    "knownHostsResource": { "index": 42, "kind": "parameter", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the S [...]
+    "knownHostsResource": { "index": 42, "kind": "parameter", "displayName": 
"Known Hosts Resource", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:ssl", 
"configurationClass": "org.apache.camel.component.ssh.SshConfiguration", 
"configurationField": "configuration", "description": "Sets the resource path 
for a known_hosts file used to verify the S [...]
     "macs": { "index": 43, "kind": "parameter", "displayName": "Macs", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
message authentication code algorithms in their order of preference. The MAC 
algo [...]
     "password": { "index": 44, "kind": "parameter", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Sets the password to use in connecting to 
remote SSH server. Requires keyPairProvider to be set [...]
     "signatures": { "index": 45, "kind": "parameter", "displayName": 
"Signatures", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.ssh.SshConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of allowed\/supported 
signature algorithms in their order of preference." },
diff --git a/components/camel-ssh/src/main/docs/ssh-component.adoc 
b/components/camel-ssh/src/main/docs/ssh-component.adoc
index 698ec5f305da..e1b2e38de453 100644
--- a/components/camel-ssh/src/main/docs/ssh-component.adoc
+++ b/components/camel-ssh/src/main/docs/ssh-component.adoc
@@ -163,6 +163,72 @@ YAML::
 An example of using Public Key authentication is provided in
 `examples/camel-example-ssh-security`.
 
+=== Server host key verification
+
+The SSH Component verifies the host key of the SSH server only when the 
`knownHostsResource` option
+is set. It refers to a file in the OpenSSH `known_hosts` format, such as the 
output of `ssh-keyscan`.
+Set `failOnUnknownHost` to `true` as well, so that a server without any entry 
in that file is rejected.
+When it is `false` (the default), such a server is accepted and a warning is 
logged.
+
+The entries are matched as OpenSSH matches them:
+
+* Each host pattern of an entry is compared, ignoring case, with the host name 
or address the client
+connects to: the host configured on the endpoint, or the `HostName` that 
`~/.ssh/config` gives for it.
+It is written `host` for port 22 and `[host]:port` for another port. `*` and 
`?` are wildcards, a
+pattern starting with `!` excludes the hosts it matches, and hashed entries 
are supported. So
+`db.example.com` only applies to port 22, `[db.example.com]:2222` to port 
2222, and `*` to every host
+and port. The host is never resolved: an endpoint configured with an address 
only uses the entries for
+that address, and an IPv6 address such as `::1` is not expanded.
+* The server is accepted when an entry of its host lists the key it presents. 
A host can be listed
+with several keys, one per line.
+* The server is rejected when the key it presents is listed in a matching 
`@revoked` entry, or when
+its host has entries without a marker but none of them lists that key, 
whatever the value of
+`failOnUnknownHost`. That option only decides for a host without such an 
entry: `@revoked` and
+`@cert-authority` entries do not list a host.
+* `@cert-authority` entries (OpenSSH host certificates) are not supported and 
are ignored.
+* A `known_hosts` resource that cannot be read, or that has a line that cannot 
be read (for example
+an unknown `@` marker, a missing key, or a key that is not valid base64), 
makes every connection fail.
+
+The following route verifies the host key of a server listening on port 2222 
against a
+`known_hosts` file that contains the line `[db.example.com]:2222 ssh-ed25519 
AAAAC3NzaC1lZDI1NTE5...`.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:status")
+    
.to("ssh://[email protected]:2222?certResource=classpath:test_rsa&knownHostsResource=file:/etc/camel/known_hosts&failOnUnknownHost=true");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:status"/>
+  <to 
uri="ssh://[email protected]:2222?certResource=classpath:test_rsa&amp;knownHostsResource=file:/etc/camel/known_hosts&amp;failOnUnknownHost=true"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:status
+      steps:
+        - to:
+            uri: ssh://[email protected]:2222
+            parameters:
+              certResource: "classpath:test_rsa"
+              knownHostsResource: "file:/etc/camel/known_hosts"
+              failOnUnknownHost: true
+----
+====
+
 [[SSH-CertificateDependencies]]
 === Certificate Dependencies
 
diff --git 
a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java
 
b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java
index 1b5556466d72..73bd032342bb 100644
--- 
a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java
+++ 
b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifier.java
@@ -18,30 +18,66 @@ package org.apache.camel.component.ssh;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigInteger;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
-import java.security.NoSuchAlgorithmException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
 import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
 import java.util.Base64;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.Collections;
+import java.util.Locale;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.support.ResourceHelper;
-import org.apache.camel.util.IOHelper;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
 import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
 import org.apache.sshd.client.session.ClientSession;
-import org.bouncycastle.util.Arrays;
+import org.apache.sshd.client.session.ClientSessionCreator;
+import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * ServerKeyVerifier that takes a camel resource as input file to validate the 
server key against.
- *
+ * <p>
+ * The resource uses the OpenSSH known_hosts format, and its entries are 
matched as OpenSSH matches them. Each host
+ * pattern of an entry is compared, ignoring case, with the host name or 
address the client connects to (the host
+ * configured on the endpoint, or the {@code HostName} that {@code 
~/.ssh/config} gives for it, as it is written and
+ * never resolved through DNS, so an IPv6 address such as {@code ::1} is not 
expanded), written {@code host} for port 22
+ * and {@code [host]:port} for another port: {@code *} and {@code ?} are 
wildcards, a pattern starting with {@code !}
+ * excludes the hosts it matches, and hashed entries are supported. The server 
is accepted when an entry of its host
+ * lists the key it presents. It is rejected when that key is listed in a 
matching {@code @revoked} entry, or when its
+ * host has entries without a marker but none of them lists that key. {@code 
failOnUnknownHost} only decides for a host
+ * without such an entry: {@code @revoked} and {@code @cert-authority} entries 
do not list a host, and host certificates
+ * are not supported. A resource that cannot be read, or that has a line that 
cannot be read, rejects every server.
  */
 public class ResourceBasedSSHKeyVerifier implements ServerKeyVerifier {
+    private static final int DEFAULT_PORT = 22;
+    private static final String MARKER_REVOKED = "@revoked";
+    private static final String MARKER_CERT_AUTHORITY = "@cert-authority";
+    private static final String HASHED_HOST_PREFIX = "|1|";
+    private static final String HASH_ALGORITHM = "HmacSHA1";
+    private static final int HASH_LENGTH = 20;
+
+    /**
+     * The host configuration that the client connected with, which the ssh 
producer and consumer put in the connection
+     * context of the session: the entries are matched against its {@code 
HostName} as it is written. Without it, the
+     * address the client connected to is used, which writes an IPv6 address 
such as {@code ::1} in its full form.
+     *
+     * @since 4.24
+     */
+    public static final AttributeRepository.AttributeKey<HostConfigEntry> 
HOST_CONFIG_ENTRY
+            = new AttributeRepository.AttributeKey<>();
+
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
     private CamelContext camelContext;
@@ -62,21 +98,58 @@ public class ResourceBasedSSHKeyVerifier implements 
ServerKeyVerifier {
     @Override
     public boolean verifyServerKey(ClientSession sshClientSession, 
SocketAddress remoteAddress, PublicKey serverKey) {
         log.debug("Trying to find known_hosts file {}", knownHostsResource);
-        InputStream knownHostsInputStream = null;
-        try {
-            knownHostsInputStream = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext,
-                    knownHostsResource);
-            List<String> possibleTokens = 
getKnownHostsFileTokensForSocketAddress(remoteAddress);
-            log.debug("Trying to match PublicKey against provided known_hosts 
file");
-            PublicKey matchingKey = 
findKeyForServerToken(knownHostsInputStream, possibleTokens);
-            if (matchingKey != null) {
-                log.debug("Found PublicKey match for server");
-                return Arrays.areEqual(matchingKey.getEncoded(), 
serverKey.getEncoded());
-            }
+        String knownHosts;
+        try (InputStream knownHostsInputStream
+                = 
ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, 
knownHostsResource)) {
+            knownHosts = new String(knownHostsInputStream.readAllBytes(), 
StandardCharsets.UTF_8);
         } catch (IOException ioException) {
-            log.debug(String.format("Could not find known_hosts file %s", 
knownHostsResource), ioException);
-        } finally {
-            IOHelper.close(knownHostsInputStream);
+            log.warn("Could not read the known_hosts file {}, connection will 
fail", knownHostsResource, ioException);
+            return false;
+        }
+
+        String server = toKnownHostsName(sshClientSession, remoteAddress);
+        boolean hostListed = false;
+        boolean keyListed = false;
+        int lineNumber = 0;
+        for (String line : knownHosts.split("\\R")) {
+            lineNumber++;
+            String text = line.trim();
+            if (text.isEmpty() || text.charAt(0) == '#') {
+                continue;
+            }
+            // a line that cannot be read could list the server, so it cannot 
be skipped
+            KnownHostsEntry entry = KnownHostsEntry.parse(text, 
sshClientSession);
+            if (entry == null) {
+                log.warn("Line {} of the known_hosts file {} cannot be read, 
connection will fail", lineNumber,
+                        knownHostsResource);
+                return false;
+            }
+            if (server == null || MARKER_CERT_AUTHORITY.equals(entry.marker) 
|| !entry.matches(server)) {
+                continue;
+            }
+            boolean sameKey = isSameKey(entry, serverKey);
+            if (MARKER_REVOKED.equals(entry.marker)) {
+                if (sameKey) {
+                    log.warn("The key presented by {} is marked as revoked in 
the known_hosts file {}", server,
+                            knownHostsResource);
+                    return false;
+                }
+            } else {
+                hostListed = true;
+                if (sameKey) {
+                    keyListed = true;
+                }
+            }
+        }
+
+        if (keyListed) {
+            log.debug("Found PublicKey match for server {}", server);
+            return true;
+        }
+        if (hostListed) {
+            log.warn("The key presented by {} does not match any key listed 
for it in the known_hosts file {}", server,
+                    knownHostsResource);
+            return false;
         }
         if (failOnUnknownHost) {
             log.warn("Could not find matching key for client session, 
connection will fail due to configuration");
@@ -88,105 +161,164 @@ public class ResourceBasedSSHKeyVerifier implements 
ServerKeyVerifier {
         }
     }
 
-    private PublicKey findKeyForServerToken(InputStream knownHostsInputStream, 
List<String> possibleTokens) {
-        String knowHostsLines = 
camelContext.getTypeConverter().convertTo(String.class, knownHostsInputStream);
-        if (knowHostsLines == null) {
-            log.warn("Could not read from the known_hosts file input stream");
-            return null;
+    private boolean isSameKey(KnownHostsEntry entry, PublicKey serverKey) {
+        if (entry.key == null) {
+            log.warn("Unsupported key type {} in the known_hosts file {}", 
entry.keyType, knownHostsResource);
+            return false;
         }
+        return KeyUtils.compareKeys(entry.key, serverKey);
+    }
 
-        for (String s : knowHostsLines.split("\n")) {
-            String[] parts = s.split(" ");
-            if (parts.length != 3) {
-                log.warn("Found malformed entry in known_hosts file");
-                continue;
-            }
-            String entry = parts[0];
-            String key = parts[2];
-            for (String serverToken : possibleTokens) {
-                if (entry.contains(serverToken)) {
-                    try {
-                        return loadKey(key);
-                    } catch (NoSuchAlgorithmException | 
InvalidKeySpecException e) {
-                        log.warn(String.format("Could not load key for server 
token %s", entry), e);
-                    }
-                }
-            }
+    /*
+     * The name OpenSSH looks the server up by: the host name or address the 
client connected to, as the host
+     * configuration writes it and never resolved through DNS, with the port 
the client connected to, written host for
+     * port 22 and [host]:port for another port. The connected address cannot 
give the host as it is written, as it
+     * writes an IPv6 address such as ::1 in its full form.
+     */
+    private static String toKnownHostsName(ClientSession session, 
SocketAddress remoteAddress) {
+        if (!(remoteAddress instanceof InetSocketAddress) && !(remoteAddress 
instanceof SshdSocketAddress)) {
+            return null;
+        }
+        SshdSocketAddress address = 
SshdSocketAddress.toSshdSocketAddress(remoteAddress);
+        String name = address.getHostName();
+        SshdSocketAddress target = session != null ? 
session.getAttribute(ClientSessionCreator.TARGET_SERVER) : null;
+        AttributeRepository context = session != null ? 
session.getConnectionContext() : null;
+        HostConfigEntry hostConfig = context != null ? 
context.getAttribute(HOST_CONFIG_ENTRY) : null;
+        if (target != null) {
+            // a host reached through a jump host, whose HostName SSHD keeps 
as it is written
+            name = target.getHostName();
+        } else if (hostConfig != null && (hostConfig.getProxyJump() == null || 
hostConfig.getProxyJump().isEmpty())) {
+            // the host the client connected to directly, with the host 
configuration it connected with
+            name = hostConfig.getHostName();
         }
-        return null;
+        if (name.length() > 1 && name.charAt(0) == '[' && 
name.charAt(name.length() - 1) == ']') {
+            // an IPv6 address in brackets
+            name = name.substring(1, name.length() - 1);
+        }
+        name = name.toLowerCase(Locale.ROOT);
+        return address.getPort() == DEFAULT_PORT ? name : "[" + name + "]:" + 
address.getPort();
     }
 
-    private List<String> getKnownHostsFileTokensForSocketAddress(SocketAddress 
remoteAddress) {
-        List<String> returnList = new LinkedList<>();
-        if (remoteAddress instanceof InetSocketAddress) {
-            InetSocketAddress inetSocketAddress = (InetSocketAddress) 
remoteAddress;
-
-            String hostName = inetSocketAddress.getHostName();
-            String ipAddress = inetSocketAddress.getAddress().getHostAddress();
-            String remotePort = String.valueOf(inetSocketAddress.getPort());
-
-            returnList.add(hostName);
-            returnList.add("[" + hostName + "]:" + remotePort);
-            returnList.add(ipAddress);
-            returnList.add("[" + ipAddress + "]:" + remotePort);
+    /*
+     * Matches a pattern where * stands for any sequence of characters and ? 
for any single character.
+     */
+    static boolean matchesPattern(String pattern, String name) {
+        int p = 0;
+        int n = 0;
+        int star = -1;
+        int resume = 0;
+        while (n < name.length()) {
+            if (p < pattern.length() && (pattern.charAt(p) == '?' || 
pattern.charAt(p) == name.charAt(n))) {
+                p++;
+                n++;
+            } else if (p < pattern.length() && pattern.charAt(p) == '*') {
+                star = p++;
+                resume = n;
+            } else if (star >= 0) {
+                p = star + 1;
+                n = ++resume;
+            } else {
+                return false;
+            }
         }
-
-        return returnList;
+        while (p < pattern.length() && pattern.charAt(p) == '*') {
+            p++;
+        }
+        return p == pattern.length();
     }
 
     /*
-     * Decode the public key string, which is a base64 encoded string that 
consists
-     * of multiple parts: 1. public key type (ssh-rsa, ssh-dss, ...) 2. binary 
key
-     * data (May consists of multiple parts)
-     *
-     * Each part is composed by two sub-parts 1. Length of the part (4 bytes) 
2.
-     * Binary part (length as defined by 1.)
-     *
-     * Uses SSHPublicKeyHolder to construct the actual PublicKey Object
-     *
-     * Note: Currently only supports RSA and DSA Public keys as required by
-     * https://tools.ietf.org/html/rfc4253#section-6.6
-     *
+     * A line of the known_hosts file: an optional marker, the host patterns 
or a hashed host, and the key. Anything
+     * after the key is a comment.
      */
-    PublicKey loadKey(String key) throws NoSuchAlgorithmException, 
InvalidKeySpecException {
-        SSHPublicKeyHolder sshPublicKeyHolder = new SSHPublicKeyHolder();
-
-        byte[] keyByteArray = Base64.getDecoder().decode(key);
-        int keyByteArrayCursor = 0;
-
-        byte[] tmpData = new byte[4];
-        int tmpCursor = 0;
-
-        boolean getLengthMode = true;
-        while (keyByteArrayCursor < keyByteArray.length) {
-            if (getLengthMode) {
-                if (tmpCursor < 4) {
-                    tmpData[tmpCursor] = keyByteArray[keyByteArrayCursor];
-                    tmpCursor++;
-                    keyByteArrayCursor++;
-                    continue;
-                } else {
-                    tmpCursor = 0;
-                    getLengthMode = false;
-                    tmpData = new byte[byteArrayToInt(tmpData)];
+    private static final class KnownHostsEntry {
+        private final String marker;
+        private final String hosts;
+        private final byte[] salt;
+        private final byte[] hash;
+        private final String keyType;
+        private final PublicKey key;
+
+        private KnownHostsEntry(String marker, String hosts, byte[] salt, 
byte[] hash, String keyType, PublicKey key) {
+            this.marker = marker;
+            this.hosts = hosts;
+            this.salt = salt;
+            this.hash = hash;
+            this.keyType = keyType;
+            this.key = key;
+        }
+
+        /*
+         * Returns null when the line cannot be read. A key of a type that is 
not supported is kept as a null key.
+         */
+        static KnownHostsEntry parse(String line, SessionContext session) {
+            String[] fields = line.split("\\s+");
+            int index = 0;
+            String marker = null;
+            if (fields[0].startsWith("@")) {
+                marker = fields[0];
+                if (!MARKER_REVOKED.equals(marker) && 
!MARKER_CERT_AUTHORITY.equals(marker)) {
+                    return null;
                 }
+                index = 1;
+            }
+            if (fields.length < index + 3) {
+                return null;
             }
-            tmpData[tmpCursor] = keyByteArray[keyByteArrayCursor];
-            tmpCursor++;
-            keyByteArrayCursor++;
-            if (tmpCursor == tmpData.length) {
-                sshPublicKeyHolder.push(tmpData);
-                getLengthMode = true;
-                tmpData = new byte[4];
-                tmpCursor = 0;
+            String hosts = fields[index];
+            try {
+                byte[] salt = null;
+                byte[] hash = null;
+                if (hosts.startsWith("|")) {
+                    // a hashed host: only |1|salt|hash, with a SHA-1 salt and 
hash, is supported
+                    if (!hosts.startsWith(HASHED_HOST_PREFIX)) {
+                        return null;
+                    }
+                    String[] parts = 
hosts.substring(HASHED_HOST_PREFIX.length()).split("\\|", -1);
+                    if (parts.length != 2) {
+                        return null;
+                    }
+                    salt = Base64.getDecoder().decode(parts[0]);
+                    hash = Base64.getDecoder().decode(parts[1]);
+                    if (salt.length != HASH_LENGTH || hash.length != 
HASH_LENGTH) {
+                        return null;
+                    }
+                }
+                PublicKeyEntry keyEntry = 
PublicKeyEntry.parsePublicKeyEntry(fields[index + 1] + " " + fields[index + 2]);
+                PublicKey key = keyEntry.resolvePublicKey(session, 
Collections.emptyMap(), PublicKeyEntryResolver.IGNORING);
+                return new KnownHostsEntry(marker, hosts, salt, hash, 
keyEntry.getKeyType(), key);
+            } catch (IOException | GeneralSecurityException | RuntimeException 
e) {
+                return null;
             }
         }
 
-        return sshPublicKeyHolder.toPublicKey();
-    }
+        boolean matches(String name) {
+            if (hash != null) {
+                return MessageDigest.isEqual(hash, hashOf(name));
+            }
+            boolean matched = false;
+            for (String pattern : hosts.split(",")) {
+                boolean negated = pattern.startsWith("!");
+                String hostPattern = (negated ? pattern.substring(1) : 
pattern).toLowerCase(Locale.ROOT);
+                if (!hostPattern.isEmpty() && matchesPattern(hostPattern, 
name)) {
+                    if (negated) {
+                        return false;
+                    }
+                    matched = true;
+                }
+            }
+            return matched;
+        }
 
-    private int byteArrayToInt(byte[] tmpData) {
-        return new BigInteger(tmpData).intValue();
+        private byte[] hashOf(String name) {
+            try {
+                Mac mac = Mac.getInstance(HASH_ALGORITHM);
+                mac.init(new SecretKeySpec(salt, HASH_ALGORITHM));
+                return mac.doFinal(name.getBytes(StandardCharsets.UTF_8));
+            } catch (GeneralSecurityException e) {
+                throw new IllegalStateException("Cannot compute the hash of a 
known_hosts host", e);
+            }
+        }
     }
 
 }
diff --git 
a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshConfiguration.java
 
b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshConfiguration.java
index 4a9f7bf28216..717c99621345 100644
--- 
a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshConfiguration.java
+++ 
b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshConfiguration.java
@@ -56,11 +56,16 @@ public class SshConfiguration implements Cloneable {
     private long timeout = 30000;
     @UriParam(label = "security", security = "insecure:ssl",
               description = "Sets the resource path for a known_hosts file 
used to verify the SSH server host key."
+                            + " The file uses the OpenSSH known_hosts format, 
and its entries are matched against the"
+                            + " host and port configured on the endpoint. A 
resource that cannot be read, or that has a"
+                            + " line that cannot be read, makes every 
connection fail."
                             + " When not set, the client does not verify the 
server host key against a known_hosts file.")
     private String knownHostsResource;
     @UriParam(label = "security", defaultValue = "false", security = 
"insecure:ssl", insecureValue = "false",
-              description = "Specifies whether a connection to an unknown SSH 
server host key should fail. When false,"
-                            + " host keys that are not present in the 
known_hosts resource are accepted.")
+              description = "Specifies whether a connection to a server whose 
host has no entry without a marker in"
+                            + " the known_hosts resource should fail. When 
false, such a server is accepted. A server"
+                            + " whose host has entries without a marker is 
always rejected when it presents a key that"
+                            + " none of them lists, and a key listed in a 
matching @revoked entry is always rejected.")
     private boolean failOnUnknownHost;
     @UriParam(label = "advanced", defaultValue = Channel.CHANNEL_EXEC)
     private String channelType = Channel.CHANNEL_EXEC;
@@ -301,8 +306,10 @@ public class SshConfiguration implements Cloneable {
     }
 
     /**
-     * Specifies whether a connection to an unknown host should fail or not. 
This value is only checked when the
-     * property knownHosts is set.
+     * Specifies whether a connection to a server whose host has no entry 
without a marker in the known_hosts resource
+     * should fail or not. This value is only checked when the property 
knownHostsResource is set. A server whose host
+     * has entries without a marker is always rejected when it presents a key 
that none of them lists, and a key listed
+     * in a matching @revoked entry is always rejected.
      *
      * @param failOnUnknownHost boolean flag, whether a connection to an 
unknown host should fail
      */
diff --git 
a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshHelper.java
 
b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshHelper.java
index b0703f1f30bd..c26a4bc93fe5 100644
--- 
a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshHelper.java
+++ 
b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshHelper.java
@@ -18,6 +18,7 @@ package org.apache.camel.component.ssh;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
@@ -33,12 +34,16 @@ import org.apache.camel.RuntimeCamelException;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.channel.ClientChannelEvent;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.ConnectFuture;
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.AttributeRepository;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,6 +54,24 @@ public final class SshHelper {
     private SshHelper() {
     }
 
+    /*
+     * The host configuration for the host and port, resolved as 
SshClient.connect(username, host, port) resolves it: the
+     * entry that the host config resolver of the client (by default 
~/.ssh/config) gives, or else the host itself.
+     */
+    static HostConfigEntry resolveHostConfig(SshClient client, String 
username, String host, int port)
+            throws IOException {
+        HostConfigEntryResolver resolver = client.getHostConfigEntryResolver();
+        HostConfigEntry entry
+                = resolver != null ? resolver.resolveEffectiveHost(host, port, 
null, username, null, null) : null;
+        if (entry == null) {
+            int effectivePort = port > 0 ? port : 
SshConfiguration.DEFAULT_SSH_PORT;
+            entry = SshdSocketAddress.isIPv6Address(host)
+                    ? new HostConfigEntry("", host, effectivePort, username, 
null)
+                    : new HostConfigEntry(host, host, effectivePort, username, 
null);
+        }
+        return entry;
+    }
+
     public static SshResult sendExecCommand(Map<String, Object> headers, 
String command, SshEndpoint endpoint, SshClient client)
             throws Exception {
         SshConfiguration configuration = endpoint.getConfiguration();
@@ -63,7 +86,11 @@ public final class SshHelper {
             userName = (String) headers.get(SshConstants.USERNAME_HEADER);
         }
 
-        ConnectFuture connectFuture = client.connect(userName, 
configuration.getHost(), configuration.getPort());
+        // resolve the host configuration once, and connect with it: the host 
key is verified against the host that this
+        // connection goes to, as the host configuration writes it
+        HostConfigEntry hostConfig = resolveHostConfig(client, userName, 
configuration.getHost(), configuration.getPort());
+        ConnectFuture connectFuture = client.connect(hostConfig,
+                
AttributeRepository.ofKeyValuePair(ResourceBasedSSHKeyVerifier.HOST_CONFIG_ENTRY,
 hostConfig), null);
 
         // wait getTimeout milliseconds for connect operation to complete
         connectFuture.await(configuration.getTimeout());
diff --git 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifierTest.java
 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifierTest.java
index f383150e1b05..8bc5f317c638 100644
--- 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifierTest.java
+++ 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/ResourceBasedSSHKeyVerifierTest.java
@@ -16,47 +16,359 @@
  */
 package org.apache.camel.component.ssh;
 
-import java.io.IOException;
 import java.io.InputStream;
-import java.security.NoSuchAlgorithmException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Proxy;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPairGenerator;
 import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
 
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.session.ClientSessionCreator;
+import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.junit.jupiter.api.io.TempDir;
 
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ResourceBasedSSHKeyVerifierTest {
+
+    @TempDir
+    Path dir;
+
+    private CamelContext context;
+    private PublicKey serverKey;
+    private PublicKey otherKey;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        context = new DefaultCamelContext();
+        context.start();
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
+        generator.initialize(new ECGenParameterSpec("secp256r1"));
+        serverKey = generator.generateKeyPair().getPublic();
+        otherKey = generator.generateKeyPair().getPublic();
+    }
+
+    @AfterEach
+    void tearDown() {
+        context.stop();
+    }
+
+    @Test
+    void acceptsTheKeyListedForTheHost() throws Exception {
+        assertTrue(verify(true, "db.example.com", 22, serverKey, 
entry("db.example.com", serverKey)));
+        assertTrue(verify(true, "db.example.com", 22, serverKey, 
entry("web.example.com,db.example.com", serverKey)));
+    }
 
-public class ResourceBasedSSHKeyVerifierTest {
+    @Test
+    void doesNotUseTheEntryOfAHostWhoseNameContainsTheHostName() throws 
Exception {
+        assertFalse(verify(true, "db.example.com", 22, otherKey, 
entry("old-db.example.com", otherKey)));
+        assertFalse(verify(true, "db.example.com", 22, otherKey, 
entry("db.example.com.old", otherKey)));
+    }
 
-    public static final Logger LOG = 
LoggerFactory.getLogger(ResourceBasedSSHKeyVerifierTest.class);
+    @Test
+    void doesNotUseTheEntryOfAnAddressContainingTheAddress() throws Exception {
+        String knownHosts = entry("10.0.0.12", otherKey) + entry("10.0.0.1", 
serverKey);
+        assertFalse(verify(true, "10.0.0.1", 22, otherKey, knownHosts));
+        assertTrue(verify(true, "10.0.0.1", 22, serverKey, knownHosts));
+        assertFalse(verify(true, "10.0.0.1", 22, otherKey, entry("110.0.0.1", 
otherKey)));
+    }
 
     @Test
-    public void verifyAllTypesOfPublicKeys() throws IOException {
-        final Map<String, String> keys = new LinkedHashMap<>();
-        InputStream knownHosts = 
getClass().getResourceAsStream("/known_hosts_types");
-        assertNotNull(knownHosts);
-        IoUtils.readAllLines(knownHosts).forEach(l -> {
-            String[] tab = l.split(" ");
-            if (l.startsWith("#")) {
-                return;
+    void matchesThePort() throws Exception {
+        String knownHosts = entry("[db.example.com]:2222", otherKey);
+        assertFalse(verify(true, "db.example.com", 22, otherKey, knownHosts));
+        assertFalse(verify(true, "db.example.com", 222, otherKey, knownHosts));
+        assertTrue(verify(true, "db.example.com", 2222, otherKey, knownHosts));
+        // an entry without a port only applies to the default port
+        assertFalse(verify(true, "db.example.com", 2222, serverKey, 
entry("db.example.com", serverKey)));
+    }
+
+    @Test
+    void matchesPatternsAgainstTheHostAndPortAsOpenSshWritesThem() throws 
Exception {
+        assertTrue(verify(true, "db.example.com", 2222, serverKey, entry("*", 
serverKey)));
+        assertTrue(verify(true, "db.example.com", 2222, serverKey, 
entry("[*.example.com]:2222", serverKey)));
+        assertFalse(verify(true, "db.example.com", 2222, serverKey, 
entry("*.example.com", serverKey)));
+        assertTrue(verify(true, "db.example.com", 2222, serverKey, 
entry("[db.example.com]:22?2", serverKey)));
+    }
+
+    @Test
+    void matchesNegatedPortQualifiedPatterns() throws Exception {
+        String knownHosts = 
entry("![excluded.example.com]:2222,[db.example.com]:2222", serverKey);
+        for (boolean failOnUnknownHost : new boolean[] { true, false }) {
+            assertTrue(verify(failOnUnknownHost, "db.example.com", 2222, 
serverKey, knownHosts));
+            assertFalse(verify(failOnUnknownHost, "db.example.com", 2222, 
otherKey, knownHosts));
+        }
+        assertFalse(verify(true, "excluded.example.com", 2222, serverKey, 
knownHosts));
+    }
+
+    @Test
+    void matchesWildcardNegatedAndUpperCasePatterns() throws Exception {
+        assertTrue(verify(true, "db.example.com", 22, serverKey, 
entry("*.example.com", serverKey)));
+        assertTrue(verify(true, "db.example.com", 22, serverKey, 
entry("DB.EXAMPLE.COM", serverKey)));
+        String knownHosts = entry("!db.example.com,*.example.com", otherKey);
+        assertFalse(verify(true, "db.example.com", 22, otherKey, knownHosts));
+        assertTrue(verify(true, "www.example.com", 22, otherKey, knownHosts));
+    }
+
+    @Test
+    void matchesHashedEntries() throws Exception {
+        String knownHosts = entry(hashed("db.example.com"), serverKey);
+        assertTrue(verify(true, "db.example.com", 22, serverKey, knownHosts));
+        assertFalse(verify(true, "db.example.com", 22, otherKey, knownHosts));
+        assertFalse(verify(true, "www.example.com", 22, serverKey, 
knownHosts));
+        assertFalse(verify(true, "db.example.com", 2222, serverKey, 
knownHosts));
+        assertTrue(verify(true, "db.example.com", 2222, serverKey, 
entry(hashed("[db.example.com]:2222"), serverKey)));
+    }
+
+    @Test
+    void considersEveryEntryOfTheHost() throws Exception {
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
+        generator.initialize(2048);
+        PublicKey rsaKey = generator.generateKeyPair().getPublic();
+        String knownHosts = entry("db.example.com", rsaKey) + 
entry("db.example.com", serverKey);
+        assertTrue(verify(true, "db.example.com", 22, serverKey, knownHosts));
+        assertTrue(verify(true, "db.example.com", 22, rsaKey, knownHosts));
+    }
+
+    @Test
+    void readsCommentsAndCrlfLineEndings() throws Exception {
+        String knownHosts = "# known hosts\r\n" + "db.example.com " + 
PublicKeyEntry.toString(serverKey) + " db key\r\n";
+        assertTrue(verify(true, "db.example.com", 22, serverKey, knownHosts));
+    }
+
+    @Test
+    void rejectsARevokedKey() throws Exception {
+        String knownHosts = "@revoked * " + PublicKeyEntry.toString(otherKey) 
+ "\n" + entry("db.example.com", otherKey);
+        assertFalse(verify(true, "db.example.com", 22, otherKey, knownHosts));
+        assertFalse(verify(false, "db.example.com", 22, otherKey, knownHosts));
+        assertTrue(verify(true, "db.example.com", 22, serverKey, knownHosts + 
entry("db.example.com", serverKey)));
+    }
+
+    @Test
+    void rejectsARevokedKeyOnEveryPortAndInEveryOrder() throws Exception {
+        String revoked = "@revoked * " + PublicKeyEntry.toString(serverKey) + 
"\n";
+        for (int port : new int[] { 22, 2222 }) {
+            String listed = entry(port == 22 ? "db.example.com" : 
"[db.example.com]:" + port, serverKey);
+            for (boolean failOnUnknownHost : new boolean[] { true, false }) {
+                assertFalse(verify(failOnUnknownHost, "db.example.com", port, 
serverKey, revoked + listed),
+                        () -> "revocation before the entry, port " + port + ", 
failOnUnknownHost " + failOnUnknownHost);
+                assertFalse(verify(failOnUnknownHost, "db.example.com", port, 
serverKey, listed + revoked),
+                        () -> "revocation after the entry, port " + port + ", 
failOnUnknownHost " + failOnUnknownHost);
             }
-            keys.put(tab[1], tab[2]);
-        });
-
-        ResourceBasedSSHKeyVerifier verifier = new 
ResourceBasedSSHKeyVerifier(null, null);
-        keys.forEach((type, data) -> {
-            LOG.info("Checking key {}", type);
-            try {
-                PublicKey publicKey = verifier.loadKey(data);
-                assertNotNull(publicKey);
-            } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
-                throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    @Test
+    void treatsAHostWithOnlyRevokedEntriesAsUnknown() throws Exception {
+        // as OpenSSH does: a revoked entry rejects its key but does not list 
the host
+        String knownHosts = "@revoked db.example.com " + 
PublicKeyEntry.toString(otherKey) + "\n";
+        assertFalse(verify(true, "db.example.com", 22, serverKey, knownHosts));
+        assertTrue(verify(false, "db.example.com", 22, serverKey, knownHosts));
+        assertFalse(verify(false, "db.example.com", 22, otherKey, knownHosts));
+    }
+
+    @Test
+    void rejectsEveryServerWhenALineCannotBeRead() throws Exception {
+        String key = PublicKeyEntry.toString(serverKey);
+        String[] unreadableLines = {
+                "@unknown db.example.com " + key,
+                "@revoked",
+                "db.example.com",
+                "db.example.com ssh-ed25519 not*base64",
+                "|1|c2FsdA==|aGFzaA== " + key,
+                "|1|" + base64(20) + "| " + key,
+                "|1|" + base64(20) + "|" + base64(19) + " " + key,
+                "|1|" + base64(20) + "|" + base64(21) + " " + key,
+                "|2|" + base64(20) + "|" + base64(20) + " " + key };
+        for (String line : unreadableLines) {
+            for (String knownHosts : new String[] { line + "\n", 
entry("db.example.com", serverKey) + line + "\n" }) {
+                for (boolean failOnUnknownHost : new boolean[] { true, false 
}) {
+                    assertFalse(verify(failOnUnknownHost, "db.example.com", 
22, serverKey, knownHosts), line);
+                    assertFalse(verify(failOnUnknownHost, "www.example.com", 
22, serverKey, knownHosts), line);
+                }
+            }
+        }
+    }
+
+    @Test
+    void rejectsEveryServerWhenTheResourceCannotBeRead() {
+        for (boolean failOnUnknownHost : new boolean[] { true, false }) {
+            ResourceBasedSSHKeyVerifier verifier
+                    = new ResourceBasedSSHKeyVerifier(context, "file:" + 
dir.resolve("missing"), failOnUnknownHost);
+            assertFalse(verifier.verifyServerKey(null, new 
InetSocketAddress("127.0.0.1", 22), serverKey));
+        }
+    }
+
+    @Test
+    void ignoresCertificateAuthorityEntries() throws Exception {
+        String knownHosts = "@cert-authority db.example.com " + 
PublicKeyEntry.toString(serverKey) + "\n";
+        assertFalse(verify(true, "db.example.com", 22, serverKey, knownHosts));
+    }
+
+    @Test
+    void rejectsAnotherKeyForAListedHostWhateverFailOnUnknownHost() throws 
Exception {
+        String knownHosts = entry("10.0.0.12", otherKey) + entry("10.0.0.1", 
serverKey);
+        assertFalse(verify(false, "10.0.0.1", 22, otherKey, knownHosts));
+        assertFalse(verify(false, "db.example.com", 22, otherKey, 
entry("db.example.com", serverKey)));
+    }
+
+    @Test
+    void appliesFailOnUnknownHostToHostsWithoutEntries() throws Exception {
+        String knownHosts = entry("www.example.com", serverKey);
+        assertFalse(verify(true, "db.example.com", 22, serverKey, knownHosts));
+        assertTrue(verify(false, "db.example.com", 22, serverKey, knownHosts));
+        assertFalse(verify(true, "db.example.com", 22, serverKey, ""));
+        assertTrue(verify(false, "db.example.com", 22, serverKey, ""));
+    }
+
+    @Test
+    void looksUpAnAddressAsConfigured() throws Exception {
+        // an endpoint configured with an address is looked up by that 
address, not by the name of a reverse lookup
+        String knownHosts = entry("localhost", otherKey) + entry("127.0.0.1", 
serverKey);
+        assertFalse(verify(true, new InetSocketAddress("127.0.0.1", 22), 
otherKey, knownHosts));
+        assertTrue(verify(true, new InetSocketAddress("127.0.0.1", 22), 
serverKey, knownHosts));
+    }
+
+    @Test
+    void looksUpTheHostAsTheHostConfigurationOfTheConnectionWritesIt() throws 
Exception {
+        // the address the client connected to writes ::1 as 0:0:0:0:0:0:0:1
+        InetSocketAddress ipv6 = new InetSocketAddress("::1", 2222);
+        String knownHosts = entry("[::1]:2222", serverKey);
+        for (String hostName : new String[] { "::1", "[::1]" }) {
+            // as an endpoint configured with ::1, or Host review-ipv6 / 
HostName ::1 in ~/.ssh/config, connects
+            ClientSession session = session(new HostConfigEntry("review-ipv6", 
hostName, 2222, "smx"), null);
+            for (boolean failOnUnknownHost : new boolean[] { true, false }) {
+                assertTrue(verify(failOnUnknownHost, session, ipv6, serverKey, 
knownHosts), hostName);
+                assertFalse(verify(failOnUnknownHost, session, ipv6, otherKey, 
knownHosts), hostName);
             }
-        });
+        }
+        // Host db / HostName db.example.com in ~/.ssh/config
+        ClientSession db = session(new HostConfigEntry("db", "db.example.com", 
22, "smx"), null);
+        InetSocketAddress dbAddress
+                = new 
InetSocketAddress(InetAddress.getByAddress("db.example.com", new byte[] { 10, 
0, 0, 1 }), 22);
+        assertTrue(verify(true, db, dbAddress, serverKey, 
entry("db.example.com", serverKey)));
+        assertFalse(verify(true, db, dbAddress, serverKey, entry("db", 
serverKey)));
+        assertFalse(verify(false, db, dbAddress, otherKey, 
entry("db.example.com", serverKey)));
+        // without the host configuration of the connection, the connected 
address is used as it writes the host
+        assertFalse(verify(true, session(null, null), ipv6, serverKey, 
knownHosts));
+    }
+
+    @Test
+    void looksUpAHostReachedThroughAJumpHostAsItIsWritten() throws Exception {
+        HostConfigEntry viaJump = new HostConfigEntry("review-ipv6", "::1", 
2222, "smx", "jump.example.com");
+        // the target, which SSHD connects to through the jump host and keeps 
as it is written
+        ClientSession target = session(viaJump, new SshdSocketAddress("::1", 
2222));
+        InetSocketAddress ipv6 = new InetSocketAddress("::1", 2222);
+        assertTrue(verify(true, target, ipv6, serverKey, entry("[::1]:2222", 
serverKey)));
+        assertFalse(verify(false, target, ipv6, otherKey, entry("[::1]:2222", 
serverKey)));
+        // the jump host itself is looked up by its own address, not by the 
name of the target
+        ClientSession jump = session(viaJump, null);
+        InetSocketAddress jumpAddress
+                = new 
InetSocketAddress(InetAddress.getByAddress("jump.example.com", new byte[] { 10, 
0, 0, 2 }), 22);
+        String knownHosts = entry("jump.example.com", otherKey) + 
entry("[::1]:2222", serverKey);
+        assertTrue(verify(true, jump, jumpAddress, otherKey, knownHosts));
+        assertFalse(verify(true, jump, jumpAddress, serverKey, knownHosts));
+    }
+
+    @Test
+    void verifiesAllSupportedKeyTypes() throws Exception {
+        ResourceBasedSSHKeyVerifier verifier = new 
ResourceBasedSSHKeyVerifier(context, "classpath:known_hosts_types", true);
+        try (InputStream knownHosts = 
getClass().getResourceAsStream("/known_hosts_types")) {
+            assertNotNull(knownHosts);
+            for (String line : IoUtils.readAllLines(knownHosts)) {
+                String[] fields = line.split(" ");
+                PublicKey key = PublicKeyEntry.parsePublicKeyEntry(fields[1] + 
" " + fields[2])
+                        .resolvePublicKey(null, Collections.emptyMap(), 
PublicKeyEntryResolver.FAILING);
+                assertTrue(verifier.verifyServerKey(null, new 
InetSocketAddress(fields[0], 22), key), fields[1]);
+                assertFalse(verifier.verifyServerKey(null, new 
InetSocketAddress(fields[0], 22), serverKey), fields[1]);
+            }
+        }
+    }
+
+    private boolean verify(boolean failOnUnknownHost, String host, int port, 
PublicKey presented, String knownHosts)
+            throws Exception {
+        // the SSH client hands the verifier the configured host name together 
with its resolved address
+        InetAddress address = InetAddress.getByAddress(host, new byte[] { 10, 
0, 0, 1 });
+        return verify(failOnUnknownHost, new InetSocketAddress(address, port), 
presented, knownHosts);
+    }
+
+    private boolean verify(
+            boolean failOnUnknownHost, InetSocketAddress server, PublicKey 
presented, String knownHosts)
+            throws Exception {
+        return verify(failOnUnknownHost, null, server, presented, knownHosts);
+    }
+
+    private boolean verify(
+            boolean failOnUnknownHost, ClientSession session, 
InetSocketAddress server, PublicKey presented,
+            String knownHosts)
+            throws Exception {
+        Path file = Files.createTempFile(dir, "known_hosts", "");
+        Files.writeString(file, knownHosts, StandardCharsets.UTF_8);
+        ResourceBasedSSHKeyVerifier verifier = new 
ResourceBasedSSHKeyVerifier(context, "file:" + file, failOnUnknownHost);
+        return verifier.verifyServerKey(session, server, presented);
+    }
+
+    /*
+     * A session connected with the given host configuration, as the ssh 
producer and consumer connect, and reached
+     * through a jump host when a target server is given: all that the 
verifier asks the session for.
+     */
+    private static ClientSession session(HostConfigEntry hostConfig, 
SshdSocketAddress targetServer) {
+        AttributeRepository connectionContext = hostConfig != null
+                ? 
AttributeRepository.ofKeyValuePair(ResourceBasedSSHKeyVerifier.HOST_CONFIG_ENTRY,
 hostConfig) : null;
+        return (ClientSession) 
Proxy.newProxyInstance(ClientSession.class.getClassLoader(),
+                new Class<?>[] { ClientSession.class }, (proxy, method, args) 
-> {
+                    switch (method.getName()) {
+                        case "getConnectionContext":
+                            return connectionContext;
+                        case "getAttribute":
+                            return args[0] == 
ClientSessionCreator.TARGET_SERVER ? targetServer : null;
+                        default:
+                            Class<?> type = method.getReturnType();
+                            // the default value of a primitive type, null 
otherwise
+                            return type.isPrimitive() && type != void.class
+                                    ? Array.get(Array.newInstance(type, 1), 0) 
: null;
+                    }
+                });
+    }
+
+    private static String base64(int length) {
+        return Base64.getEncoder().encodeToString(new byte[length]);
+    }
+
+    private static String entry(String hosts, PublicKey key) {
+        return hosts + " " + PublicKeyEntry.toString(key) + "\n";
+    }
+
+    private static String hashed(String host) throws Exception {
+        byte[] salt = new byte[20];
+        Arrays.fill(salt, (byte) 7);
+        Mac mac = Mac.getInstance("HmacSHA1");
+        mac.init(new SecretKeySpec(salt, "HmacSHA1"));
+        byte[] hash = mac.doFinal(host.getBytes(StandardCharsets.UTF_8));
+        return "|1|" + Base64.getEncoder().encodeToString(salt) + "|" + 
Base64.getEncoder().encodeToString(hash);
     }
 }
diff --git 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostEdDSATest.java
 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostEdDSATest.java
index 243610be3cad..2fc472611aa7 100644
--- 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostEdDSATest.java
+++ 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostEdDSATest.java
@@ -22,6 +22,14 @@ import org.junit.jupiter.api.Test;
 
 public class SshComponentKnownHostEdDSATest extends SshComponentTestSupport {
 
+    private String knownHosts;
+
+    @Override
+    public void doPreSetup() throws Exception {
+        super.doPreSetup();
+        knownHosts = writeKnownHosts("known_hosts_eddsa", "[localhost]:" + 
port + " " + serverPublicKey());
+    }
+
     @Test
     public void testProducerWithEdDSAKeyType() throws Exception {
         final String msg = "test";
@@ -50,12 +58,13 @@ public class SshComponentKnownHostEdDSATest extends 
SshComponentTestSupport {
                 onException(Exception.class).handled(true).to("mock:error");
 
                 from("ssh://smx:smx@localhost:" + port
-                     + 
"?useFixedDelay=true&delay=40000&pollCommand=test%0A&knownHostsResource=classpath:known_hosts_eddsa&failOnUnknownHost=true")
+                     + 
"?useFixedDelay=true&delay=40000&pollCommand=test%0A&knownHostsResource=" + 
knownHosts
+                     + "&failOnUnknownHost=true")
                         .to("mock:result");
 
                 from("direct:ssh")
                         .to("ssh://smx:smx@localhost:" + port
-                            + 
"?timeout=3000&knownHostsResource=classpath:known_hosts_eddsa&failOnUnknownHost=true")
+                            + "?timeout=3000&knownHostsResource=" + knownHosts 
+ "&failOnUnknownHost=true")
                         .to("mock:password");
             }
         };
diff --git 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostIPv6Test.java
 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostIPv6Test.java
new file mode 100644
index 000000000000..45d390cec790
--- /dev/null
+++ 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostIPv6Test.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.ssh;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.KeyPairGenerator;
+import java.security.spec.ECGenParameterSpec;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.sshd.client.ClientBuilder;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/**
+ * A server reached at the IPv6 address ::1 is looked up as [::1]:port, as the 
address is written, although the address
+ * the client connects to writes it as 0:0:0:0:0:0:0:1: both when the endpoint 
is configured with ::1, and when the
+ * endpoint is configured with an alias that the host configuration of the 
client (as ~/.ssh/config would) gives the
+ * HostName ::1 for. The host is set on the endpoint, as the endpoint uri does 
not accept an IPv6 address.
+ */
+class SshComponentKnownHostIPv6Test extends SshComponentTestSupport {
+
+    private static final String ALIAS = "review-ipv6";
+
+    private String knownHostsServerKey;
+    private String knownHostsOtherKey;
+
+    @Override
+    public void doPreSetup() throws Exception {
+        super.doPreSetup();
+        assumeTrue(isIPv6LoopbackAvailable(), "The IPv6 loopback address is 
not available");
+        knownHostsServerKey = writeKnownHosts("known_hosts_server_key", 
"[::1]:" + port + " " + serverPublicKey());
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
+        generator.initialize(new ECGenParameterSpec("secp256r1"));
+        String otherKey = 
PublicKeyEntry.toString(generator.generateKeyPair().getPublic());
+        knownHostsOtherKey = writeKnownHosts("known_hosts_other_key", "[::1]:" 
+ port + " " + otherKey);
+    }
+
+    @Test
+    void testProducerWithTheServerKey() throws Exception {
+        assertDelivered("serverKey");
+    }
+
+    @Test
+    void testProducerWithTheServerKeyAcceptingUnknownHosts() throws Exception {
+        assertDelivered("serverKeyAcceptingUnknownHosts");
+    }
+
+    @Test
+    void testProducerWithAnotherKey() throws Exception {
+        assertRejected("otherKey");
+    }
+
+    @Test
+    void testProducerWithAnotherKeyAcceptingUnknownHosts() throws Exception {
+        assertRejected("otherKeyAcceptingUnknownHosts");
+    }
+
+    @Test
+    void testProducerWithAnAliasAndTheServerKey() throws Exception {
+        assertDelivered("aliasServerKey");
+    }
+
+    @Test
+    void testProducerWithAnAliasAndTheServerKeyAcceptingUnknownHosts() throws 
Exception {
+        assertDelivered("aliasServerKeyAcceptingUnknownHosts");
+    }
+
+    @Test
+    void testProducerWithAnAliasAndAnotherKey() throws Exception {
+        assertRejected("aliasOtherKey");
+    }
+
+    @Test
+    void testProducerWithAnAliasAndAnotherKeyAcceptingUnknownHosts() throws 
Exception {
+        assertRejected("aliasOtherKeyAcceptingUnknownHosts");
+    }
+
+    /*
+     * Each route uses its own user name, which the test server records when 
it receives credentials.
+     */
+    private void assertDelivered(String route) throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+        mock.expectedBodiesReceived("test");
+        getMockEndpoint("mock:error").expectedMessageCount(0);
+
+        template.sendBody("direct:" + route, "test");
+
+        MockEndpoint.assertIsSatisfied(context);
+        assertTrue(authenticatedUsers.contains(route), "the server should 
receive the credentials");
+    }
+
+    private void assertRejected(String route) throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(0);
+        getMockEndpoint("mock:error").expectedMessageCount(1);
+
+        template.sendBody("direct:" + route, "test");
+
+        MockEndpoint.assertIsSatisfied(context);
+        assertFalse(authenticatedUsers.contains(route), "the server should not 
receive any credentials");
+    }
+
+    private boolean isIPv6LoopbackAvailable() {
+        try (Socket socket = new Socket()) {
+            socket.connect(new InetSocketAddress("::1", port), 2000);
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /*
+     * The host configuration of the client, as ~/.ssh/config would give it: 
Host review-ipv6, HostName ::1. It answers
+     * only once, so the host key must be checked against the host that the 
connection was resolved to, without asking
+     * the host configuration again.
+     */
+    private static HostConfigEntryResolver resolvingOnce() {
+        AtomicBoolean resolved = new AtomicBoolean();
+        return (host, hostPort, localAddress, username, proxyJump, attributes) 
-> {
+            if (!ALIAS.equals(host)) {
+                return null;
+            }
+            if (resolved.getAndSet(true)) {
+                throw new IOException("The host configuration answers only 
once");
+            }
+            return new HostConfigEntry(host, "::1", hostPort, username);
+        };
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                onException(Exception.class).handled(true).to("mock:error");
+
+                route("serverKey", "::1", knownHostsServerKey, true);
+                route("serverKeyAcceptingUnknownHosts", "::1", 
knownHostsServerKey, false);
+                route("otherKey", "::1", knownHostsOtherKey, true);
+                route("otherKeyAcceptingUnknownHosts", "::1", 
knownHostsOtherKey, false);
+                route("aliasServerKey", ALIAS, knownHostsServerKey, true);
+                route("aliasServerKeyAcceptingUnknownHosts", ALIAS, 
knownHostsServerKey, false);
+                route("aliasOtherKey", ALIAS, knownHostsOtherKey, true);
+                route("aliasOtherKeyAcceptingUnknownHosts", ALIAS, 
knownHostsOtherKey, false);
+            }
+
+            private void route(String name, String host, String knownHosts, 
boolean failOnUnknownHost) {
+                SshEndpoint endpoint = context.getEndpoint("ssh://" + name + 
":secret@localhost:" + port
+                                                           + 
"?timeout=3000&knownHostsResource=" + knownHosts
+                                                           + 
"&failOnUnknownHost=" + failOnUnknownHost,
+                        SshEndpoint.class);
+                endpoint.setHost(host);
+                endpoint.getConfiguration()
+                        
.setClientBuilder(ClientBuilder.builder().hostConfigEntryResolver(resolvingOnce()));
+                from("direct:" + name).to(endpoint).to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostTest.java
 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostTest.java
index 72915c883e7b..977e28e27605 100644
--- 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostTest.java
+++ 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentKnownHostTest.java
@@ -16,12 +16,42 @@
  */
 package org.apache.camel.component.ssh;
 
+import java.io.IOException;
+import java.security.KeyPairGenerator;
+import java.security.spec.ECGenParameterSpec;
+
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.sshd.client.ClientBuilder;
+import org.apache.sshd.client.config.hosts.HostConfigEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 public class SshComponentKnownHostTest extends SshComponentTestSupport {
 
+    private String knownHostsValid;
+    private String knownHostsOtherHost;
+    private String knownHostsSeveralKeys;
+    private String knownHostsOtherKey;
+
+    @Override
+    public void doPreSetup() throws Exception {
+        super.doPreSetup();
+        String serverKey = serverPublicKey();
+        String serverEntry = "[localhost]:" + port + " " + serverKey;
+        knownHostsValid = writeKnownHosts("known_hosts_valid", serverEntry);
+        knownHostsOtherHost = writeKnownHosts("known_hosts_other_host", 
"old-localhost " + serverKey);
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
+        generator.initialize(new ECGenParameterSpec("secp256r1"));
+        String otherKey = 
PublicKeyEntry.toString(generator.generateKeyPair().getPublic());
+        knownHostsSeveralKeys
+                = writeKnownHosts("known_hosts_several_keys", "[localhost]:" + 
port + " " + otherKey, serverEntry);
+        knownHostsOtherKey = writeKnownHosts("known_hosts_other_key", 
"[localhost]:" + port + " " + otherKey);
+    }
+
     @Test
     public void testProducerWithValidFile() throws Exception {
         final String msg = "test";
@@ -64,6 +94,73 @@ public class SshComponentKnownHostTest extends 
SshComponentTestSupport {
         MockEndpoint.assertIsSatisfied(context);
     }
 
+    @Test
+    void testProducerWithKeyListedForAnotherHost() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:password");
+        mock.expectedMessageCount(0);
+        getMockEndpoint("mock:error").expectedMessageCount(1);
+
+        template.sendBody("direct:sshOtherHost", "test");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    void testProducerWithSeveralKeysForTheServer() throws Exception {
+        final String msg = "test";
+
+        MockEndpoint mock = getMockEndpoint("mock:password");
+        mock.expectedMessageCount(1);
+        mock.expectedBodiesReceived(msg);
+
+        template.sendBody("direct:sshSeveralKeys", msg);
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    void testProducerWithAnAliasInTheHostConfiguration() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:password");
+        mock.expectedMessageCount(1);
+        mock.expectedBodiesReceived("test");
+
+        template.sendBody("direct:aliasServerKey", "test");
+
+        MockEndpoint.assertIsSatisfied(context);
+        assertTrue(authenticatedUsers.contains("aliasServerKey"));
+    }
+
+    @Test
+    void testProducerWithAnAliasInTheHostConfigurationAndAnotherKey() throws 
Exception {
+        getMockEndpoint("mock:password").expectedMessageCount(0);
+        getMockEndpoint("mock:error").expectedMessageCount(1);
+
+        template.sendBody("direct:aliasOtherKey", "test");
+
+        MockEndpoint.assertIsSatisfied(context);
+        assertFalse(authenticatedUsers.contains("aliasOtherKey"));
+    }
+
+    @Test
+    void testProducerWithAFailingHostConfiguration() throws Exception {
+        assertNotConnected("brokenAlias");
+    }
+
+    @Test
+    void testProducerWithAFailingHostConfigurationAcceptingUnknownHosts() 
throws Exception {
+        assertNotConnected("brokenAliasAcceptingUnknownHosts");
+    }
+
+    private void assertNotConnected(String route) throws Exception {
+        getMockEndpoint("mock:password").expectedMessageCount(0);
+        getMockEndpoint("mock:error").expectedMessageCount(1);
+
+        template.sendBody("direct:" + route, "test");
+
+        MockEndpoint.assertIsSatisfied(context);
+        assertFalse(authenticatedUsers.contains(route));
+    }
+
     @Test
     public void testPollingConsumerWithValidKnownHostFile() throws Exception {
         MockEndpoint mock = getMockEndpoint("mock:result");
@@ -99,7 +196,8 @@ public class SshComponentKnownHostTest extends 
SshComponentTestSupport {
                 onException(Exception.class).handled(true).to("mock:error");
 
                 from("ssh://smx:smx@localhost:" + port
-                     + 
"?useFixedDelay=true&delay=40000&pollCommand=test%0A&knownHostsResource=classpath:known_hosts_valid&failOnUnknownHost=true")
+                     + 
"?useFixedDelay=true&delay=40000&pollCommand=test%0A&knownHostsResource=" + 
knownHostsValid
+                     + "&failOnUnknownHost=true")
                         .to("mock:result");
 
                 from("ssh://smx:smx@localhost:" + port
@@ -112,7 +210,28 @@ public class SshComponentKnownHostTest extends 
SshComponentTestSupport {
 
                 from("direct:ssh")
                         .to("ssh://smx:smx@localhost:" + port
-                            + 
"?timeout=3000&knownHostsResource=classpath:known_hosts_valid&failOnUnknownHost=true")
+                            + "?timeout=3000&knownHostsResource=" + 
knownHostsValid + "&failOnUnknownHost=true")
+                        .to("mock:password");
+
+                from("direct:sshOtherHost")
+                        .to("ssh://smx:smx@localhost:" + port
+                            + "?timeout=3000&knownHostsResource=" + 
knownHostsOtherHost + "&failOnUnknownHost=true")
+                        .to("mock:password");
+
+                // the host configuration of the client, as ~/.ssh/config 
would give it: Host db-alias, HostName localhost
+                
from("direct:aliasServerKey").to(aliasEndpoint("aliasServerKey", 
knownHostsValid, true))
+                        .to("mock:password");
+                from("direct:aliasOtherKey").to(aliasEndpoint("aliasOtherKey", 
knownHostsOtherKey, false))
+                        .to("mock:password");
+
+                // a host configuration that cannot resolve the host: the 
producer must not connect anywhere
+                
from("direct:brokenAlias").to(brokenAliasEndpoint("brokenAlias", 
true)).to("mock:password");
+                from("direct:brokenAliasAcceptingUnknownHosts")
+                        
.to(brokenAliasEndpoint("brokenAliasAcceptingUnknownHosts", 
false)).to("mock:password");
+
+                from("direct:sshSeveralKeys")
+                        .to("ssh://smx:smx@localhost:" + port
+                            + "?timeout=3000&knownHostsResource=" + 
knownHostsSeveralKeys + "&failOnUnknownHost=true")
                         .to("mock:password");
 
                 from("direct:sshInvalid").to("ssh://smx:smx@localhost:" + port
@@ -125,4 +244,27 @@ public class SshComponentKnownHostTest extends 
SshComponentTestSupport {
             }
         };
     }
+
+    private SshEndpoint aliasEndpoint(String user, String knownHosts, boolean 
failOnUnknownHost) {
+        SshEndpoint endpoint = context.getEndpoint("ssh://" + user + 
":secret@db-alias:" + port
+                                                   + 
"?timeout=3000&knownHostsResource=" + knownHosts
+                                                   + "&failOnUnknownHost=" + 
failOnUnknownHost,
+                SshEndpoint.class);
+        
endpoint.getConfiguration().setClientBuilder(ClientBuilder.builder().hostConfigEntryResolver(
+                (host, hostPort, localAddress, username, proxyJump, 
attributes) -> "db-alias".equals(host)
+                        ? new HostConfigEntry(host, "localhost", hostPort, 
username) : null));
+        return endpoint;
+    }
+
+    private SshEndpoint brokenAliasEndpoint(String user, boolean 
failOnUnknownHost) {
+        SshEndpoint endpoint = context.getEndpoint("ssh://" + user + 
":secret@broken-alias:" + port
+                                                   + 
"?timeout=3000&knownHostsResource=" + knownHostsValid
+                                                   + "&failOnUnknownHost=" + 
failOnUnknownHost,
+                SshEndpoint.class);
+        
endpoint.getConfiguration().setClientBuilder(ClientBuilder.builder().hostConfigEntryResolver(
+                (host, hostPort, localAddress, username, proxyJump, 
attributes) -> {
+                    throw new IOException("Cannot read the host 
configuration");
+                }));
+        return endpoint;
+    }
 }
diff --git 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentTestSupport.java
 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentTestSupport.java
index e0260028ae43..db4b23cfea6b 100644
--- 
a/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentTestSupport.java
+++ 
b/components/camel-ssh/src/test/java/org/apache/camel/component/ssh/SshComponentTestSupport.java
@@ -17,15 +17,26 @@
 package org.apache.camel.component.ssh;
 
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.server.SshServer;
 
 public class SshComponentTestSupport extends CamelTestSupport {
     protected SshServer sshd;
     protected int port;
+    /**
+     * The users the test server received credentials from, a password or a 
public key.
+     */
+    protected final Set<String> authenticatedUsers = 
ConcurrentHashMap.newKeySet();
 
     @Override
     public void doPreSetup() throws Exception {
@@ -33,8 +44,14 @@ public class SshComponentTestSupport extends 
CamelTestSupport {
         sshd.setPort(0);
         sshd.setKeyPairProvider(new 
FileKeyPairProvider(Paths.get(getHostKey())));
         sshd.setCommandFactory(new TestEchoCommandFactory());
-        sshd.setPasswordAuthenticator((username, password, session) -> true);
-        sshd.setPublickeyAuthenticator((username, key, session) -> true);
+        sshd.setPasswordAuthenticator((username, password, session) -> {
+            authenticatedUsers.add(username);
+            return true;
+        });
+        sshd.setPublickeyAuthenticator((username, key, session) -> {
+            authenticatedUsers.add(username);
+            return true;
+        });
         sshd.start();
         port = sshd.getPort();
     }
@@ -43,6 +60,25 @@ public class SshComponentTestSupport extends 
CamelTestSupport {
         return "src/test/resources/hostkey.pem";
     }
 
+    /**
+     * The public key of the test server, in the form used by a known_hosts 
entry.
+     */
+    protected String serverPublicKey() throws IOException, 
GeneralSecurityException {
+        KeyPair keyPair = 
sshd.getKeyPairProvider().loadKeys(null).iterator().next();
+        return PublicKeyEntry.toString(keyPair.getPublic());
+    }
+
+    /**
+     * Writes a known_hosts file for this test and returns it as a resource. 
The test server listens on a random port,
+     * so its entries must use the [localhost]:port form.
+     */
+    protected String writeKnownHosts(String name, String... entries) throws 
IOException {
+        Path file = Paths.get("target", "known_hosts", 
getClass().getSimpleName(), name);
+        Files.createDirectories(file.getParent());
+        Files.writeString(file, String.join("\n", entries) + "\n");
+        return "file:" + file;
+    }
+
     @Override
     public void doPostTearDown() throws IOException, InterruptedException {
         if (sshd != null) {
diff --git a/components/camel-ssh/src/test/resources/known_hosts_eddsa 
b/components/camel-ssh/src/test/resources/known_hosts_eddsa
deleted file mode 100644
index 8ccd01181c5c..000000000000
--- a/components/camel-ssh/src/test/resources/known_hosts_eddsa
+++ /dev/null
@@ -1 +0,0 @@
-127.0.0.1 ssh-ed25519 
AAAAC3NzaC1lZDI1NTE5AAAAIGY16PbGw2BjMtxaUUWBYK9TKVeavaOXG2FvlAEe80x8
diff --git a/components/camel-ssh/src/test/resources/known_hosts_valid 
b/components/camel-ssh/src/test/resources/known_hosts_valid
deleted file mode 100644
index e7ae1daf75e1..000000000000
--- a/components/camel-ssh/src/test/resources/known_hosts_valid
+++ /dev/null
@@ -1 +0,0 @@
-127.0.0.1 ssh-rsa 
AAAAB3NzaC1yc2EAAAADAQABAAAAgQDdfIWeSV4o68dRrKSzFd/Bk51E65UTmmSrmW0O1ohtzi6HzsDPjXgCtlTt3FqTcfFfI92IlTr4JWqC9UK1QT1ZTeng0MkPQmv68hDANHbt5CpETZHjW5q4OOgWhVvj5IyOC2NZHtKlJBkdsMAa15ouOOJLzBvAvbqOR/yUROsEiQ==
\ No newline at end of file
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SshComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SshComponentBuilderFactory.java
index 19243cffd326..3665cc88fc71 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SshComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/SshComponentBuilderFactory.java
@@ -445,9 +445,12 @@ public interface SshComponentBuilderFactory {
     
         
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
          * 
@@ -516,8 +519,11 @@ public interface SshComponentBuilderFactory {
     
         /**
          * Sets the resource path for a known_hosts file used to verify the SSH
-         * server host key. When not set, the client does not verify the server
-         * host key against a known_hosts file.
+         * server host key. The file uses the OpenSSH known_hosts format, and
+         * its entries are matched against the host and port configured on the
+         * endpoint. A resource that cannot be read, or that has a line that
+         * cannot be read, makes every connection fail. When not set, the 
client
+         * does not verify the server host key against a known_hosts file.
          * 
          * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
          * 
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SshEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SshEndpointBuilderFactory.java
index e83926e90b7f..c12f8d86ca9d 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SshEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SshEndpointBuilderFactory.java
@@ -628,9 +628,12 @@ public interface SshEndpointBuilderFactory {
             return this;
         }
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -645,9 +648,12 @@ public interface SshEndpointBuilderFactory {
             return this;
         }
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
@@ -728,8 +734,11 @@ public interface SshEndpointBuilderFactory {
         }
         /**
          * Sets the resource path for a known_hosts file used to verify the SSH
-         * server host key. When not set, the client does not verify the server
-         * host key against a known_hosts file.
+         * server host key. The file uses the OpenSSH known_hosts format, and
+         * its entries are matched against the host and port configured on the
+         * endpoint. A resource that cannot be read, or that has a line that
+         * cannot be read, makes every connection fail. When not set, the 
client
+         * does not verify the server host key against a known_hosts file.
          * 
          * The option is a: <code>java.lang.String</code> type.
          * 
@@ -1344,9 +1353,12 @@ public interface SshEndpointBuilderFactory {
             return this;
         }
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -1361,9 +1373,12 @@ public interface SshEndpointBuilderFactory {
             return this;
         }
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
@@ -1444,8 +1459,11 @@ public interface SshEndpointBuilderFactory {
         }
         /**
          * Sets the resource path for a known_hosts file used to verify the SSH
-         * server host key. When not set, the client does not verify the server
-         * host key against a known_hosts file.
+         * server host key. The file uses the OpenSSH known_hosts format, and
+         * its entries are matched against the host and port configured on the
+         * endpoint. A resource that cannot be read, or that has a line that
+         * cannot be read, makes every connection fail. When not set, the 
client
+         * does not verify the server host key against a known_hosts file.
          * 
          * The option is a: <code>java.lang.String</code> type.
          * 
@@ -1953,9 +1971,12 @@ public interface SshEndpointBuilderFactory {
             return this;
         }
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -1970,9 +1991,12 @@ public interface SshEndpointBuilderFactory {
             return this;
         }
         /**
-         * Specifies whether a connection to an unknown SSH server host key
-         * should fail. When false, host keys that are not present in the
-         * known_hosts resource are accepted.
+         * Specifies whether a connection to a server whose host has no entry
+         * without a marker in the known_hosts resource should fail. When 
false,
+         * such a server is accepted. A server whose host has entries without a
+         * marker is always rejected when it presents a key that none of them
+         * lists, and a key listed in a matching revoked entry is always
+         * rejected.
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
@@ -2053,8 +2077,11 @@ public interface SshEndpointBuilderFactory {
         }
         /**
          * Sets the resource path for a known_hosts file used to verify the SSH
-         * server host key. When not set, the client does not verify the server
-         * host key against a known_hosts file.
+         * server host key. The file uses the OpenSSH known_hosts format, and
+         * its entries are matched against the host and port configured on the
+         * endpoint. A resource that cannot be read, or that has a line that
+         * cannot be read, makes every connection fail. When not set, the 
client
+         * does not verify the server host key against a known_hosts file.
          * 
          * The option is a: <code>java.lang.String</code> type.
          * 

Reply via email to