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

jamesnetherton pushed a commit to branch camel-quarkus-main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus-examples.git


The following commit(s) were added to refs/heads/camel-quarkus-main by this 
push:
     new 421a8650 Add http-pqc-j25 example using JDK post-quantum TLS without 
Bouncy Castle
421a8650 is described below

commit 421a865058a08d7cdb29989abdf6262c0d8b128e
Author: Jiří Ondrušek <[email protected]>
AuthorDate: Tue Nov 10 17:30:37 2026 +0100

    Add http-pqc-j25 example using JDK post-quantum TLS without Bouncy Castle
    
    * Add http-pqc-j25 example using JDK post-quantum TLS without Bouncy Castle
    
    Same use case as http-pqc-j21, but the hybrid X25519MLKEM768 key exchange
    comes from the JDK's SunJSSE (JDK 25.0.5+ / 27, JEP 527), configured only
    through the Quarkus TLS registry (key-exchange-groups, 
pqc-enforcement-policy).
    Demo certificates are generated during the build by the SmallRye certificate
    generator (Maven plugin and @Certificates). Tests are skipped on JDKs older
    than 25.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    * Address review: simplify PQC example
    
    Assert IOException for a missing client certificate (TLS 1.3 may fail after 
the handshake).
    Call SSLParameters named groups API directly, dropping JDK 17 workarounds.
    Reword the response text, note CI skips native mode, remove 
quarkus.log.file.enabled.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    ---------
    
    Co-authored-by: James Netherton <[email protected]>
    Co-authored-by: jiriOndrusek-agent <[email protected]>
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 docs/modules/ROOT/attachments/examples.json        |   5 +
 http-pqc-j25/README.adoc                           | 153 ++++++++++
 http-pqc-j25/eclipse-formatter-config.xml          | 276 +++++++++++++++++
 http-pqc-j25/pom.xml                               | 338 +++++++++++++++++++++
 .../main/java/org/acme/http/pqc/PqcCamelRoute.java |  45 +++
 .../src/main/resources/application.properties      |  58 ++++
 .../java/org/acme/http/pqc/AbstractPqcTest.java    | 132 ++++++++
 .../test/java/org/acme/http/pqc/PqcOnlyTest.java   |  53 ++++
 .../org/acme/http/pqc/PqcSupportedCondition.java   |  52 ++++
 .../java/org/acme/http/pqc/PqcWithFallbackIT.java  |  26 ++
 .../org/acme/http/pqc/PqcWithFallbackTest.java     |  43 +++
 .../org/acme/http/pqc/profiles/PqcOnlyProfile.java |  34 +++
 pom.xml                                            |   1 +
 13 files changed, 1216 insertions(+)

diff --git a/docs/modules/ROOT/attachments/examples.json 
b/docs/modules/ROOT/attachments/examples.json
index 5a8f36bc..92e7aa95 100644
--- a/docs/modules/ROOT/attachments/examples.json
+++ b/docs/modules/ROOT/attachments/examples.json
@@ -59,6 +59,11 @@
     "description": "Shows how to implement quantum-resistant TLS using the 
BouncyCastle JSSE provider for native PQC support with hybrid cipher suites",
     "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/http-pqc";
   },
