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 e413bf8987c8 CAMEL-26549: camel-ftp, camel-mina-sftp - the SFTP and
SCP clients can negotiate the post-quantum key exchange (#29094)
e413bf8987c8 is described below
commit e413bf8987c84a089d88800523cc0f3f34a6b05b
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Nov 5 13:00:43 2026 +0100
CAMEL-26549: camel-ftp, camel-mina-sftp - the SFTP and SCP clients can
negotiate the post-quantum key exchange (#29094)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]>
---
.../camel/catalog/docs/mina-sftp-security.adoc | 48 ++++++++++++++++-
.../apache/camel/catalog/docs/sftp-component.adoc | 26 +++++++++
.../apache/camel/catalog/docs/ssh-component.adoc | 20 +++++++
components/camel-ftp/pom.xml | 9 ++++
.../camel-ftp/src/main/docs/sftp-component.adoc | 26 +++++++++
.../integration/SftpPostQuantumKeyExchangeIT.java | 61 ++++++++++++++++++++++
components/camel-mina-sftp/pom.xml | 9 ++++
.../src/main/docs/mina-sftp-security.adoc | 48 ++++++++++++++++-
.../mina/sftp/SftpKeyExchangeProtocolsIT.java | 18 +++++++
.../camel-ssh/src/main/docs/ssh-component.adoc | 20 +++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 29 ++++++++++
11 files changed, 312 insertions(+), 2 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mina-sftp-security.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mina-sftp-security.adoc
index 8ed427ab53be..fec1aa72d607 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mina-sftp-security.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mina-sftp-security.adoc
@@ -194,12 +194,58 @@
from("mina-sftp://user@host/path?password=secret&keyExchangeProtocols=curve25519
.to("file:local");
----
+=== Post-Quantum Key Exchange
+
+The hybrid post-quantum key exchanges are proposed to any server that offers
them, with no configuration, so a
+connection is protected against harvest-now-decrypt-later attacks by default.
They are implemented through Bouncy
+Castle, which `camel-mina-sftp` brings on the classpath.
+
+`sshd-core` proposes `sntrup761x25519-sha512` first, under its name and under
its `[email protected]`
+alias, and the ML-KEM groups after it. A server that offers both, such as
OpenSSH 9.9 or later, therefore negotiates
+`sntrup761x25519-sha512` rather than ML-KEM, and an OpenSSH server before 9.9
offers only the alias. To prefer ML-KEM,
+put `mlkem768x25519-sha256` first in `keyExchangeProtocols`.
+
+Note that `keyExchangeProtocols` *replaces* the proposal rather than adding to
it: an endpoint that pins a list of
+classical algorithms never negotiates a post-quantum one. Check any value
configured before Camel 4.24. To require
+the post-quantum exchange, and fail rather than fall back, pin the proposal to
the hybrids alone:
+
+[source,java]
+----
+from("mina-sftp://user@host/path?password=secret&keyExchangeProtocols=mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected]")
+ .to("file:local");
+----
+
+With Bouncy Castle on the classpath, Apache MINA SSHD also registers it as the
security provider of all its
+cryptography, not only of the key exchange: key factories, key pair
generation, key agreement, signatures and digests
+come from Bouncy Castle rather than from the JDK providers, which matters on a
JVM configured for FIPS. The
+registration is switched off with the
`-Dorg.apache.sshd.security.provider.BC.enabled=false` system property.
+
=== Available Key Exchange Protocols
[cols="2,3,1"]
|===
| Protocol Name | Description | Recommended
+| `mlkem768x25519-sha256`
+| Hybrid ML-KEM-768 with X25519 (post-quantum)
+| Yes
+
+| `mlkem768nistp256-sha256`
+| Hybrid ML-KEM-768 with NIST P-256 (post-quantum)
+| Yes
+
+| `mlkem1024nistp384-sha384`
+| Hybrid ML-KEM-1024 with NIST P-384 (post-quantum)
+| Yes
+
+| `sntrup761x25519-sha512`
+| Hybrid Streamlined NTRU Prime with X25519 (post-quantum)
+| Yes
+
+| `[email protected]`
+| The same exchange under the name that OpenSSH servers before 9.9 use
(post-quantum)
+| Yes
+
| `curve25519-sha256`
| Modern Curve25519 with SHA-256
| Yes
@@ -311,7 +357,7 @@ OpenSSH certificate variants are also supported (e.g.,
`ssh-ed25519-cert-v01@ope
[source,java]
----
from("mina-sftp://user@host/path?password=secret"
- +
"&keyExchangeProtocols=curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group16-sha512"
+ +
"&keyExchangeProtocols=mlkem768x25519-sha256,curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group16-sha512"
+ "&serverHostKeys=ssh-ed25519,rsa-sha2-512,ecdsa-sha2-nistp256"
+
"&[email protected],[email protected],aes256-ctr")
.to("file:local");
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sftp-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sftp-component.adoc
index f893b297d3c3..97d53e24d3dd 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sftp-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/sftp-component.adoc
@@ -290,6 +290,32 @@
https://datatracker.ietf.org/doc/html/draft-miller-ssh-cert-04[draft-04]) are re
If you need to control the CA signature algorithms that JSch will accept, use
the
`caSignatureAlgorithms` parameter.
+=== Post-Quantum Key Exchange
+
+The SFTP client proposes the hybrid post-quantum key exchange
`mlkem768x25519-sha256` ahead of the classical groups,
+so a connection to a server that offers it is protected against
harvest-now-decrypt-later attacks without any
+configuration. ML-KEM is implemented either by the JDK, from Java 24 on, or by
Bouncy Castle, which `camel-ftp` brings
+on the classpath for the Java 21 baseline. Where neither is available, the
group is dropped from the proposal and the
+handshake falls back to a classical key exchange.
+
+The server decides as much as the client does. jsch proposes only
`mlkem768x25519-sha256` by default, so a server that
+offers `[email protected]` alone, such as OpenSSH before 9.9,
takes the connection back to a classical
+key exchange unless that group is added to `keyExchangeProtocols`, and a
server that offers no hybrid group does so in
+any case. To require the post-quantum exchange, and fail rather than fall
back, pin the proposal to the hybrid groups:
+
+[source,text]
+----
+sftp://localhost/inbox?username=admin&keyExchangeProtocols=mlkem768x25519-sha256,[email protected]
+----
+
+`keyExchangeProtocols` replaces the proposal rather than adding to it, so an
endpoint that pins a list of classical
+groups never negotiates a post-quantum one. Check any value configured before
Camel 4.24, and add
+`mlkem768x25519-sha256`, and `[email protected]` for the
servers before OpenSSH 9.9, to the front of
+it.
+
+The same applies to the `ssh` component through its `kex` option. The `scp`
component (`camel-jsch`) has no endpoint
+option for the proposal: the `jsch.kex` system property sets it for the whole
JVM.
+
=== Restoring Deprecated Key Types and Algorithms
As of Camel 3.17.0, key types and algorithms that use SHA1 have been
deprecated. These can be restored, if necessary, by setting JSch configuration
directly. E.g.:
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 3682eec3af7a..52e5aae9d87c 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
@@ -241,6 +241,26 @@ YAML::
----
====
+=== Post-Quantum Key Exchange
+
+`sshd-core` offers the hybrid post-quantum key exchanges
`mlkem768x25519-sha256`, `mlkem768nistp256-sha256` and
+`mlkem1024nistp384-sha384`, as well as `sntrup761x25519-sha512`, and proposes
them to a server that supports them
+without any configuration. They are implemented through Bouncy Castle, which
`camel-ssh` already brings on the
+classpath. `sntrup761x25519-sha512` is proposed first, under its name and
under its `[email protected]`
+alias, so a server that offers both, such as OpenSSH 9.9 or later, negotiates
it rather than ML-KEM; an OpenSSH server
+before 9.9 offers only the alias. To prefer ML-KEM, put
`mlkem768x25519-sha256` first in `kex`.
+
+The `kex` option *replaces* the proposal rather than adding to it, so an
endpoint that pins a list of classical
+algorithms never negotiates a post-quantum one. Check any value configured
before Camel 4.24. To require the
+post-quantum exchange, and fail rather than fall back to a classical one, pin
`kex` to the hybrid groups alone:
+
+[source,text]
+----
+ssh://host:22?username=admin&kex=mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected]
+----
+
+The equivalent option on the `sftp` component is `keyExchangeProtocols`.
+
[[SSH-CertificateDependencies]]
=== Certificate Dependencies
diff --git a/components/camel-ftp/pom.xml b/components/camel-ftp/pom.xml
index fe5d8bcbcb36..548e3400a1be 100644
--- a/components/camel-ftp/pom.xml
+++ b/components/camel-ftp/pom.xml
@@ -45,6 +45,15 @@
<artifactId>jsch</artifactId>
<version>${jsch-version}</version>
</dependency>
+ <!-- jsch proposes the post-quantum key exchange mlkem768x25519-sha256
first, but implements ML-KEM either
+ through the JDK 24+ multi-release classes or through the
BouncyCastle lightweight API. On the Java 21
+ baseline only the second path exists, and without it jsch drops
the group from its proposal and the
+ connection silently falls back to a classical key exchange. -->
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>${bouncycastle-version}</version>
+ </dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
diff --git a/components/camel-ftp/src/main/docs/sftp-component.adoc
b/components/camel-ftp/src/main/docs/sftp-component.adoc
index f893b297d3c3..97d53e24d3dd 100644
--- a/components/camel-ftp/src/main/docs/sftp-component.adoc
+++ b/components/camel-ftp/src/main/docs/sftp-component.adoc
@@ -290,6 +290,32 @@
https://datatracker.ietf.org/doc/html/draft-miller-ssh-cert-04[draft-04]) are re
If you need to control the CA signature algorithms that JSch will accept, use
the
`caSignatureAlgorithms` parameter.
+=== Post-Quantum Key Exchange
+
+The SFTP client proposes the hybrid post-quantum key exchange
`mlkem768x25519-sha256` ahead of the classical groups,
+so a connection to a server that offers it is protected against
harvest-now-decrypt-later attacks without any
+configuration. ML-KEM is implemented either by the JDK, from Java 24 on, or by
Bouncy Castle, which `camel-ftp` brings
+on the classpath for the Java 21 baseline. Where neither is available, the
group is dropped from the proposal and the
+handshake falls back to a classical key exchange.
+
+The server decides as much as the client does. jsch proposes only
`mlkem768x25519-sha256` by default, so a server that
+offers `[email protected]` alone, such as OpenSSH before 9.9,
takes the connection back to a classical
+key exchange unless that group is added to `keyExchangeProtocols`, and a
server that offers no hybrid group does so in
+any case. To require the post-quantum exchange, and fail rather than fall
back, pin the proposal to the hybrid groups:
+
+[source,text]
+----
+sftp://localhost/inbox?username=admin&keyExchangeProtocols=mlkem768x25519-sha256,[email protected]
+----
+
+`keyExchangeProtocols` replaces the proposal rather than adding to it, so an
endpoint that pins a list of classical
+groups never negotiates a post-quantum one. Check any value configured before
Camel 4.24, and add
+`mlkem768x25519-sha256`, and `[email protected]` for the
servers before OpenSSH 9.9, to the front of
+it.
+
+The same applies to the `ssh` component through its `kex` option. The `scp`
component (`camel-jsch`) has no endpoint
+option for the proposal: the `jsch.kex` system property sets it for the whole
JVM.
+
=== Restoring Deprecated Key Types and Algorithms
As of Camel 3.17.0, key types and algorithms that use SHA1 have been
deprecated. These can be restored, if necessary, by setting JSch configuration
directly. E.g.:
diff --git
a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/integration/SftpPostQuantumKeyExchangeIT.java
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/integration/SftpPostQuantumKeyExchangeIT.java
new file mode 100644
index 000000000000..faa358b08bf9
--- /dev/null
+++
b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/integration/SftpPostQuantumKeyExchangeIT.java
@@ -0,0 +1,61 @@
+/*
+ * 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.sftp.integration;
+
+import java.io.File;
+
+import com.jcraft.jsch.JSch;
+import org.apache.camel.Exchange;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The hybrid post-quantum key exchange must be usable, and used by default.
+ * <p/>
+ * jsch proposes {@code mlkem768x25519-sha256} first, but it only implements
ML-KEM through the JDK 24+ multi-release
+ * classes or through the BouncyCastle lightweight API - so on the Java 21
baseline the group is dropped from the
+ * proposal, and the handshake silently falls back to a classical key
exchange, unless BouncyCastle is on the runtime
+ * classpath.
+ */
+@EnabledIf(value =
"org.apache.camel.test.infra.ftp.services.embedded.SftpUtil#hasRequiredAlgorithms('src/test/resources/hostkey.pem')")
+public class SftpPostQuantumKeyExchangeIT extends SftpServerTestSupport {
+
+ private static final String PQC_KEX = "mlkem768x25519-sha256";
+
+ @Test
+ public void theDefaultProposalLeadsWithTheHybridGroup() {
+ assertTrue(JSch.getConfig("kex").startsWith(PQC_KEX),
+ "jsch must propose " + PQC_KEX + " first, but proposes: " +
JSch.getConfig("kex"));
+ }
+
+ @Test
+ public void aTransferNegotiatesTheHybridGroup() {
+ // pinning the proposal to the hybrid group only: the handshake can
succeed only if ML-KEM is really available
+ String uri
+ =
"sftp://localhost:{{ftp.server.port}}/{{ftp.root.dir}}?username=admin&password=admin"
+ + "&keyExchangeProtocols=" + PQC_KEX
+ + "&knownHostsFile=" + service.getKnownHostsFile();
+ template.sendBodyAndHeader(uri, "Hello World", Exchange.FILE_NAME,
"hello-pqc.txt");
+
+ File file = ftpFile("hello-pqc.txt").toFile();
+ assertTrue(file.exists(), "File should exist: " + file);
+ assertEquals("Hello World",
context.getTypeConverter().convertTo(String.class, file));
+ }
+}
diff --git a/components/camel-mina-sftp/pom.xml
b/components/camel-mina-sftp/pom.xml
index e0829ccf85cb..96873700dbd4 100644
--- a/components/camel-mina-sftp/pom.xml
+++ b/components/camel-mina-sftp/pom.xml
@@ -47,6 +47,15 @@
<artifactId>sshd-sftp</artifactId>
<version>${sshd-version}</version>
</dependency>
+ <!-- sshd implements the post-quantum key exchanges
(mlkem768x25519-sha256 and the other hybrids, and
+ sntrup761x25519-sha512) through the BouncyCastle lightweight API.
Without it on the runtime classpath
+ those factories report themselves unsupported, so they never
reach the proposal and the connection
+ silently falls back to a classical key exchange. -->
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>${bouncycastle-version}</version>
+ </dependency>
<!-- testing -->
<dependency>
diff --git a/components/camel-mina-sftp/src/main/docs/mina-sftp-security.adoc
b/components/camel-mina-sftp/src/main/docs/mina-sftp-security.adoc
index 8ed427ab53be..fec1aa72d607 100644
--- a/components/camel-mina-sftp/src/main/docs/mina-sftp-security.adoc
+++ b/components/camel-mina-sftp/src/main/docs/mina-sftp-security.adoc
@@ -194,12 +194,58 @@
from("mina-sftp://user@host/path?password=secret&keyExchangeProtocols=curve25519
.to("file:local");
----
+=== Post-Quantum Key Exchange
+
+The hybrid post-quantum key exchanges are proposed to any server that offers
them, with no configuration, so a
+connection is protected against harvest-now-decrypt-later attacks by default.
They are implemented through Bouncy
+Castle, which `camel-mina-sftp` brings on the classpath.
+
+`sshd-core` proposes `sntrup761x25519-sha512` first, under its name and under
its `[email protected]`
+alias, and the ML-KEM groups after it. A server that offers both, such as
OpenSSH 9.9 or later, therefore negotiates
+`sntrup761x25519-sha512` rather than ML-KEM, and an OpenSSH server before 9.9
offers only the alias. To prefer ML-KEM,
+put `mlkem768x25519-sha256` first in `keyExchangeProtocols`.
+
+Note that `keyExchangeProtocols` *replaces* the proposal rather than adding to
it: an endpoint that pins a list of
+classical algorithms never negotiates a post-quantum one. Check any value
configured before Camel 4.24. To require
+the post-quantum exchange, and fail rather than fall back, pin the proposal to
the hybrids alone:
+
+[source,java]
+----
+from("mina-sftp://user@host/path?password=secret&keyExchangeProtocols=mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected]")
+ .to("file:local");
+----
+
+With Bouncy Castle on the classpath, Apache MINA SSHD also registers it as the
security provider of all its
+cryptography, not only of the key exchange: key factories, key pair
generation, key agreement, signatures and digests
+come from Bouncy Castle rather than from the JDK providers, which matters on a
JVM configured for FIPS. The
+registration is switched off with the
`-Dorg.apache.sshd.security.provider.BC.enabled=false` system property.
+
=== Available Key Exchange Protocols
[cols="2,3,1"]
|===
| Protocol Name | Description | Recommended
+| `mlkem768x25519-sha256`
+| Hybrid ML-KEM-768 with X25519 (post-quantum)
+| Yes
+
+| `mlkem768nistp256-sha256`
+| Hybrid ML-KEM-768 with NIST P-256 (post-quantum)
+| Yes
+
+| `mlkem1024nistp384-sha384`
+| Hybrid ML-KEM-1024 with NIST P-384 (post-quantum)
+| Yes
+
+| `sntrup761x25519-sha512`
+| Hybrid Streamlined NTRU Prime with X25519 (post-quantum)
+| Yes
+
+| `[email protected]`
+| The same exchange under the name that OpenSSH servers before 9.9 use
(post-quantum)
+| Yes
+
| `curve25519-sha256`
| Modern Curve25519 with SHA-256
| Yes
@@ -311,7 +357,7 @@ OpenSSH certificate variants are also supported (e.g.,
`ssh-ed25519-cert-v01@ope
[source,java]
----
from("mina-sftp://user@host/path?password=secret"
- +
"&keyExchangeProtocols=curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group16-sha512"
+ +
"&keyExchangeProtocols=mlkem768x25519-sha256,curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group16-sha512"
+ "&serverHostKeys=ssh-ed25519,rsa-sha2-512,ecdsa-sha2-nistp256"
+
"&[email protected],[email protected],aes256-ctr")
.to("file:local");
diff --git
a/components/camel-mina-sftp/src/test/java/org/apache/camel/component/file/remote/mina/sftp/SftpKeyExchangeProtocolsIT.java
b/components/camel-mina-sftp/src/test/java/org/apache/camel/component/file/remote/mina/sftp/SftpKeyExchangeProtocolsIT.java
index c2caf941fc61..e866335acfe4 100644
---
a/components/camel-mina-sftp/src/test/java/org/apache/camel/component/file/remote/mina/sftp/SftpKeyExchangeProtocolsIT.java
+++
b/components/camel-mina-sftp/src/test/java/org/apache/camel/component/file/remote/mina/sftp/SftpKeyExchangeProtocolsIT.java
@@ -74,6 +74,24 @@ public class SftpKeyExchangeProtocolsIT extends
SftpServerTestSupport {
assertEquals(expectedKex,
endpoint.getConfiguration().getKeyExchangeProtocols());
}
+ @Test
+ public void testSftpPostQuantumKeyExchange() {
+ // pinning the proposal to the hybrid group only: the handshake can
succeed only if ML-KEM is really
+ // available, which it is through the BouncyCastle lightweight API
sshd implements it against
+ String kex = "mlkem768x25519-sha256";
+ String uri
+ =
"mina-sftp://localhost:{{ftp.server.port}}/{{ftp.root.dir}}?username=admin&password=admin&keyExchangeProtocols="
+ + kex + "&knownHostsFile=" + service.getKnownHostsFile();
+ template.sendBodyAndHeader(uri, "Hello World", Exchange.FILE_NAME,
"hello-kex-pqc.txt");
+
+ File file = ftpFile("hello-kex-pqc.txt").toFile();
+ assertTrue(file.exists(), "File should exist: " + file);
+ assertEquals("Hello World",
context.getTypeConverter().convertTo(String.class, file));
+
+ MinaSftpEndpoint endpoint = context.getEndpoint(uri,
MinaSftpEndpoint.class);
+ assertEquals(List.of(kex),
endpoint.getConfiguration().getKeyExchangeProtocols());
+ }
+
@Test
public void testSftpInvalidKeyExchangeProtocol() {
String uri
diff --git a/components/camel-ssh/src/main/docs/ssh-component.adoc
b/components/camel-ssh/src/main/docs/ssh-component.adoc
index 3682eec3af7a..52e5aae9d87c 100644
--- a/components/camel-ssh/src/main/docs/ssh-component.adoc
+++ b/components/camel-ssh/src/main/docs/ssh-component.adoc
@@ -241,6 +241,26 @@ YAML::
----
====
+=== Post-Quantum Key Exchange
+
+`sshd-core` offers the hybrid post-quantum key exchanges
`mlkem768x25519-sha256`, `mlkem768nistp256-sha256` and
+`mlkem1024nistp384-sha384`, as well as `sntrup761x25519-sha512`, and proposes
them to a server that supports them
+without any configuration. They are implemented through Bouncy Castle, which
`camel-ssh` already brings on the
+classpath. `sntrup761x25519-sha512` is proposed first, under its name and
under its `[email protected]`
+alias, so a server that offers both, such as OpenSSH 9.9 or later, negotiates
it rather than ML-KEM; an OpenSSH server
+before 9.9 offers only the alias. To prefer ML-KEM, put
`mlkem768x25519-sha256` first in `kex`.
+
+The `kex` option *replaces* the proposal rather than adding to it, so an
endpoint that pins a list of classical
+algorithms never negotiates a post-quantum one. Check any value configured
before Camel 4.24. To require the
+post-quantum exchange, and fail rather than fall back to a classical one, pin
`kex` to the hybrid groups alone:
+
+[source,text]
+----
+ssh://host:22?username=admin&kex=mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected]
+----
+
+The equivalent option on the `sftp` component is `keyExchangeProtocols`.
+
[[SSH-CertificateDependencies]]
=== Certificate Dependencies
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 25cad5f984cb..d85dce0dbc0c 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
@@ -1334,6 +1334,35 @@ refused for reading and for writing, where it used to be
read or written. Name t
its real directory instead. An absolute `file` inside the project directory is
now accepted, as the same file named
relative to it.
+=== camel-ftp, camel-jsch, camel-mina-sftp - the SFTP and SCP clients
negotiate the post-quantum key exchange
+
+`camel-ftp` and `camel-mina-sftp` now declare
`org.bouncycastle:bcprov-jdk18on` as a compile dependency, and
+`camel-jsch` picks it up through `camel-ftp`.
+
+Both SSH stacks propose a hybrid post-quantum key exchange ahead of the
classical groups - `mlkem768x25519-sha256`
+for jsch; `sntrup761x25519-sha512`, also under its
`[email protected]` name, then
+`mlkem768x25519-sha256`, `mlkem1024nistp384-sha384` and
`mlkem768nistp256-sha256`, in that order, for Apache MINA
+SSHD - but both implement ML-KEM through the Bouncy Castle lightweight API
(jsch can also use the JDK from Java 24
+on). On the Java 21 baseline neither had it on the runtime classpath, so the
hybrid groups never reached the
+proposal and every SFTP or SCP connection silently fell back to a classical
key exchange.
+
+These endpoints therefore now perform a different key exchange than before
against a server that offers a hybrid
+group. Which one depends on the server: OpenSSH before 9.9 offers only
`[email protected]`, which
+`mina-sftp` negotiates and `sftp` does not propose unless it is added to
`keyExchangeProtocols`; OpenSSH 9.9 and
+later also offers `mlkem768x25519-sha256`, which `sftp` negotiates, while
`mina-sftp` still negotiates
+`sntrup761x25519-sha512` because it proposes that first. A deployment that
must keep the previous behaviour can pin
+the classical groups with `keyExchangeProtocols` on `sftp` and `mina-sftp`,
and with `kex` on `ssh`. `scp`
+(`camel-jsch`) has no such option: there the proposal is set for the whole JVM
with the `jsch.kex` system property,
+for example
`-Djsch.kex=curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group16-sha512`.
Note that these
+options replace the proposal rather than adding to it, so an endpoint that
already pins a list of classical groups
+is unaffected - and will not negotiate a post-quantum exchange until a hybrid
group is added to that list.
+
+With Bouncy Castle on the classpath, Apache MINA SSHD also registers it as the
security provider of all its
+cryptography in `camel-mina-sftp`, not only of the key exchange: key
factories, key pair generation, key agreement,
+signatures and digests now come from Bouncy Castle rather than from the JDK
providers, which matters on a JVM
+configured for FIPS. The registration is switched off with the
`-Dorg.apache.sshd.security.provider.BC.enabled=false`
+system property.
+
=== camel-ftp, camel-mina-sftp, camel-jsch, camel-smb, camel-azure-files -
checksumFileAlgorithm writes a checksum again
Since 4.18 the producers of the remote file components (ftp, ftps, sftp,
mina-sftp, scp, smb, azure-files) accepted