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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 5c9d5bc1c133 CAMEL-25890: camel-ftp - FTPS: verify the host name of 
the server over TLS (#28186)
5c9d5bc1c133 is described below

commit 5c9d5bc1c1333cc4b4a36cfb5d737b2944a1be02
Author: Federico Mariani <[email protected]>
AuthorDate: Wed Oct 21 12:54:03 2026 +0200

    CAMEL-25890: camel-ftp - FTPS: verify the host name of the server over TLS 
(#28186)
    
    The FTPS client created by the ftps endpoint did not check that the server
    certificate matches the host of the endpoint. A new hostnameVerification
    option (default true, the same name as in camel-mina, camel-mllp and
    camel-smpp) enables endpoint checking on the client the endpoint creates; a
    custom ftpClient is left unchanged. The 4.24 upgrade guide notes the 
behaviour
    change and that hostnameVerification=false keeps the previous behaviour. The
    test keystores are regenerated with a localhost subject alternative name.
---
 .../org/apache/camel/catalog/components/ftps.json  |  17 +--
 .../apache/camel/catalog/docs/ftp-component.adoc   |  13 +++
 .../file/remote/FtpsEndpointConfigurer.java        |   6 ++
 .../file/remote/FtpsEndpointUriFactory.java        |   3 +-
 .../apache/camel/component/file/remote/ftps.json   |  17 +--
 .../camel-ftp/src/main/docs/ftp-component.adoc     |  13 +++
 .../component/file/remote/FtpsConfiguration.java   |  15 +++
 .../camel/component/file/remote/FtpsEndpoint.java  |   2 +
 .../FtpsEndpointHostnameVerificationTest.java      |  74 +++++++++++++
 .../FileToFtpsHostnameVerificationIT.java          |  87 ++++++++++++++++
 components/camel-ftp/src/test/resources/client.jks | Bin 1960 -> 2089 bytes
 components/camel-ftp/src/test/resources/server.jks | Bin 1961 -> 2971 bytes
 .../java/org/apache/camel/util/SecurityUtils.java  |   1 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  12 +++
 .../endpoint/dsl/FtpsEndpointBuilderFactory.java   | 114 +++++++++++++++++++++
 .../src/main/resources/server.jks                  | Bin 1961 -> 2971 bytes
 16 files changed, 357 insertions(+), 17 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
index 81aaec1cd35d..075f91d010d2 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json
@@ -169,13 +169,14 @@
     "execProt": { "index": 114, "kind": "parameter", "displayName": "Exec 
Prot", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "The exec protection 
level PROT command. C - Clear S - Safe(SSL protocol only) E - Confidential(SSL 
protoc [...]
     "ftpClientKeyStoreParameters": { "index": 115, "kind": "parameter", 
"displayName": "Ftp Client Key Store Parameters", "group": "security", "label": 
"security", "required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"ftpClient.keyStore.", "multiValue": true, "deprecated": false, "autowired": 
false, "secret": false, "description": "Set the key store parameters. This is a 
multi-value option with prefix: ftpClient.keyStore." },
     "ftpClientTrustStoreParameters": { "index": 116, "kind": "parameter", 
"displayName": "Ftp Client Trust Store Parameters", "group": "security", 
"label": "security", "required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"ftpClient.trustStore.", "multiValue": true, "deprecated": false, "autowired": 
false, "secret": false, "description": "Set the trust store parameters. This is 
a multi-value option with prefix: ftpClient.trustStore." },
-    "implicit": { "index": 117, "kind": "parameter", "displayName": 
"Implicit", "group": "security", "label": "security", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the security mode 
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
-    "password": { "index": 118, "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Password to use for 
login" },
-    "securityProtocol": { "index": 119, "kind": "parameter", "displayName": 
"Security Protocol", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "TLSv1.3", 
"configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the underlying 
security protocol." },
-    "sslContextParameters": { "index": 120, "kind": "parameter", 
"displayName": "Ssl Context Parameters", "group": "security", "label": 
"security", "required": false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "description": "Gets the JSSE 
configuration that overrides any settings in 
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and 
FtpsConfiguration#getSecurityPr [...]
-    "username": { "index": 121, "kind": "parameter", "displayName": 
"Username", "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Username to use for 
login" },
-    "shuffle": { "index": 122, "kind": "parameter", "displayName": "Shuffle", 
"group": "sort", "label": "consumer,sort", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "description": "To shuffle the list of 
files (sort in random order)" },
-    "sortBy": { "index": 123, "kind": "parameter", "displayName": "Sort By", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "Built-in sort by using the File Language. 
Supports nested sorts, so you can have a sort by file name and as a 2nd group 
sort by modified date." },
-    "sorter": { "index": 124, "kind": "parameter", "displayName": "Sorter", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "object", 
"javaType": 
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
 "deprecated": false, "autowired": false, "secret": false, "description": 
"Pluggable sorter as a java.util.Comparator class." }
+    "hostnameVerification": { "index": 117, "kind": "parameter", 
"displayName": "Hostname Verification", "group": "security", "label": 
"security", "required": false, "type": "boolean", "javaType": "boolean", 
"deprecated": false, "autowired": false, "secret": false, "security": 
"insecure:ssl", "insecureValue": "false", "defaultValue": true, 
"configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Whether to ch [...]
+    "implicit": { "index": 118, "kind": "parameter", "displayName": 
"Implicit", "group": "security", "label": "security", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the security mode 
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
+    "password": { "index": 119, "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Password to use for 
login" },
+    "securityProtocol": { "index": 120, "kind": "parameter", "displayName": 
"Security Protocol", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "TLSv1.3", 
"configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the underlying 
security protocol." },
+    "sslContextParameters": { "index": 121, "kind": "parameter", 
"displayName": "Ssl Context Parameters", "group": "security", "label": 
"security", "required": false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "description": "Gets the JSSE 
configuration that overrides any settings in 
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and 
FtpsConfiguration#getSecurityPr [...]
+    "username": { "index": 122, "kind": "parameter", "displayName": 
"Username", "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Username to use for 
login" },
+    "shuffle": { "index": 123, "kind": "parameter", "displayName": "Shuffle", 
"group": "sort", "label": "consumer,sort", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "description": "To shuffle the list of 
files (sort in random order)" },
+    "sortBy": { "index": 124, "kind": "parameter", "displayName": "Sort By", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "Built-in sort by using the File Language. 
Supports nested sorts, so you can have a sort by file name and as a 2nd group 
sort by modified date." },
+    "sorter": { "index": 125, "kind": "parameter", "displayName": "Sorter", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "object", 
"javaType": 
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
 "deprecated": false, "autowired": false, "secret": false, "description": 
"Pluggable sorter as a java.util.Comparator class." }
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
index 9276d50ec3f8..1064898ead14 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ftp-component.adoc
@@ -80,6 +80,19 @@ include::partial$component-endpoint-headers.adoc[]
 
 == Usage
 
+=== FTPS host name verification
+
+The FTPS client that Camel creates checks, on the control connection, that the 
certificate of the server matches the
+host of the endpoint, with the same rules as an HTTPS client. The check 
happens during the TLS handshake, before login.
+Data connections (`PROT P`) use the same trust manager, but Apache Commons Net 
does not check their host name. A
+connection to an IP address needs a certificate that lists that address as a 
subject alternative name. Set
+`hostnameVerification=false` to turn this check off; the certificate is still 
checked by the trust manager. The option
+does not apply to a custom `ftpClient`.
+
+Use the `hostnameVerification` option to control the check. Setting 
`ftpClient.endpointCheckingEnabled` on the endpoint
+also turns it on or off, and takes precedence over `hostnameVerification`, but 
the security policy (for example
+`camel.main.profile=prod`) only checks the `hostnameVerification` option.
+
 === FTPS component default trust store
 
 When using the `ftpClient.` properties related to SSL with the FTPS
diff --git 
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
 
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
index 0517052361d0..9b2c72c4e272 100644
--- 
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
+++ 
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointConfigurer.java
@@ -33,6 +33,8 @@ public class FtpsEndpointConfigurer extends 
FtpEndpointConfigurer implements Gen
         case "ftpClientKeyStoreParameters": 
target.setFtpClientKeyStoreParameters(property(camelContext, 
java.util.Map.class, value)); return true;
         case "ftpclienttruststoreparameters":
         case "ftpClientTrustStoreParameters": 
target.setFtpClientTrustStoreParameters(property(camelContext, 
java.util.Map.class, value)); return true;
+        case "hostnameverification":
+        case "hostnameVerification": 
target.getConfiguration().setHostnameVerification(property(camelContext, 
boolean.class, value)); return true;
         case "implicit": 
target.getConfiguration().setImplicit(property(camelContext, boolean.class, 
value)); return true;
         case "securityprotocol":
         case "securityProtocol": 
target.getConfiguration().setSecurityProtocol(property(camelContext, 
java.lang.String.class, value)); return true;
@@ -55,6 +57,8 @@ public class FtpsEndpointConfigurer extends 
FtpEndpointConfigurer implements Gen
         case "ftpClientKeyStoreParameters": return java.util.Map.class;
         case "ftpclienttruststoreparameters":
         case "ftpClientTrustStoreParameters": return java.util.Map.class;
+        case "hostnameverification":
+        case "hostnameVerification": return boolean.class;
         case "implicit": return boolean.class;
         case "securityprotocol":
         case "securityProtocol": return java.lang.String.class;
@@ -78,6 +82,8 @@ public class FtpsEndpointConfigurer extends 
FtpEndpointConfigurer implements Gen
         case "ftpClientKeyStoreParameters": return 
target.getFtpClientKeyStoreParameters();
         case "ftpclienttruststoreparameters":
         case "ftpClientTrustStoreParameters": return 
target.getFtpClientTrustStoreParameters();
+        case "hostnameverification":
+        case "hostnameVerification": return 
target.getConfiguration().isHostnameVerification();
         case "implicit": return target.getConfiguration().isImplicit();
         case "securityprotocol":
         case "securityProtocol": return 
target.getConfiguration().getSecurityProtocol();
diff --git 
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
 
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
index e1802aef6f99..b10e071881c3 100644
--- 
a/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
+++ 
b/components/camel-ftp/src/generated/java/org/apache/camel/component/file/remote/FtpsEndpointUriFactory.java
@@ -24,7 +24,7 @@ public class FtpsEndpointUriFactory extends 
org.apache.camel.support.component.E
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(125);
+        Set<String> props = new HashSet<>(126);
         props.add("account");
         props.add("activePortRange");
         props.add("allowNullBody");
@@ -77,6 +77,7 @@ public class FtpsEndpointUriFactory extends 
org.apache.camel.support.component.E
         props.add("greedy");
         props.add("handleDirectoryParserAbsoluteResult");
         props.add("host");
+        props.add("hostnameVerification");
         props.add("idempotent");
         props.add("idempotentEager");
         props.add("idempotentKey");
diff --git 
a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
 
b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
index 81aaec1cd35d..075f91d010d2 100644
--- 
a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
+++ 
b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json
@@ -169,13 +169,14 @@
     "execProt": { "index": 114, "kind": "parameter", "displayName": "Exec 
Prot", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "The exec protection 
level PROT command. C - Clear S - Safe(SSL protocol only) E - Confidential(SSL 
protoc [...]
     "ftpClientKeyStoreParameters": { "index": 115, "kind": "parameter", 
"displayName": "Ftp Client Key Store Parameters", "group": "security", "label": 
"security", "required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"ftpClient.keyStore.", "multiValue": true, "deprecated": false, "autowired": 
false, "secret": false, "description": "Set the key store parameters. This is a 
multi-value option with prefix: ftpClient.keyStore." },
     "ftpClientTrustStoreParameters": { "index": 116, "kind": "parameter", 
"displayName": "Ftp Client Trust Store Parameters", "group": "security", 
"label": "security", "required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"ftpClient.trustStore.", "multiValue": true, "deprecated": false, "autowired": 
false, "secret": false, "description": "Set the trust store parameters. This is 
a multi-value option with prefix: ftpClient.trustStore." },
-    "implicit": { "index": 117, "kind": "parameter", "displayName": 
"Implicit", "group": "security", "label": "security", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the security mode 
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
-    "password": { "index": 118, "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Password to use for 
login" },
-    "securityProtocol": { "index": 119, "kind": "parameter", "displayName": 
"Security Protocol", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "TLSv1.3", 
"configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the underlying 
security protocol." },
-    "sslContextParameters": { "index": 120, "kind": "parameter", 
"displayName": "Ssl Context Parameters", "group": "security", "label": 
"security", "required": false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "description": "Gets the JSSE 
configuration that overrides any settings in 
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and 
FtpsConfiguration#getSecurityPr [...]
-    "username": { "index": 121, "kind": "parameter", "displayName": 
"Username", "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Username to use for 
login" },
-    "shuffle": { "index": 122, "kind": "parameter", "displayName": "Shuffle", 
"group": "sort", "label": "consumer,sort", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "description": "To shuffle the list of 
files (sort in random order)" },
-    "sortBy": { "index": 123, "kind": "parameter", "displayName": "Sort By", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "Built-in sort by using the File Language. 
Supports nested sorts, so you can have a sort by file name and as a 2nd group 
sort by modified date." },
-    "sorter": { "index": 124, "kind": "parameter", "displayName": "Sorter", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "object", 
"javaType": 
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
 "deprecated": false, "autowired": false, "secret": false, "description": 
"Pluggable sorter as a java.util.Comparator class." }
+    "hostnameVerification": { "index": 117, "kind": "parameter", 
"displayName": "Hostname Verification", "group": "security", "label": 
"security", "required": false, "type": "boolean", "javaType": "boolean", 
"deprecated": false, "autowired": false, "secret": false, "security": 
"insecure:ssl", "insecureValue": "false", "defaultValue": true, 
"configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Whether to ch [...]
+    "implicit": { "index": 118, "kind": "parameter", "displayName": 
"Implicit", "group": "security", "label": "security", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the security mode 
(Implicit\/Explicit). true - Implicit Mode \/ False - Explicit Mode" },
+    "password": { "index": 119, "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Password to use for 
login" },
+    "securityProtocol": { "index": 120, "kind": "parameter", "displayName": 
"Security Protocol", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "TLSv1.3", 
"configurationClass": 
"org.apache.camel.component.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Set the underlying 
security protocol." },
+    "sslContextParameters": { "index": 121, "kind": "parameter", 
"displayName": "Ssl Context Parameters", "group": "security", "label": 
"security", "required": false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "description": "Gets the JSSE 
configuration that overrides any settings in 
FtpsEndpoint#ftpClientKeyStoreParameters, ftpClientTrustStoreParameters, and 
FtpsConfiguration#getSecurityPr [...]
+    "username": { "index": 122, "kind": "parameter", "displayName": 
"Username", "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.file.remote.FtpsConfiguration", 
"configurationField": "configuration", "description": "Username to use for 
login" },
+    "shuffle": { "index": 123, "kind": "parameter", "displayName": "Shuffle", 
"group": "sort", "label": "consumer,sort", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "description": "To shuffle the list of 
files (sort in random order)" },
+    "sortBy": { "index": 124, "kind": "parameter", "displayName": "Sort By", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "Built-in sort by using the File Language. 
Supports nested sorts, so you can have a sort by file name and as a 2nd group 
sort by modified date." },
+    "sorter": { "index": 125, "kind": "parameter", "displayName": "Sorter", 
"group": "sort", "label": "consumer,sort", "required": false, "type": "object", 
"javaType": 
"java.util.Comparator<org.apache.camel.component.file.GenericFile<org.apache.commons.net.ftp.FTPFile>>",
 "deprecated": false, "autowired": false, "secret": false, "description": 
"Pluggable sorter as a java.util.Comparator class." }
   }
 }
diff --git a/components/camel-ftp/src/main/docs/ftp-component.adoc 
b/components/camel-ftp/src/main/docs/ftp-component.adoc
index 9276d50ec3f8..1064898ead14 100644
--- a/components/camel-ftp/src/main/docs/ftp-component.adoc
+++ b/components/camel-ftp/src/main/docs/ftp-component.adoc
@@ -80,6 +80,19 @@ include::partial$component-endpoint-headers.adoc[]
 
 == Usage
 
+=== FTPS host name verification
+
+The FTPS client that Camel creates checks, on the control connection, that the 
certificate of the server matches the
+host of the endpoint, with the same rules as an HTTPS client. The check 
happens during the TLS handshake, before login.
+Data connections (`PROT P`) use the same trust manager, but Apache Commons Net 
does not check their host name. A
+connection to an IP address needs a certificate that lists that address as a 
subject alternative name. Set
+`hostnameVerification=false` to turn this check off; the certificate is still 
checked by the trust manager. The option
+does not apply to a custom `ftpClient`.
+
+Use the `hostnameVerification` option to control the check. Setting 
`ftpClient.endpointCheckingEnabled` on the endpoint
+also turns it on or off, and takes precedence over `hostnameVerification`, but 
the security policy (for example
+`camel.main.profile=prod`) only checks the `hostnameVerification` option.
+
 === FTPS component default trust store
 
 When using the `ftpClient.` properties related to SSL with the FTPS
diff --git 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
index ea102238e8b3..c61c6232fa8a 100644
--- 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
+++ 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsConfiguration.java
@@ -37,6 +37,8 @@ public class FtpsConfiguration extends FtpConfiguration {
     private String execProt;
     @UriParam(label = "security")
     private Long execPbsz;
+    @UriParam(label = "security", defaultValue = "true", security = 
"insecure:ssl", insecureValue = "false")
+    private boolean hostnameVerification = true;
 
     public FtpsConfiguration() {
         setProtocol("ftps");
@@ -119,4 +121,17 @@ public class FtpsConfiguration extends FtpConfiguration {
     public void setExecPbsz(Long execPbsz) {
         this.execPbsz = execPbsz;
     }
+
+    public boolean isHostnameVerification() {
+        return hostnameVerification;
+    }
+
+    /**
+     * Whether to check that the certificate of the server matches the host 
Camel connects to, with the same rules as an
+     * HTTPS client. When disabled, any certificate accepted by the trust 
manager is accepted, whatever host it was
+     * issued for. This option does not apply to a custom ftpClient.
+     */
+    public void setHostnameVerification(boolean hostnameVerification) {
+        this.hostnameVerification = hostnameVerification;
+    }
 }
diff --git 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
index 7223f29c5a7b..37251328723b 100644
--- 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
+++ 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java
@@ -164,6 +164,8 @@ public class FtpsEndpoint extends FtpEndpoint<FTPFile> {
             }
         }
 
+        
client.setEndpointCheckingEnabled(getFtpsConfiguration().isHostnameVerification());
+
         return client;
     }
 
diff --git 
a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsEndpointHostnameVerificationTest.java
 
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsEndpointHostnameVerificationTest.java
new file mode 100644
index 000000000000..2b80917be695
--- /dev/null
+++ 
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsEndpointHostnameVerificationTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.file.remote;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.commons.net.ftp.FTPSClient;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FtpsEndpointHostnameVerificationTest extends CamelTestSupport {
+
+    @BindToRegistry("sslContextParameters")
+    private final SSLContextParameters sslContextParameters = new 
SSLContextParameters();
+
+    @BindToRegistry("customFtpClient")
+    private final FTPSClient customFtpClient = new FTPSClient();
+
+    @Test
+    public void createdClientChecksServerHostNameByDefault() throws Exception {
+        FtpsEndpoint endpoint = context.getEndpoint("ftps://localhost/dir", 
FtpsEndpoint.class);
+
+        FTPSClient client = (FTPSClient) endpoint.createFtpClient();
+
+        assertTrue(client.isEndpointCheckingEnabled());
+    }
+
+    @Test
+    public void createdClientWithSslContextParametersChecksServerHostName() 
throws Exception {
+        FtpsEndpoint endpoint = 
context.getEndpoint("ftps://localhost/dir?sslContextParameters=#sslContextParameters",
+                FtpsEndpoint.class);
+
+        FTPSClient client = (FTPSClient) endpoint.createFtpClient();
+
+        assertTrue(client.isEndpointCheckingEnabled());
+    }
+
+    @Test
+    public void hostnameVerificationCanBeTurnedOff() throws Exception {
+        FtpsEndpoint endpoint = 
context.getEndpoint("ftps://localhost/dir?hostnameVerification=false", 
FtpsEndpoint.class);
+        FtpsEndpoint endpointWithSslContextParameters = context.getEndpoint(
+                
"ftps://localhost/other?hostnameVerification=false&sslContextParameters=#sslContextParameters",
+                FtpsEndpoint.class);
+
+        assertFalse(((FTPSClient) 
endpoint.createFtpClient()).isEndpointCheckingEnabled());
+        assertFalse(((FTPSClient) 
endpointWithSslContextParameters.createFtpClient()).isEndpointCheckingEnabled());
+    }
+
+    @Test
+    public void customFtpClientIsNotChanged() throws Exception {
+        FtpsEndpoint endpoint = 
context.getEndpoint("ftps://localhost/dir?ftpClient=#customFtpClient", 
FtpsEndpoint.class);
+
+        endpoint.createRemoteFileOperations();
+
+        assertFalse(customFtpClient.isEndpointCheckingEnabled());
+    }
+}
diff --git 
a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FileToFtpsHostnameVerificationIT.java
 
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FileToFtpsHostnameVerificationIT.java
new file mode 100644
index 000000000000..5ae945cc81be
--- /dev/null
+++ 
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FileToFtpsHostnameVerificationIT.java
@@ -0,0 +1,87 @@
+/*
+ * 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.file.remote.integration;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.support.jsse.KeyStoreParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.apache.camel.util.ObjectHelper;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import static org.apache.camel.test.junit6.TestSupport.assertFileExists;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Test that the ftps component checks the host name of the server: the 
certificate of the test server only lists
+ * localhost, not 127.0.0.1.
+ */
+@EnabledIf(value = 
"org.apache.camel.test.infra.ftp.services.embedded.FtpsUtil#hasRequiredAlgorithms")
+public class FileToFtpsHostnameVerificationIT extends 
FtpsServerExplicitSSLWithoutClientAuthTestSupport {
+
+    @BindToRegistry("sslContextParameters")
+    public SSLContextParameters createSslContextParams() {
+        KeyStoreParameters ksp = new KeyStoreParameters();
+        ksp.setResource("server.jks");
+        ksp.setPassword("password");
+
+        TrustManagersParameters tmp = new TrustManagersParameters();
+        tmp.setKeyStore(ksp);
+
+        SSLContextParameters sslContextParameters = new SSLContextParameters();
+        sslContextParameters.setSecureSocketProtocol("TLSv1.2");
+        sslContextParameters.setTrustManagers(tmp);
+
+        return sslContextParameters;
+    }
+
+    private String getFtpUrl(String host, String directory) {
+        return "ftps://admin@" + host + ":" + service.getPort() + "/" + 
directory
+               + 
"?password=admin&disableSecureDataChannelDefaults=true&implicit=false"
+               + 
"&sslContextParameters=#sslContextParameters&maximumReconnectAttempts=0&fileName=hello.txt";
+    }
+
+    @Test
+    public void uploadToHostListedInCertificate() {
+        template.sendBody(getFtpUrl("localhost", "listed"), "Hello World");
+
+        assertFileExists(service.ftpFile("listed/hello.txt"));
+    }
+
+    @Test
+    public void uploadToHostNotListedInCertificateFails() {
+        String uri = getFtpUrl("127.0.0.1", "notlisted");
+
+        Exception ex = assertThrows(CamelExecutionException.class, () -> 
template.sendBody(uri, "Hello World"));
+
+        assertNotNull(ObjectHelper.getException(SSLHandshakeException.class, 
ex), "The TLS handshake should fail");
+        assertFalse(service.ftpFile("notlisted/hello.txt").toFile().exists());
+    }
+
+    @Test
+    public void 
uploadToHostNotListedInCertificateWithHostnameVerificationTurnedOff() {
+        template.sendBody(getFtpUrl("127.0.0.1", "unchecked") + 
"&hostnameVerification=false", "Hello World");
+
+        assertFileExists(service.ftpFile("unchecked/hello.txt"));
+    }
+}
diff --git a/components/camel-ftp/src/test/resources/client.jks 
b/components/camel-ftp/src/test/resources/client.jks
index cf3228bd89c4..3086fb935589 100644
Binary files a/components/camel-ftp/src/test/resources/client.jks and 
b/components/camel-ftp/src/test/resources/client.jks differ
diff --git a/components/camel-ftp/src/test/resources/server.jks 
b/components/camel-ftp/src/test/resources/server.jks
index 28f294b3e9a9..3efde9123134 100644
Binary files a/components/camel-ftp/src/test/resources/server.jks and 
b/components/camel-ftp/src/test/resources/server.jks differ
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java 
b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
index 4713b7842e34..6c1e6ee5bcb3 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
@@ -159,6 +159,7 @@ public final class SecurityUtils {
                 "component:opa",
                 "component:openfga"));
         owners.put("hostnameverification", Set.of(
+                "component:ftps",
                 "component:mina",
                 "component:mllp",
                 "component:netty",
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index d403cecd0527..d22ffd4ca0b3 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1560,6 +1560,18 @@ MINA does not pass a failed handshake on as an error: 
the producer fails with a
 `org.apache.mina.filter.ssl.SslHandler`. A producer that starts timing out 
after the upgrade should be checked for a
 certificate that does not list the host.
 
+=== camel-ftp - the host name of the FTPS server is verified
+
+The `ftps` endpoint now checks, on the control connection, that the 
certificate of the server matches the host of the
+endpoint, with the same rules as an HTTPS client, with `sslContextParameters` 
as well as with the
+`ftpClient.trustStore.*` options. Before, any certificate accepted by the 
trust manager was accepted, whatever host the
+certificate was issued for. Data connections are not checked by host name.
+
+A connection to a host name or an IP address that the certificate does not 
list now fails the TLS handshake, before
+login; an IP address must be listed as a subject alternative name of the 
certificate. Use a certificate that matches the
+host of the endpoint, or set the new `hostnameVerification` option to `false` 
to keep the previous behaviour. A custom
+`ftpClient` set on the endpoint is not changed.
+
 === camel-aws-secrets-manager - the CloudTrail refresh task reads every event 
since the last check
 
 With `camel.vault.aws.refreshEnabled=true` and without SQS notification, each 
check now reads every page of the
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
index b3d72e667651..965ea58aede7 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java
@@ -2244,6 +2244,44 @@ public interface FtpsEndpointBuilderFactory {
             doSetMultiValueProperties("ftpClientTrustStoreParameters", 
"ftpClient.trustStore.", values);
             return this;
         }
+        /**
+         * Whether to check that the certificate of the server matches the host
+         * Camel connects to, with the same rules as an HTTPS client. When
+         * disabled, any certificate accepted by the trust manager is accepted,
+         * whatever host it was issued for. This option does not apply to a
+         * custom ftpClient.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: security
+         * 
+         * @param hostnameVerification the value to set
+         * @return the dsl builder
+         */
+        default FtpsEndpointConsumerBuilder hostnameVerification(boolean 
hostnameVerification) {
+            doSetProperty("hostnameVerification", hostnameVerification);
+            return this;
+        }
+        /**
+         * Whether to check that the certificate of the server matches the host
+         * Camel connects to, with the same rules as an HTTPS client. When
+         * disabled, any certificate accepted by the trust manager is accepted,
+         * whatever host it was issued for. This option does not apply to a
+         * custom ftpClient.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: security
+         * 
+         * @param hostnameVerification the value to set
+         * @return the dsl builder
+         */
+        default FtpsEndpointConsumerBuilder hostnameVerification(String 
hostnameVerification) {
+            doSetProperty("hostnameVerification", hostnameVerification);
+            return this;
+        }
         /**
          * Set the security mode (Implicit/Explicit). true - Implicit Mode /
          * False - Explicit Mode.
@@ -4221,6 +4259,44 @@ public interface FtpsEndpointBuilderFactory {
             doSetMultiValueProperties("ftpClientTrustStoreParameters", 
"ftpClient.trustStore.", values);
             return this;
         }
+        /**
+         * Whether to check that the certificate of the server matches the host
+         * Camel connects to, with the same rules as an HTTPS client. When
+         * disabled, any certificate accepted by the trust manager is accepted,
+         * whatever host it was issued for. This option does not apply to a
+         * custom ftpClient.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: security
+         * 
+         * @param hostnameVerification the value to set
+         * @return the dsl builder
+         */
+        default FtpsEndpointProducerBuilder hostnameVerification(boolean 
hostnameVerification) {
+            doSetProperty("hostnameVerification", hostnameVerification);
+            return this;
+        }
+        /**
+         * Whether to check that the certificate of the server matches the host
+         * Camel connects to, with the same rules as an HTTPS client. When
+         * disabled, any certificate accepted by the trust manager is accepted,
+         * whatever host it was issued for. This option does not apply to a
+         * custom ftpClient.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: security
+         * 
+         * @param hostnameVerification the value to set
+         * @return the dsl builder
+         */
+        default FtpsEndpointProducerBuilder hostnameVerification(String 
hostnameVerification) {
+            doSetProperty("hostnameVerification", hostnameVerification);
+            return this;
+        }
         /**
          * Set the security mode (Implicit/Explicit). true - Implicit Mode /
          * False - Explicit Mode.
@@ -5701,6 +5777,44 @@ public interface FtpsEndpointBuilderFactory {
             doSetMultiValueProperties("ftpClientTrustStoreParameters", 
"ftpClient.trustStore.", values);
             return this;
         }
+        /**
+         * Whether to check that the certificate of the server matches the host
+         * Camel connects to, with the same rules as an HTTPS client. When
+         * disabled, any certificate accepted by the trust manager is accepted,
+         * whatever host it was issued for. This option does not apply to a
+         * custom ftpClient.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: security
+         * 
+         * @param hostnameVerification the value to set
+         * @return the dsl builder
+         */
+        default FtpsEndpointBuilder hostnameVerification(boolean 
hostnameVerification) {
+            doSetProperty("hostnameVerification", hostnameVerification);
+            return this;
+        }
+        /**
+         * Whether to check that the certificate of the server matches the host
+         * Camel connects to, with the same rules as an HTTPS client. When
+         * disabled, any certificate accepted by the trust manager is accepted,
+         * whatever host it was issued for. This option does not apply to a
+         * custom ftpClient.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: security
+         * 
+         * @param hostnameVerification the value to set
+         * @return the dsl builder
+         */
+        default FtpsEndpointBuilder hostnameVerification(String 
hostnameVerification) {
+            doSetProperty("hostnameVerification", hostnameVerification);
+            return this;
+        }
         /**
          * Set the security mode (Implicit/Explicit). true - Implicit Mode /
          * False - Explicit Mode.
diff --git a/test-infra/camel-test-infra-ftp/src/main/resources/server.jks 
b/test-infra/camel-test-infra-ftp/src/main/resources/server.jks
index 28f294b3e9a9..3efde9123134 100644
Binary files a/test-infra/camel-test-infra-ftp/src/main/resources/server.jks 
and b/test-infra/camel-test-infra-ftp/src/main/resources/server.jks differ

Reply via email to