+  {
+    "title": "HTTP with Post-Quantum Cryptography (PQC) (Java 25)",
+    "description": "Shows how to implement quantum-resistant TLS using the 
JDK\u0027s own hybrid X25519MLKEM768 key exchange, configured through the 
Quarkus TLS registry, without Bouncy Castle",
+    "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/http-pqc-j25";
+  },
   {
     "title": "HTTP with vanilla JAX-RS or with Camel `platform-http` 
component",
     "description": "Shows how to create HTTP endpoints using either the 
RESTEasy",
diff --git a/http-pqc-j25/README.adoc b/http-pqc-j25/README.adoc
new file mode 100644
index 00000000..16d169d8
--- /dev/null
+++ b/http-pqc-j25/README.adoc
@@ -0,0 +1,153 @@
+= HTTP with Post-Quantum Cryptography (PQC): A Camel Quarkus example (Java 25)
+:cq-example-description: An example that shows how to implement 
quantum-resistant TLS using the JDK's own hybrid X25519MLKEM768 key exchange, 
configured through the Quarkus TLS registry, without Bouncy Castle
+
+{cq-description}
+
+This example serves a Camel `platform-http` endpoint over mutual TLS 1.3 
protected by the hybrid post-quantum key exchange **X25519MLKEM768** (classical 
X25519 ECDH combined with ML-KEM-768, NIST FIPS 203).
+
+Unlike the `http-pqc-j21` example, it uses **no Bouncy Castle at all**:
+
+[cols="1,2,2"]
+|===
+| | `http-pqc-j21` | `http-pqc-j25` (this example)
+
+| TLS provider
+| Bouncy Castle JSSE, registered in code
+| JDK SunJSSE
+
+| PQC configuration
+| `jdk.tls.namedGroups` system property
+| Quarkus TLS registry: `quarkus.tls.pqc-enforcement-policy` (JDK default 
named groups)
+
+| Certificates
+| Generated at startup with the Bouncy Castle X.509 API
+| Generated at build/test time with the SmallRye certificate generator
+
+| Extra runtime dependencies
+| `camel-quarkus-support-bouncycastle`, `bctls-jdk18on`
+| none
+|===
+
+NOTE: TLS is pure JDK. Bouncy Castle is used only at build and test time, by 
the SmallRye certificate generator (Maven plugin and the `test` scoped 
`smallrye-certificate-generator-junit5`). It is not on the application 
classpath: `mvn dependency:tree -Dscope=runtime` and `target/quarkus-app/lib` 
contain no Bouncy Castle jar.
+
+IMPORTANT: Requires a JDK with X25519MLKEM768 in SunJSSE 
(https://openjdk.org/jeps/527[JEP 527]): **JDK 25.0.5 (October 2026 update) or 
newer, or JDK 27+**. On older JDK 25 builds the application fails at startup 
with `PQC enforcement policy ... requires X25519MLKEM768 but the configured SSL 
engine does not support it`. Check your JDK (the output must contain 
`X25519MLKEM768`):
+
+[source,shell]
+----
+echo 'System.out.println(String.join(",", 
javax.net.ssl.SSLContext.getDefault().getSupportedSSLParameters().getNamedGroups()))'
 | jshell -q
+----
+
+The tests detect the support at runtime (`PqcSupportedCondition` checks the 
JDK's supported TLS named groups) and are skipped on JDKs without 
X25519MLKEM768, e.g. JDK 25.0.0 - 25.0.4. The tests also require JDK 25 
(`@EnabledForJreRange`). On JDK 21 (CI) the module is compiled with `release` 
21 and the tests are skipped.
+
+TIP: Check the 
https://camel.apache.org/camel-quarkus/latest/first-steps.html[Camel Quarkus 
User guide] for prerequisites and other general information.
+
+== Configuration
+
+The whole PQC setup is in `src/main/resources/application.properties`:
+
+[source,properties]
+----
+quarkus.tls.protocols = TLSv1.3
+quarkus.tls.ssl-engine = jdkssl
+quarkus.tls.pqc-enforcement-policy = client-negotiated
+----
+
+`quarkus.tls.pqc-enforcement-policy` values:
+
+* `client-negotiated` (default here): X25519MLKEM768 is accepted (JDK 25.0.5+ 
offers it by default), classical clients can still connect. Use it for gradual 
migration.
+* `strict`: X25519MLKEM768 is required, only post-quantum hybrid groups are 
accepted, classical clients are rejected.
+* `relaxed` (Quarkus default): PQC is neither enforced nor checked at startup. 
Do not use it for this purpose.
+
+Both `client-negotiated` and `strict` fail at startup when the JDK does not 
support X25519MLKEM768, so the server never silently falls back to 
classical-only TLS.
+
+== Mutual TLS and certificates
+
+The server requires a client certificate (`quarkus.http.ssl.client-auth = 
required`) and trusts only the demo client certificate 
(`quarkus.tls.trust-store`), in both the default and the `strict` setup. 
Clients without a certificate are rejected during the TLS handshake.
+
+For running the application by hand, the Maven build (`generate-resources` 
phase) creates demo certificates in `target/certs/` with the 
`smallrye-certificate-generator-maven-plugin` (password `changeit`, valid 365 
days):
+
+* `pqc-keystore.p12`, `pqc-server-truststore.p12` - server identity 
(`CN=localhost`, SAN `localhost`, `127.0.0.1`) and trusted client
+* `pqc-client-keystore.p12`, `pqc-client-truststore.p12` - client identity and 
trusted server
+* `pqc.crt`, `pqc-client.crt`, `pqc-client.key`, `pqc-client-ca.crt`, ... - 
the same in PEM form (e.g. for `curl`)
+
+The tests generate their own certificates in `target/test-certs/` 
(`@Certificates` on `AbstractPqcTest`) and use them via the `%test` profile.
+
+The generated keys and the `changeit` passwords are for demo only, do not 
reuse them.
+
+The certificates use classical RSA signatures. The post-quantum protection 
comes from the key exchange, which defeats "harvest now, decrypt later" attacks 
on recorded traffic.
+
+== Start in Development Mode
+
+[source,shell]
+----
+mvn clean compile quarkus:dev
+----
+
+The application starts on a random HTTPS port with mutual TLS. Look for 
`Listening on: https://localhost:xxxxx` in the log. Endpoint: `/pqc/secure`.
+
+Any JDK 25.0.5+ client offers X25519MLKEM768 by default. `curl` built with 
OpenSSL 3.5+ also negotiates X25519MLKEM768; clients without PQC support fall 
back to a classical group thanks to `client-negotiated`:
+
+[source,shell]
+----
+curl --cert target/certs/pqc-client.crt --key target/certs/pqc-client.key \
+     --cacert target/certs/pqc-client-ca.crt \
+     https://localhost:xxxxx/pqc/secure
+----
+
+NOTE: The response text describes the hybrid key exchange; a classical client 
connected through the fallback does not get it.
+
+== Package and Run the Application
+
+TIP: Find more details about the JVM mode and Native mode in the Package and 
run section of 
https://camel.apache.org/camel-quarkus/latest/first-steps.html#_package_and_run_the_application[Camel
 Quarkus User guide]
+
+=== JVM Mode
+
+[source,shell]
+----
+mvn clean package
+java -jar target/quarkus-app/quarkus-run.jar
+----
+
+=== Native Mode
+
+IMPORTANT: Native mode requires having GraalVM and other tools installed. 
Please check the Prerequisites section of 
https://camel.apache.org/camel-quarkus/latest/first-steps.html#_prerequisites[Camel
 Quarkus User guide].
+
+The native image must be built by a GraalVM / Mandrel release based on JDK 
25.0.5 or newer, otherwise it lacks X25519MLKEM768 and fails at startup. On a 
JDK below 25 the `skip-native-below-jdk25` profile turns the native build and 
ITs off, even with `-Dnative`. For that reason CI does not run native mode for 
this example, so it is verified by hand on a Mandrel/GraalVM 25.0.5+ builder.
+
+[source,shell]
+----
+mvn clean package -Dnative
+./target/*-runner
+----
+
+== Testing
+
+[source,shell]
+----
+mvn clean test
+----
+
+The tests call the server with the JDK TLS stack, restricting the named groups 
offered by the client:
+
+* `PqcWithFallbackTest` (`client-negotiated`): hybrid client ✓, classical-only 
client (`x25519,secp256r1`) ✓, client without certificate ✗
+* `PqcOnlyTest` (`strict`, via `PqcOnlyProfile`): hybrid client ✓, JDK default 
client ✓, classical-only client ✗ (TLS handshake fails), client without 
certificate ✗
+
+Native mode runs `PqcWithFallbackIT` only, because a native executable is 
built with one configuration:
+
+[source,shell]
+----
+mvn clean verify -Dnative
+----
+
+NOTE: The tests use RestAssured (Apache HttpClient) rather than 
`java.net.http.HttpClient`, because the latter ignores 
`SSLParameters.setNamedGroups()`.
+
+== Additional Resources
+
+* https://openjdk.org/jeps/527[JEP 527 - Post-Quantum Hybrid Key Exchange for 
TLS 1.3]
+* https://quarkus.io/guides/tls-registry-reference[Quarkus TLS registry 
reference]
+* https://csrc.nist.gov/pubs/fips/203/final[NIST FIPS 203 - ML-KEM Standard]
+* https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/[IETF Draft - 
Hybrid ECDHE-MLKEM Key Agreement for TLS 1.3]
+
+== Feedback
+
+Please report bugs and propose improvements via 
https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus] 
project.
diff --git a/http-pqc-j25/eclipse-formatter-config.xml 
b/http-pqc-j25/eclipse-formatter-config.xml
new file mode 100644
index 00000000..2248b2b8
--- /dev/null
+++ b/http-pqc-j25/eclipse-formatter-config.xml
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<profiles version="8">
+    <profile name="Camel Java Conventions" version="8" 
kind="CodeFormatterProfile">
+        <setting 
id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration"
 value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" 
value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" 
value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" 
value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration"
 value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration"
 value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration"
 value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" 
value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" 
value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_block_comments" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_comments" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_header" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_html" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.indent_return_description" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" 
value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.line_length" 
value="120"/>
+        <setting id="org.eclipse.jdt.core.formatter.compact_else_if" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation" 
value="2"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer"
 value="2"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.indentation.size" 
value="8"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do 
not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do 
not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not 
insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not 
insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation"
 value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.lineSplit" value="128"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body"
 value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.char" 
value="space"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.size" 
value="4"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.disabling_tag" 
value="CHECKSTYLE:OFF"/>
+        <setting id="org.eclipse.jdt.core.formatter.enabling_tag" 
value="CHECKSTYLE:ON"/>
+    </profile>
+</profiles>
diff --git a/http-pqc-j25/pom.xml b/http-pqc-j25/pom.xml
new file mode 100644
index 00000000..7e906388
--- /dev/null
+++ b/http-pqc-j25/pom.xml
@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-quarkus-examples-http-pqc-j25</artifactId>
+    <groupId>org.apache.camel.quarkus.examples</groupId>
+    <version>4.0.0-SNAPSHOT</version>
+
+    <name>Camel Quarkus :: Examples :: HTTP PQC Java 25</name>
+    <description>Camel Quarkus Example :: HTTP with Post-Quantum Cryptography 
on Java 25 using X25519MLKEM768 without Bouncy Castle</description>
+
+    <properties>
+        <quarkus.platform.version>4.0.0.Beta3</quarkus.platform.version>
+        
<camel-quarkus.platform.version>4.0.0-SNAPSHOT</camel-quarkus.platform.version>
+
+        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+        
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        
<camel-quarkus.platform.group-id>org.apache.camel.quarkus</camel-quarkus.platform.group-id>
+        
<camel-quarkus.platform.artifact-id>camel-quarkus-bom</camel-quarkus.platform.artifact-id>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <!-- Builds on JDK 21+ (CI runs with JDK 21 and 25); the tests are 
skipped at runtime below JDK 25 -->
+        <maven.compiler.release>21</maven.compiler.release>
+
+        <formatter-maven-plugin.version>2.29.0</formatter-maven-plugin.version>
+        <impsort-maven-plugin.version>1.13.0</impsort-maven-plugin.version>
+        <license-maven-plugin.version>5.1.2</license-maven-plugin.version>
+        <maven-compiler-plugin.version>3.16.0</maven-compiler-plugin.version>
+        <maven-jar-plugin.version>3.5.1</maven-jar-plugin.version>
+        <maven-surefire-plugin.version>3.5.6</maven-surefire-plugin.version>
+        <!-- Keep aligned with smallrye-certificate-generator managed by the 
Quarkus BOM -->
+        
<smallrye-certificate-generator.version>1.1.3</smallrye-certificate-generator.version>
+
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Import BOM -->
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>${camel-quarkus.platform.group-id}</groupId>
+                <artifactId>${camel-quarkus.platform.artifact-id}</artifactId>
+                <version>${camel-quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-platform-http</artifactId>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.smallrye.certs</groupId>
+            <artifactId>smallrye-certificate-generator-junit5</artifactId>
+            <version>${smallrye-certificate-generator.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+
+                <plugin>
+                    <groupId>net.revelc.code.formatter</groupId>
+                    <artifactId>formatter-maven-plugin</artifactId>
+                    <version>${formatter-maven-plugin.version}</version>
+                    <configuration>
+                        
<configFile>${maven.multiModuleProjectDirectory}/eclipse-formatter-config.xml</configFile>
+                        <lineEnding>LF</lineEnding>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>net.revelc.code</groupId>
+                    <artifactId>impsort-maven-plugin</artifactId>
+                    <version>${impsort-maven-plugin.version}</version>
+                    <configuration>
+                        <groups>java.,javax.,org.w3c.,org.xml.,junit.</groups>
+                        <removeUnused>true</removeUnused>
+                        <staticAfter>true</staticAfter>
+                        
<staticGroups>java.,javax.,org.w3c.,org.xml.,junit.</staticGroups>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <showDeprecation>true</showDeprecation>
+                        <showWarnings>true</showWarnings>
+                        <compilerArgs>
+                            <arg>-Xlint:unchecked</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <configuration>
+                        <failIfNoTests>false</failIfNoTests>
+                        <systemPropertyVariables>
+                            
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        </systemPropertyVariables>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>${quarkus.platform.group-id}</groupId>
+                    <artifactId>quarkus-maven-plugin</artifactId>
+                    <version>${quarkus.platform.version}</version>
+                    <extensions>true</extensions>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>${maven-jar-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>com.mycila</groupId>
+                    <artifactId>license-maven-plugin</artifactId>
+                    <version>${license-maven-plugin.version}</version>
+                    <configuration>
+                        <failIfUnknown>true</failIfUnknown>
+                        
<header>${maven.multiModuleProjectDirectory}/header.txt</header>
+                        <excludes>
+                            <exclude>**/*.adoc</exclude>
+                            <exclude>**/*.txt</exclude>
+                            <exclude>**/LICENSE.txt</exclude>
+                            <exclude>**/LICENSE</exclude>
+                            <exclude>**/NOTICE.txt</exclude>
+                            <exclude>**/NOTICE</exclude>
+                            <exclude>**/README</exclude>
+                            <exclude>**/pom.xml.versionsBackup</exclude>
+                            <exclude>**/quarkus.log*</exclude>
+                            <exclude>**/*.p12</exclude>
+                        </excludes>
+                        <mapping>
+                            <java>SLASHSTAR_STYLE</java>
+                            <properties>CAMEL_PROPERTIES_STYLE</properties>
+                            <kt>SLASHSTAR_STYLE</kt>
+                        </mapping>
+                        <headerDefinitions>
+                            
<headerDefinition>${maven.multiModuleProjectDirectory}/license-properties-headerdefinition.xml</headerDefinition>
+                        </headerDefinitions>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+
+            <!-- Demo certificates for running the application by hand (the 
tests generate their own).
+                 Bouncy Castle is used by this plugin only, it is not on the 
application classpath. -->
+            <plugin>
+                <groupId>io.smallrye.certs</groupId>
+                
<artifactId>smallrye-certificate-generator-maven-plugin</artifactId>
+                <version>${smallrye-certificate-generator.version}</version>
+                <executions>
+                    <execution>
+                        <id>generate-certificates</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            
<outputDirectory>${project.build.directory}/certs</outputDirectory>
+                            <certificates>
+                                <certificate>
+                                    <name>pqc</name>
+                                    <formats>
+                                        <format>PKCS12</format>
+                                        <format>PEM</format>
+                                    </formats>
+                                    <password>changeit</password>
+                                    <duration>365</duration>
+                                    <client>true</client>
+                                    <subjectAlternativeNames>
+                                        
<subjectAlternativeName>DNS:localhost</subjectAlternativeName>
+                                        
<subjectAlternativeName>IP:127.0.0.1</subjectAlternativeName>
+                                    </subjectAlternativeNames>
+                                </certificate>
+                            </certificates>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build</id>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code</groupId>
+                <artifactId>impsort-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>sort-imports</id>
+                        <goals>
+                            <goal>sort</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>com.mycila</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>license-format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.native.enabled>true</quarkus.native.enabled>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <!-- This example needs JDK 25.0.5+, but CI runs the native build on 
JDK 21 -->
+        <profile>
+            <id>skip-native-below-jdk25</id>
+            <activation>
+                <jdk>(,25)</jdk>
+            </activation>
+            <properties>
+                <quarkus.native.enabled>false</quarkus.native.enabled>
+                <skipITs>true</skipITs>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/http-pqc-j25/src/main/java/org/acme/http/pqc/PqcCamelRoute.java 
b/http-pqc-j25/src/main/java/org/acme/http/pqc/PqcCamelRoute.java
new file mode 100644
index 00000000..3b8fc8c9
--- /dev/null
+++ b/http-pqc-j25/src/main/java/org/acme/http/pqc/PqcCamelRoute.java
@@ -0,0 +1,45 @@
+/*
+ * 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.acme.http.pqc;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
+
+/**
+ * Simple route demonstrating Post-Quantum Cryptography (PQC) with TLS 1.3.
+ */
+@ApplicationScoped
+public class PqcCamelRoute extends EndpointRouteBuilder {
+
+    private static final String SECURE_RESPONSE = "Secure data delivered over 
mutual TLS.\n\n" +
+            "Connection details:\n" +
+            "- TLS key exchange: negotiated according to the server's PQC 
policy\n" +
+            "- Provider: SunJSSE (JDK 25, no Bouncy Castle)\n\n" +
+            "When X25519MLKEM768 is negotiated, it protects the session key 
against\n" +
+            "harvest-now-decrypt-later attacks. Authentication is classical 
(RSA).";
+
+    @Override
+    public void configure() throws Exception {
+        // API endpoint for secure data delivery over PQC-enabled TLS
+        // Client certificates are validated at TLS layer 
(quarkus.http.ssl.client-auth=required)
+        from(platformHttp("/pqc/secure"))
+                .routeId("pqc-secure")
+                .log("Serving secure data via PQC-enabled TLS connection")
+                .setBody(constant(SECURE_RESPONSE))
+                .setHeader("Content-Type", constant("text/plain; 
charset=utf-8"));
+    }
+}
diff --git a/http-pqc-j25/src/main/resources/application.properties 
b/http-pqc-j25/src/main/resources/application.properties
new file mode 100644
index 00000000..fc939cef
--- /dev/null
+++ b/http-pqc-j25/src/main/resources/application.properties
@@ -0,0 +1,58 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+#
+# Quarkus
+#
+quarkus.banner.enabled = false
+
+# HTTPS only, on a random port (prevents port conflicts in tests)
+quarkus.http.ssl-port = 0
+quarkus.http.insecure-requests = disabled
+
+# Require client certificates (mutual TLS)
+quarkus.http.ssl.client-auth = required
+
+#
+# TLS registry (default bucket, used by the HTTP server)
+# Server key store, and trust store with the client certificate.
+# Generated by smallrye-certificate-generator-maven-plugin during the Maven 
build, see pom.xml
+#
+quarkus.tls.key-store.p12.path = target/certs/pqc-keystore.p12
+# Demo only: generated keys and "changeit" passwords, do not reuse
+quarkus.tls.key-store.p12.password = changeit
+quarkus.tls.trust-store.p12.path = target/certs/pqc-server-truststore.p12
+quarkus.tls.trust-store.p12.password = changeit
+# The tests generate their own certificates (@Certificates in AbstractPqcTest)
+%test.quarkus.tls.key-store.p12.path = target/test-certs/pqc-keystore.p12
+%test.quarkus.tls.trust-store.p12.path = 
target/test-certs/pqc-server-truststore.p12
+# The packaged application (PqcWithFallbackIT) uses the test certificates as 
well
+quarkus.test.integration-test-profile = test
+
+# TLS 1.3 is required for hybrid key exchange
+quarkus.tls.protocols = TLSv1.3
+
+# Use the JDK's own TLS stack (SunJSSE), no Bouncy Castle or OpenSSL
+quarkus.tls.ssl-engine = jdkssl
+
+# Hybrid post-quantum key exchange (X25519 + ML-KEM-768), with classical 
fallback.
+# Named groups are the JDK defaults (JDK 25.0.5+ includes X25519MLKEM768).
+# 'client-negotiated' accepts X25519MLKEM768 but still accepts classical-only 
clients.
+# 'strict' accepts only post-quantum groups (see PqcOnlyProfile in the tests).
+# Both fail at startup if the JDK does not support X25519MLKEM768.
+# 'relaxed' (Quarkus default) enforces nothing and does no
+# startup check, so PQC silently depends on the JDK. Do not use it here.
+quarkus.tls.pqc-enforcement-policy = client-negotiated
diff --git a/http-pqc-j25/src/test/java/org/acme/http/pqc/AbstractPqcTest.java 
b/http-pqc-j25/src/test/java/org/acme/http/pqc/AbstractPqcTest.java
new file mode 100644
index 00000000..44a3521c
--- /dev/null
+++ b/http-pqc-j25/src/test/java/org/acme/http/pqc/AbstractPqcTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.acme.http.pqc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyStore;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManagerFactory;
+
+import io.restassured.RestAssured;
+import io.restassured.config.RestAssuredConfig;
+import io.restassured.config.SSLConfig;
+import io.restassured.specification.RequestSpecification;
+import io.smallrye.certs.Format;
+import io.smallrye.certs.junit5.Certificate;
+import io.smallrye.certs.junit5.Certificates;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Calls the server with the JDK TLS stack (SunJSSE, no Bouncy Castle), 
restricting the TLS named groups the client
+ * offers. The certificates for mutual TLS are generated before the tests 
(Bouncy Castle is used for that in test
+ * scope only).
+ * <p>
+ * Runs on JDK 25+ only, and only when it supports X25519MLKEM768 (JDK 
25.0.5+, see {@link PqcSupportedCondition}).
+ */
+@EnabledForJreRange(min = JRE.JAVA_25)
+@ExtendWith(PqcSupportedCondition.class)
+@Certificates(baseDir = "target/test-certs", certificates = @Certificate(name 
= "pqc", password = "changeit", formats = Format.PKCS12, client = true, 
subjectAlternativeNames = {
+        "DNS:localhost", "IP:127.0.0.1" }))
+abstract class AbstractPqcTest {
+
+    /** Hybrid post-quantum key exchange only. */
+    static final String[] HYBRID_GROUPS = { "X25519MLKEM768" };
+    /** Classical key exchange only, as offered by a client without 
post-quantum support. */
+    static final String[] CLASSICAL_GROUPS = { "x25519", "secp256r1" };
+
+    private static final char[] PASSWORD = "changeit".toCharArray();
+
+    /**
+     * @param expectSuccess whether the secure endpoint must answer, otherwise 
the TLS handshake must fail
+     * @param namedGroups   the TLS named groups offered by the client, or 
{@code null} for the JDK defaults
+     */
+    void assertRequest(boolean expectSuccess, String[] namedGroups) throws 
Exception {
+        if (expectSuccess) {
+            request(namedGroups, true)
+                    .get("/pqc/secure")
+                    .then()
+                    .statusCode(200)
+                    .body(containsString("Secure data delivered"));
+        } else {
+            assertThrows(SSLException.class, () -> request(namedGroups, 
true).get("/pqc/secure"));
+        }
+    }
+
+    /** Mutual TLS: a client without a certificate is rejected, even when it 
offers the hybrid group. */
+    void assertRejectedWithoutClientCertificate() {
+        // TLS 1.3 reports the missing certificate after the handshake, so the 
failure may be a plain IOException
+        // (e.g. SocketException) instead of an SSLException
+        assertThrows(IOException.class, () -> request(HYBRID_GROUPS, 
false).get("/pqc/secure"));
+    }
+
+    /**
+     * @param namedGroups       the TLS named groups offered by the client, or 
{@code null} for the JDK defaults
+     * @param clientCertificate whether the client presents its certificate
+     */
+    private static RequestSpecification request(String[] namedGroups, boolean 
clientCertificate) throws Exception {
+        SSLSocketFactory socketFactory = new 
SSLSocketFactory(createSslContext(clientCertificate),
+                SSLSocketFactory.STRICT_HOSTNAME_VERIFIER) {
+            @Override
+            protected void prepareSocket(SSLSocket socket) {
+                SSLParameters parameters = socket.getSSLParameters();
+                parameters.setProtocols(new String[] { "TLSv1.3" });
+                if (namedGroups != null) {
+                    parameters.setNamedGroups(namedGroups);
+                }
+                socket.setSSLParameters(parameters);
+            }
+        };
+        return given()
+                
.config(RestAssuredConfig.config().sslConfig(SSLConfig.sslConfig().sslSocketFactory(socketFactory)))
+                .baseUri("https://localhost:"; + RestAssured.port);
+    }
+
+    private static SSLContext createSslContext(boolean clientCertificate) 
throws Exception {
+        KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init(loadKeyStore("target/test-certs/pqc-client-keystore.p12"), 
PASSWORD);
+
+        TrustManagerFactory tmf = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(loadKeyStore("target/test-certs/pqc-client-truststore.p12"));
+
+        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
+        sslContext.init(clientCertificate ? kmf.getKeyManagers() : null, 
tmf.getTrustManagers(), null);
+        return sslContext;
+    }
+
+    private static KeyStore loadKeyStore(String path) throws Exception {
+        KeyStore keyStore = KeyStore.getInstance("PKCS12");
+        try (InputStream in = Files.newInputStream(Path.of(path))) {
+            keyStore.load(in, PASSWORD);
+        }
+        return keyStore;
+    }
+}
diff --git a/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcOnlyTest.java 
b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcOnlyTest.java
new file mode 100644
index 00000000..91a095b7
--- /dev/null
+++ b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcOnlyTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.acme.http.pqc;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import org.acme.http.pqc.profiles.PqcOnlyProfile;
+import org.junit.jupiter.api.Test;
+
+/**
+ * PQC-only server ({@code strict} policy): only clients offering 
X25519MLKEM768 can connect.
+ *
+ * No native *IT counterpart: a native executable is built with one 
configuration only (the default, with fallback).
+ */
+@QuarkusTest
+@TestProfile(PqcOnlyProfile.class)
+class PqcOnlyTest extends AbstractPqcTest {
+
+    @Test
+    void hybridClientIsAccepted() throws Exception {
+        assertRequest(true, HYBRID_GROUPS);
+    }
+
+    @Test
+    void jdkDefaultClientIsAccepted() throws Exception {
+        // JDK 25.0.5+ offers X25519MLKEM768 by default
+        assertRequest(true, null);
+    }
+
+    @Test
+    void classicalClientIsRejected() throws Exception {
+        assertRequest(false, CLASSICAL_GROUPS);
+    }
+
+    @Test
+    void clientWithoutCertificateIsRejected() {
+        assertRejectedWithoutClientCertificate();
+    }
+}
diff --git 
a/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcSupportedCondition.java 
b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcSupportedCondition.java
new file mode 100644
index 00000000..3a9edeb2
--- /dev/null
+++ b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcSupportedCondition.java
@@ -0,0 +1,52 @@
+/*
+ * 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.acme.http.pqc;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLContext;
+
+import org.junit.jupiter.api.extension.ConditionEvaluationResult;
+import org.junit.jupiter.api.extension.ExecutionCondition;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+/**
+ * Skips the tests when the running JDK's TLS stack (SunJSSE) does not support 
X25519MLKEM768 (JEP 527), e.g. JDK 25
+ * before 25.0.5. The application would fail at startup there.
+ */
+class PqcSupportedCondition implements ExecutionCondition {
+
+    private static final String HYBRID_GROUP = "X25519MLKEM768";
+
+    @Override
+    public ConditionEvaluationResult 
evaluateExecutionCondition(ExtensionContext context) {
+        return isSupported()
+                ? ConditionEvaluationResult.enabled(HYBRID_GROUP + " is 
supported by the JDK")
+                : ConditionEvaluationResult.disabled(HYBRID_GROUP + " is not 
supported by JDK "
+                        + Runtime.version() + ", JDK 25.0.5+ is required");
+    }
+
+    static boolean isSupported() {
+        try {
+            String[] groups = 
SSLContext.getDefault().getDefaultSSLParameters().getNamedGroups();
+            return groups != null && 
Arrays.asList(groups).contains(HYBRID_GROUP);
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+}
diff --git 
a/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcWithFallbackIT.java 
b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcWithFallbackIT.java
new file mode 100644
index 00000000..71614d3a
--- /dev/null
+++ b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcWithFallbackIT.java
@@ -0,0 +1,26 @@
+/*
+ * 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.acme.http.pqc;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+/**
+ * Runs PqcWithFallbackTest against the packaged application (native mode: 
{@code mvn verify -Dnative}).
+ */
+@QuarkusIntegrationTest
+class PqcWithFallbackIT extends PqcWithFallbackTest {
+}
diff --git 
a/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcWithFallbackTest.java 
b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcWithFallbackTest.java
new file mode 100644
index 00000000..c2056432
--- /dev/null
+++ b/http-pqc-j25/src/test/java/org/acme/http/pqc/PqcWithFallbackTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.acme.http.pqc;
+
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Default configuration ({@code client-negotiated} policy): hybrid key 
exchange is accepted, classical clients can
+ * still connect.
+ */
+@QuarkusTest
+class PqcWithFallbackTest extends AbstractPqcTest {
+
+    @Test
+    void hybridClientIsAccepted() throws Exception {
+        assertRequest(true, HYBRID_GROUPS);
+    }
+
+    @Test
+    void classicalClientIsAccepted() throws Exception {
+        assertRequest(true, CLASSICAL_GROUPS);
+    }
+
+    @Test
+    void clientWithoutCertificateIsRejected() {
+        assertRejectedWithoutClientCertificate();
+    }
+}
diff --git 
a/http-pqc-j25/src/test/java/org/acme/http/pqc/profiles/PqcOnlyProfile.java 
b/http-pqc-j25/src/test/java/org/acme/http/pqc/profiles/PqcOnlyProfile.java
new file mode 100644
index 00000000..0c8e0055
--- /dev/null
+++ b/http-pqc-j25/src/test/java/org/acme/http/pqc/profiles/PqcOnlyProfile.java
@@ -0,0 +1,34 @@
+/*
+ * 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.acme.http.pqc.profiles;
+
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTestProfile;
+
+/**
+ * PQC-only server: the policy is switched to {@code strict}. Vert.x then 
replaces the configured key exchange groups
+ * with the hybrid PQC groups (X25519MLKEM768, SecP256r1MLKEM768, 
SecP384r1MLKEM1024), so classical-only clients are
+ * rejected.
+ */
+public class PqcOnlyProfile implements QuarkusTestProfile {
+
+    @Override
+    public Map<String, String> getConfigOverrides() {
+        return Map.of("quarkus.tls.pqc-enforcement-policy", "strict");
+    }
+}
diff --git a/pom.xml b/pom.xml
index bb992294..f3c48eea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
         <module>file-split-log-xml</module>
         <module>http-log</module>
         <module>http-pqc</module>
+        <module>http-pqc-j25</module>
         <module>jdbc-datasource</module>
         <module>jms-jpa</module>
         <module>jpa-idempotent-repository</module>

Reply via email to