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

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

commit d3400edfa59577a068486ef78d893ddf1d94dc91
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Oct 14 10:23:46 2025 +0200

    PQC Examples: Moving examples from Java to YAML with Groovy Beans
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 pqc-document-signing/PQCDocumentSigningRoutes.java | 316 ------------------
 pqc-document-signing/README.adoc                   | 103 ++++--
 pqc-document-signing/application.properties        |   6 +-
 pqc-document-signing/pqc-document-signing.yaml     | 353 +++++++++++++++++++++
 4 files changed, 424 insertions(+), 354 deletions(-)

diff --git a/pqc-document-signing/PQCDocumentSigningRoutes.java 
b/pqc-document-signing/PQCDocumentSigningRoutes.java
deleted file mode 100644
index 94f0f70..0000000
--- a/pqc-document-signing/PQCDocumentSigningRoutes.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * 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.example.pqc;
-
-import java.security.KeyPair;
-import java.security.Security;
-import java.util.Base64;
-
-import org.apache.camel.builder.RouteBuilder;
-import 
org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
-import org.springframework.vault.authentication.TokenAuthentication;
-import org.springframework.vault.client.VaultEndpoint;
-import org.springframework.vault.core.VaultTemplate;
-
-/**
- * RouteBuilder for PQC Document Signing with HashiCorp Vault integration.
- *
- * This class defines 12 routes:
- * 1. Initialize PQC signing key on startup
- * 2. REST API - Sign a document
- * 3. REST API - Verify a document signature
- * 4. REST API - Get key metadata
- * 5. REST API - List all keys
- * 6. REST API - Rotate signing key
- * 7. Scheduled job - Check key rotation needs
- * 8. Helper - Update key usage metadata
- * 9. Helper - Get key metadata
- * 10. Helper - Check key expiration and auto-rotate
- * 11. Helper - Automatic key rotation on expiration
- */
-public class PQCDocumentSigningRoutes extends RouteBuilder {
-
-    @Override
-    public void configure() throws Exception {
-
-        // Register BouncyCastle providers for PQC algorithms
-        if (Security.getProvider("BC") == null) {
-            Security.addProvider(new BouncyCastleProvider());
-        }
-        if (Security.getProvider("BCPQC") == null) {
-            Security.addProvider(new BouncyCastlePQCProvider());
-        }
-
-        // Register beans for HashiCorp Vault integration
-        registerVaultBeans();
-
-        // Route 1: Initialize PQC signing key on startup
-        from("timer:init?repeatCount=1")
-            .routeId("initialize-signing-key")
-            .log("Initializing PQC signing key in Vault...")
-            .bean("keyLifecycleManager", "generateKeyPair('DILITHIUM', 
'document-signing-key')")
-            .log("PQC signing key initialized successfully")
-            .process(exchange -> {
-                // Register the KeyPair bean directly for autowiring
-                HashicorpVaultKeyLifecycleManager keyManager = 
exchange.getContext().getRegistry()
-                    .lookupByNameAndType("keyLifecycleManager", 
HashicorpVaultKeyLifecycleManager.class);
-                KeyPair keyPair = keyManager.getKey("document-signing-key");
-
-                // Register as KeyPair type so PQC component can autowire it
-                bindToRegistry("signingKey", keyPair);
-            })
-            .to("direct:get-key-metadata");
-
-        // Route 2: REST API - Sign a document
-        from("platform-http:/api/sign")
-            .routeId("sign-document-api")
-            .log("Received document signing request: ${body}")
-            .setHeader("originalBody", simple("${body}"))
-            // Sign the document using PQC
-            
.toD("pqc:sign?operation=sign&signatureAlgorithm=DILITHIUM&keyPair=#signingKey")
-            // Convert binary signature to base64
-            .process(exchange -> {
-                byte[] signature = 
exchange.getMessage().getHeader("CamelPQCSignature", byte[].class);
-                if (signature != null) {
-                    String base64Signature = 
Base64.getEncoder().encodeToString(signature);
-                    exchange.getMessage().setHeader("CamelPQCSignature", 
base64Signature);
-                }
-            })
-            .log("Document signed with quantum-resistant signature")
-            // Update key usage metadata
-            .to("direct:update-key-usage")
-            // Check if key needs rotation or has expired
-            .to("direct:check-key-expiration")
-            // Prepare response with signature and key metadata
-            .setBody(simple("{\n" +
-                "  \"status\": \"signed\",\n" +
-                "  \"document\": \"${header.originalBody}\",\n" +
-                "  \"signature\": \"${header.CamelPQCSignature}\",\n" +
-                "  \"signatureAlgorithm\": \"DILITHIUM\",\n" +
-                "  \"keyId\": \"document-signing-key\",\n" +
-                "  \"keyMetadata\": ${body}\n" +
-                "}"))
-            .setHeader("Content-Type", constant("application/json"))
-            .log("Response: ${body}");
-
-        // Route 3: REST API - Verify a document signature
-        from("platform-http:/api/verify")
-            .routeId("verify-document-api")
-            .log("Received document verification request: ${body}")
-            // Get signature from X-Signature header (workaround for header 
filtering)
-            .process(exchange -> {
-                String base64Signature = 
exchange.getIn().getHeader("X-Signature", String.class);
-                if (base64Signature == null || base64Signature.isEmpty()) {
-                    throw new IllegalArgumentException("X-Signature header is 
missing or empty");
-                }
-                byte[] signature = Base64.getDecoder().decode(base64Signature);
-                exchange.getIn().setHeader("CamelPQCSignature", signature);
-                exchange.getIn().setHeader("signatureLength", 
signature.length);
-            })
-            .log("Verifying signature of length: ${header.signatureLength} 
bytes")
-            // Verify the document signature using PQC
-            .doTry()
-                
.toD("pqc:verify?operation=verify&signatureAlgorithm=DILITHIUM&keyPair=#signingKey")
-                .log("Verification completed. Result: 
${header.CamelPQCVerify}")
-            .doCatch(Exception.class)
-                .log("ERROR during verification: ${exception.message}")
-                .setHeader("CamelPQCVerify", constant(false))
-            .end()
-            .choice()
-                .when(simple("${header.CamelPQCVerify} == true"))
-                    .setBody(simple("{\n" +
-                        "  \"status\": \"verified\",\n" +
-                        "  \"valid\": true,\n" +
-                        "  \"message\": \"Document signature is valid\",\n" +
-                        "  \"signatureAlgorithm\": \"DILITHIUM\"\n" +
-                        "}"))
-                .when(simple("${header.CamelPQCVerify} == false"))
-                    .setBody(simple("{\n" +
-                        "  \"status\": \"verified\",\n" +
-                        "  \"valid\": false,\n" +
-                        "  \"message\": \"Document signature is invalid\",\n" +
-                        "  \"signatureAlgorithm\": \"DILITHIUM\"\n" +
-                        "}"))
-            .end()
-            .setHeader("Content-Type", constant("application/json"))
-            .log("Verification result: ${body}");
-
-        // Route 4: REST API - Get key metadata
-        from("platform-http:/api/key/metadata")
-            .routeId("get-key-metadata-api")
-            .log("Fetching key metadata...")
-            .to("direct:get-key-metadata");
-
-        // Route 5: REST API - List all keys
-        from("platform-http:/api/keys")
-            .routeId("list-keys-api")
-            .log("Listing all PQC keys...")
-            .bean("keyLifecycleManager", "listKeys")
-            .setBody(simple("{\n" +
-                "  \"keys\": ${body}\n" +
-                "}"))
-            .setHeader("Content-Type", constant("application/json"))
-            .log("Keys listed: ${body}");
-
-        // Route 6: REST API - Rotate signing key
-        from("platform-http:/api/key/rotate")
-            .routeId("rotate-key-api")
-            .log("Rotating signing key...")
-            .bean("keyLifecycleManager", "rotateKey('document-signing-key', 
'document-signing-key-v2', 'DILITHIUM')")
-            .log("Key rotated successfully: old key deprecated, new key 
active")
-            .setBody(simple("{\n" +
-                "  \"status\": \"rotated\",\n" +
-                "  \"oldKey\": \"document-signing-key\",\n" +
-                "  \"newKey\": \"document-signing-key-v2\",\n" +
-                "  \"message\": \"Key rotation completed successfully\"\n" +
-                "}"))
-            .setHeader("Content-Type", constant("application/json"));
-
-        // Route 7: Scheduled job - Check key rotation needs
-        from("timer:checkRotation?period={{key.rotation.check.period}}")
-            .routeId("check-rotation-schedule")
-            .log("Checking if key needs rotation...")
-            .bean("keyLifecycleManager", 
"needsRotation('document-signing-key', 'P{{key.max.age.days}}D', 
{{key.max.usage.count}})")
-            .choice()
-                .when(simple("${body} == true"))
-                    .log("WARNING: Key 'document-signing-key' needs rotation!")
-                    .to("direct:get-key-metadata")
-                    .log("Current key metadata: ${body}")
-                .otherwise()
-                    .log("Key rotation not needed yet")
-            .end();
-
-        // Route 8: Helper - Update key usage metadata
-        from("direct:update-key-usage")
-            .routeId("update-key-usage")
-            .bean("keyLifecycleManager", 
"getKeyMetadata('document-signing-key')")
-            .setProperty("metadata", simple("${body}"))
-            .process(exchange -> {
-                // Update last used timestamp and increment usage count
-                Object metadata = exchange.getProperty("metadata");
-                if (metadata != null) {
-                    // Call updateLastUsed on the metadata object
-                    
metadata.getClass().getMethod("updateLastUsed").invoke(metadata);
-                    exchange.getMessage().setBody(metadata);
-                }
-            })
-            .bean("keyLifecycleManager", 
"updateKeyMetadata('document-signing-key', ${body})")
-            .log("Key usage updated. Usage count: ${body.usageCount}");
-
-        // Route 9: Helper - Get key metadata
-        from("direct:get-key-metadata")
-            .routeId("get-key-metadata-helper")
-            .bean("keyLifecycleManager", 
"getKeyMetadata('document-signing-key')")
-            .setBody(simple("{\n" +
-                "  \"keyId\": \"${body.keyId}\",\n" +
-                "  \"algorithm\": \"${body.algorithm}\",\n" +
-                "  \"status\": \"${body.status}\",\n" +
-                "  \"createdAt\": \"${body.createdAt}\",\n" +
-                "  \"lastUsedAt\": \"${body.lastUsedAt}\",\n" +
-                "  \"usageCount\": ${body.usageCount},\n" +
-                "  \"ageInDays\": ${body.ageInDays},\n" +
-                "  \"expiresAt\": \"${body.expiresAt}\",\n" +
-                "  \"nextRotationAt\": \"${body.nextRotationAt}\",\n" +
-                "  \"expired\": ${body.expired},\n" +
-                "  \"needsRotation\": ${body.needsRotation}\n" +
-                "}"))
-            .setHeader("Content-Type", constant("application/json"))
-            .log("Key metadata: ${body}");
-
-        // Route 10: Helper - Check key expiration and auto-rotate
-        from("direct:check-key-expiration")
-            .routeId("check-key-expiration")
-            .bean("keyLifecycleManager", 
"getKeyMetadata('document-signing-key')")
-            .choice()
-                .when(simple("${body.usageCount} >= {{key.max.usage.count}}"))
-                    .log("WARNING: Key has reached maximum usage count 
(${body.usageCount} >= {{key.max.usage.count}})")
-                    .setProperty("expirationReason", simple("usage count 
(${body.usageCount} uses)"))
-                    .to("direct:auto-rotate-key")
-                .when(simple("${body.ageInDays} >= {{key.max.age.days}}"))
-                    .log("WARNING: Key has reached maximum age 
(${body.ageInDays} >= {{key.max.age.days}} days)")
-                    .setProperty("expirationReason", simple("age 
(${body.ageInDays} days)"))
-                    .to("direct:auto-rotate-key")
-                .otherwise()
-                    .setBody(simple("Key is valid (usage: 
${body.usageCount}/{{key.max.usage.count}}, age: 
${body.ageInDays}/{{key.max.age.days}} days)"))
-            .end();
-
-        // Route 11: Helper - Automatic key rotation on expiration
-        from("direct:auto-rotate-key")
-            .routeId("auto-rotate-key")
-            .log(">>> AUTOMATIC KEY ROTATION TRIGGERED <<<")
-            .log("Reason: Key expired due to 
${exchangeProperty.expirationReason}")
-            // Generate new key ID with timestamp
-            .setProperty("timestamp", simple("${date:now:yyyyMMdd-HHmmss}"))
-            .setProperty("newKeyId", simple("document-signing-key"))
-            .log("Rotating key from 'document-signing-key' to 
'${exchangeProperty.newKeyId}'")
-            // Perform rotation
-            .bean("keyLifecycleManager", "rotateKey('document-signing-key', 
'${exchangeProperty.newKeyId}', 'DILITHIUM')")
-            .log(">>> KEY ROTATION COMPLETED <<<")
-            .log("Old key 'document-signing-key' status: DEPRECATED")
-            .log("New key '${exchangeProperty.newKeyId}' status: ACTIVE")
-            // Update the active key reference for future operations
-            .setProperty("activeKeyId", simple("${exchangeProperty.newKeyId}"))
-            // Get metadata of new key
-            .bean("keyLifecycleManager", 
"getKeyMetadata('${exchangeProperty.newKeyId}')")
-            .setBody(simple("{\n" +
-                "  \"rotationTriggered\": true,\n" +
-                "  \"reason\": \"${exchangeProperty.expirationReason}\",\n" +
-                "  \"oldKey\": \"document-signing-key\",\n" +
-                "  \"oldKeyStatus\": \"DEPRECATED\",\n" +
-                "  \"newKey\": \"${exchangeProperty.newKeyId}\",\n" +
-                "  \"newKeyStatus\": \"ACTIVE\",\n" +
-                "  \"newKeyCreatedAt\": \"${body.createdAt}\",\n" +
-                "  \"message\": \"Key automatically rotated due to 
${exchangeProperty.expirationReason}\"\n" +
-                "}"))
-            .log("Rotation details: ${body}");
-    }
-
-    /**
-     * Register beans for HashiCorp Vault integration
-     */
-    private void registerVaultBeans() throws Exception {
-        // Get configuration properties
-        String vaultHost = 
getContext().resolvePropertyPlaceholders("{{vault.host}}");
-        int vaultPort = 
Integer.parseInt(getContext().resolvePropertyPlaceholders("{{vault.port}}"));
-        String vaultScheme = 
getContext().resolvePropertyPlaceholders("{{vault.scheme}}");
-        String vaultToken = 
getContext().resolvePropertyPlaceholders("{{vault.token}}");
-        String secretsEngine = 
getContext().resolvePropertyPlaceholders("{{vault.secrets.engine}}");
-        String keyPrefix = 
getContext().resolvePropertyPlaceholders("{{vault.keys.prefix}}");
-
-        // Create VaultEndpoint
-        VaultEndpoint vaultEndpoint = new VaultEndpoint();
-        vaultEndpoint.setHost(vaultHost);
-        vaultEndpoint.setPort(vaultPort);
-        vaultEndpoint.setScheme(vaultScheme);
-        bindToRegistry("vaultEndpoint", vaultEndpoint);
-
-        // Create TokenAuthentication
-        TokenAuthentication tokenAuthentication = new 
TokenAuthentication(vaultToken);
-        bindToRegistry("tokenAuthentication", tokenAuthentication);
-
-        // Create VaultTemplate
-        VaultTemplate vaultTemplate = new VaultTemplate(vaultEndpoint, 
tokenAuthentication);
-        bindToRegistry("vaultTemplate", vaultTemplate);
-
-        // Create HashicorpVaultKeyLifecycleManager using constructor with all 
parameters
-        HashicorpVaultKeyLifecycleManager keyLifecycleManager =
-            new HashicorpVaultKeyLifecycleManager(vaultHost, vaultPort, 
vaultScheme, vaultToken, secretsEngine, keyPrefix);
-        bindToRegistry("keyLifecycleManager", keyLifecycleManager);
-    }
-}
diff --git a/pqc-document-signing/README.adoc b/pqc-document-signing/README.adoc
index 09b313e..5774afd 100644
--- a/pqc-document-signing/README.adoc
+++ b/pqc-document-signing/README.adoc
@@ -48,7 +48,7 @@ The example consists of the following files:
 [source,text]
 ----
 pqc-document-signing/
-├── PQCDocumentSigningRoutes.java  # Main RouteBuilder with 12 routes
+├── pqc-document-signing.yaml      # YAML configuration (beans + routes)
 ├── application.properties          # Configuration file
 └── README.adoc                    # This file
 ----
@@ -59,23 +59,17 @@ After Vault is configured and running, start the Camel 
application:
 
 [source,sh]
 ----
-$ jbang -Dcamel.jbang.version=4.16.0-SNAPSHOT camel@apache/camel run 
PQCDocumentSigningRoutes.java
-----
-
-Or using Maven:
-
-[source,sh]
-----
-$ mvn compile exec:java
+$ jbang -Dcamel.jbang.version=4.16.0-SNAPSHOT camel@apache/camel run 
pqc-document-signing.yaml
 ----
 
 The application will:
 
-1. Connect to HashiCorp Vault
-2. Generate a DILITHIUM Post-Quantum Cryptographic key pair
-3. Store the key securely in Vault at path: 
`secret/data/pqc/keys/document-signing-key`
-4. Start REST API endpoints on port 8080
-5. Begin monitoring key lifecycle (usage count, age, rotation needs)
+1. Register BouncyCastle security providers (BC, BCPQC)
+2. Connect to HashiCorp Vault
+3. Generate a DILITHIUM Post-Quantum Cryptographic key pair
+4. Store the key securely in Vault at path: 
`secret/data/pqc/keys/document-signing-key`
+5. Start REST API endpoints on port 8080
+6. Begin monitoring key lifecycle (usage count, age, rotation needs)
 
 == API Endpoints
 
@@ -301,32 +295,71 @@ $ docker stop vault
 $ docker rm vault
 ----
 
-== Architecture
+== Implementation Details
+
+The `pqc-document-signing.yaml` file contains:
+
+=== Bean Configuration
+
+**1. Security Initialization Bean** - Registers BouncyCastle providers at 
startup:
+
+[source,yaml]
+----
+- beans:
+  - name: initSecurity
+    type: java.lang.Object
+    scriptLanguage: groovy
+    script: |
+      // Register BouncyCastle providers
+      if (java.security.Security.getProvider("BC") == null) {
+          java.security.Security.addProvider(new 
org.bouncycastle.jce.provider.BouncyCastleProvider())
+      }
+      if (java.security.Security.getProvider("BCPQC") == null) {
+          java.security.Security.addProvider(new 
org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider())
+      }
+      return new Object()
+----
+
+**2. HashicorpVaultKeyLifecycleManager** - Created using Groovy script with 
6-parameter constructor:
+
+[source,yaml]
+----
+  - name: keyLifecycleManager
+    type: 
org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager
+    scriptLanguage: groovy
+    script: |
+      new 
org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager(
+          '{{vault.host}}',           // Vault server hostname
+          {{vault.port}},             // Vault server port
+          '{{vault.scheme}}',         // Connection scheme (http/https)
+          '{{vault.token}}',          // Vault authentication token
+          '{{vault.secrets.engine}}', // KV secrets engine name
+          '{{vault.keys.prefix}}'     // Prefix for key paths in Vault
+      )
+----
 
-This example demonstrates:
+=== Route Definitions
 
-1. **Post-Quantum Cryptography (PQC)** - Quantum-resistant digital signatures 
using DILITHIUM
-2. **HashiCorp Vault Integration** - Enterprise secret management for PQC keys
-3. **Key Lifecycle Management** - Automated tracking of key usage, rotation, 
and expiration
-4. **RESTful API** - Multiple endpoints for signing, verification, and key 
management
-5. **Platform HTTP Component** - Lightweight HTTP server for REST APIs
-6. **Java-based RouteBuilder** - Routes defined using Camel Java DSL
-7. **Bean Configuration** - Vault and key manager beans configured 
programmatically in Java
-8. **Scheduled Jobs** - Periodic key rotation checks
-9. **Dynamic Routing** - Uses `.toD()` for runtime endpoint creation with 
autowired KeyPair
-10. **Base64 Encoding** - Binary signatures converted to base64 for JSON 
transport
+The YAML file defines 12 routes using Camel YAML DSL:
 
-=== Implementation Details
+1. **initialize-signing-key** - Generates DILITHIUM key pair and registers it 
as a bean
+2. **sign-document-api** - POST `/api/sign` - Signs documents with PQC 
signature
+3. **verify-document-api** - POST `/api/verify` - Verifies document signatures 
(uses `X-Signature` header)
+4. **get-key-metadata-api** - GET `/api/key/metadata` - Returns key metadata
+5. **list-keys-api** - GET `/api/keys` - Lists all PQC keys in Vault
+6. **rotate-key-api** - POST `/api/key/rotate` - Manual key rotation
+7. **check-rotation-schedule** - Timer-based rotation checks
+8. **update-key-usage** - Helper route for updating key usage metadata
+9. **get-key-metadata-helper** - Helper route for retrieving key metadata
+10. **check-key-expiration** - Helper route checking if key expired
+11. **auto-rotate-key** - Automatic key rotation when key expires
 
-The `PQCDocumentSigningRoutes.java` RouteBuilder:
+=== Key Features
 
-- Registers BouncyCastle security providers (BC, BCPQC)
-- Creates and binds Vault-related beans to the registry
-- Defines 11 Camel routes using Java DSL
-- Handles KeyPair registration after key generation
-- Converts binary signatures to/from base64 for JSON transport
-- Uses dynamic routing (`.toD()`) to create PQC endpoints at runtime
-- Implements automatic key rotation based on usage count and age
+- **Declarative Configuration** - All beans and routes defined in YAML
+- **Groovy Scripts** - Used for bean instantiation and signature conversion
+- **Property Placeholders** - All configuration values from 
`application.properties`
+- **No Compilation Required** - Routes can be modified without recompilation
 
 == Security Notice
 
diff --git a/pqc-document-signing/application.properties 
b/pqc-document-signing/application.properties
index 74b80cc..4bbc048 100644
--- a/pqc-document-signing/application.properties
+++ b/pqc-document-signing/application.properties
@@ -19,12 +19,12 @@
 camel.main.name = PQCDocumentSigningExample
 
 # HashiCorp Vault Configuration
-# Port 8200 is used when running Vault via: camel infra run hashicorp-vault
+# Port 8200 is used when running Vault via: camel infra run hashicorp vault
 # Port 8201 is used when running Vault manually via Docker (see README.adoc)
 vault.host=localhost
-vault.port=8201
+vault.port=8200
 vault.scheme=http
-vault.token=myroot
+vault.token=myToken
 vault.secrets.engine=secret
 vault.keys.prefix=pqc/keys
 
diff --git a/pqc-document-signing/pqc-document-signing.yaml 
b/pqc-document-signing/pqc-document-signing.yaml
new file mode 100644
index 0000000..c47813c
--- /dev/null
+++ b/pqc-document-signing/pqc-document-signing.yaml
@@ -0,0 +1,353 @@
+# 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.
+
+# Post-Quantum Cryptography Document Signing with HashiCorp Vault
+# Single YAML file containing beans and routes
+
+# Bean Definitions
+- beans:
+  # Register BouncyCastle providers at startup using Groovy
+  - name: initSecurity
+    type: java.lang.Object
+    scriptLanguage: groovy
+    script: |
+      // Register BouncyCastle providers
+      if (java.security.Security.getProvider("BC") == null) {
+          java.security.Security.addProvider(new 
org.bouncycastle.jce.provider.BouncyCastleProvider())
+      }
+      if (java.security.Security.getProvider("BCPQC") == null) {
+          java.security.Security.addProvider(new 
org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider())
+      }
+      return new Object()
+
+  # HashicorpVaultKeyLifecycleManager using Groovy script
+  - name: keyLifecycleManager
+    type: 
org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager
+    scriptLanguage: groovy
+    script: |
+      new 
org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager(
+          '{{vault.host}}',
+          {{vault.port}},
+          '{{vault.scheme}}',
+          '{{vault.token}}',
+          '{{vault.secrets.engine}}',
+          '{{vault.keys.prefix}}'
+      )
+
+# Route Definitions
+- route:
+    id: initialize-signing-key
+    from:
+      uri: timer:init
+      parameters:
+        repeatCount: 1
+      steps:
+        - log: "Initializing PQC signing key in Vault..."
+        - bean:
+            ref: keyLifecycleManager
+            method: "generateKeyPair('DILITHIUM', 'document-signing-key')"
+        - log: "PQC signing key initialized successfully"
+        - script:
+            groovy: |
+              // Register the KeyPair bean
+              def keyManager = 
camelContext.registry.lookupByNameAndType('keyLifecycleManager',
+                  
org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager.class)
+              def keyPair = keyManager.getKey('document-signing-key')
+              camelContext.registry.bind('signingKey', keyPair)
+        - to: direct:get-key-metadata
+
+- route:
+    id: sign-document-api
+    from:
+      uri: platform-http:/api/sign
+      steps:
+        - log: "Received document signing request: ${body}"
+        - setHeader:
+            name: originalBody
+            simple: "${body}"
+        - toD: 
"pqc:sign?operation=sign&signatureAlgorithm=DILITHIUM&keyPair=#signingKey"
+        - script:
+            groovy: |
+              // Convert binary signature to base64
+              def signature = exchange.message.getHeader('CamelPQCSignature', 
byte[].class)
+              if (signature != null) {
+                  def base64Signature = 
java.util.Base64.encoder.encodeToString(signature)
+                  exchange.message.setHeader('CamelPQCSignature', 
base64Signature)
+              }
+        - log: "Document signed with quantum-resistant signature"
+        - to: direct:update-key-usage
+        - to: direct:check-key-expiration
+        - setBody:
+            simple: |
+              {
+                "status": "signed",
+                "document": "${header.originalBody}",
+                "signature": "${header.CamelPQCSignature}",
+                "signatureAlgorithm": "DILITHIUM",
+                "keyId": "document-signing-key",
+                "keyMetadata": ${body}
+              }
+        - setHeader:
+            name: Content-Type
+            constant: "application/json"
+        - log: "Response: ${body}"
+
+- route:
+    id: verify-document-api
+    from:
+      uri: platform-http:/api/verify
+      steps:
+        - log: "Received document verification request: ${body}"
+        - script:
+            groovy: |
+              // Get signature from X-Signature header
+              def base64Signature = exchange.getIn().getHeader('X-Signature', 
String.class)
+              if (base64Signature == null || base64Signature.isEmpty()) {
+                  throw new IllegalArgumentException('X-Signature header is 
missing or empty')
+              }
+              def signature = java.util.Base64.decoder.decode(base64Signature)
+              exchange.getIn().setHeader('CamelPQCSignature', signature)
+              exchange.getIn().setHeader('signatureLength', signature.length)
+        - log: "Verifying signature of length: ${header.signatureLength} bytes"
+        - doTry:
+            steps:
+              - toD: 
"pqc:verify?operation=verify&signatureAlgorithm=DILITHIUM&keyPair=#signingKey"
+              - log: "Verification completed. Result: 
${header.CamelPQCVerification}"
+            doCatch:
+              - exception:
+                  - "java.lang.Exception"
+                steps:
+                  - log: "ERROR during verification: ${exception.message}"
+                  - setHeader:
+                      name: CamelPQCVerification
+                      constant: false
+        - choice:
+            when:
+              - simple: "${header.CamelPQCVerification} == true"
+                steps:
+                  - setBody:
+                      simple: |
+                        {
+                          "status": "verified",
+                          "valid": true,
+                          "message": "Document signature is valid",
+                          "signatureAlgorithm": "DILITHIUM"
+                        }
+              - simple: "${header.CamelPQCVerification} == false"
+                steps:
+                  - setBody:
+                      simple: |
+                        {
+                          "status": "verified",
+                          "valid": false,
+                          "message": "Document signature is invalid",
+                          "signatureAlgorithm": "DILITHIUM"
+                        }
+        - setHeader:
+            name: Content-Type
+            constant: "application/json"
+        - log: "Verification result: ${body}"
+
+- route:
+    id: get-key-metadata-api
+    from:
+      uri: platform-http:/api/key/metadata
+      steps:
+        - log: "Fetching key metadata..."
+        - to: direct:get-key-metadata
+
+- route:
+    id: list-keys-api
+    from:
+      uri: platform-http:/api/keys
+      steps:
+        - log: "Listing all PQC keys..."
+        - bean:
+            ref: keyLifecycleManager
+            method: "listKeys"
+        - setBody:
+            simple: |
+              {
+                "keys": ${body}
+              }
+        - setHeader:
+            name: Content-Type
+            constant: "application/json"
+        - log: "Keys listed: ${body}"
+
+- route:
+    id: rotate-key-api
+    from:
+      uri: platform-http:/api/key/rotate
+      steps:
+        - log: "Rotating signing key..."
+        - bean:
+            ref: keyLifecycleManager
+            method: "rotateKey('document-signing-key', 'document-signing-key', 
'DILITHIUM')"
+        - log: "Key rotated successfully: old key deprecated, new key active"
+        - setBody:
+            simple: |
+              {
+                "status": "rotated",
+                "oldKey": "document-signing-key",
+                "newKey": "document-signing-key",
+                "message": "Key rotation completed successfully"
+              }
+        - setHeader:
+            name: Content-Type
+            constant: "application/json"
+
+- route:
+    id: check-rotation-schedule
+    from:
+      uri: timer:checkRotation
+      parameters:
+        period: "{{key.rotation.check.period}}"
+      steps:
+        - log: "Checking if key needs rotation..."
+        - bean:
+            ref: keyLifecycleManager
+            method: "needsRotation('document-signing-key', 
'P{{key.max.age.days}}D', {{key.max.usage.count}})"
+        - choice:
+            when:
+              - simple: "${body} == true"
+                steps:
+                  - log: "WARNING: Key 'document-signing-key' needs rotation!"
+                  - to: direct:get-key-metadata
+                  - log: "Current key metadata: ${body}"
+            otherwise:
+              steps:
+                - log: "Key rotation not needed yet"
+
+- route:
+    id: update-key-usage
+    from:
+      uri: direct:update-key-usage
+      steps:
+        - bean:
+            ref: keyLifecycleManager
+            method: "getKeyMetadata('document-signing-key')"
+        - setProperty:
+            name: metadata
+            simple: "${body}"
+        - script:
+            groovy: |
+              // Update last used timestamp and increment usage count
+              def metadata = exchange.getProperty('metadata')
+              if (metadata != null) {
+                  
metadata.getClass().getMethod('updateLastUsed').invoke(metadata)
+                  exchange.message.setBody(metadata)
+              }
+        - bean:
+            ref: keyLifecycleManager
+            method: "updateKeyMetadata('document-signing-key', ${body})"
+        - log: "Key usage updated. Usage count: ${body.usageCount}"
+
+- route:
+    id: get-key-metadata-helper
+    from:
+      uri: direct:get-key-metadata
+      steps:
+        - bean:
+            ref: keyLifecycleManager
+            method: "getKeyMetadata('document-signing-key')"
+        - setBody:
+            simple: |
+              {
+                "keyId": "${body.keyId}",
+                "algorithm": "${body.algorithm}",
+                "status": "${body.status}",
+                "createdAt": "${body.createdAt}",
+                "lastUsedAt": "${body.lastUsedAt}",
+                "usageCount": ${body.usageCount},
+                "ageInDays": ${body.ageInDays},
+                "expiresAt": "${body.expiresAt}",
+                "nextRotationAt": "${body.nextRotationAt}",
+                "expired": ${body.expired},
+                "needsRotation": ${body.needsRotation}
+              }
+        - setHeader:
+            name: Content-Type
+            constant: "application/json"
+        - log: "Key metadata: ${body}"
+
+- route:
+    id: check-key-expiration
+    from:
+      uri: direct:check-key-expiration
+      steps:
+        - bean:
+            ref: keyLifecycleManager
+            method: "getKeyMetadata('document-signing-key')"
+        - choice:
+            when:
+              - simple: "${body.usageCount} >= {{key.max.usage.count}}"
+                steps:
+                  - log: "WARNING: Key has reached maximum usage count 
(${body.usageCount} >= {{key.max.usage.count}})"
+                  - setProperty:
+                      name: expirationReason
+                      simple: "usage count (${body.usageCount} uses)"
+                  - to: direct:auto-rotate-key
+              - simple: "${body.ageInDays} >= {{key.max.age.days}}"
+                steps:
+                  - log: "WARNING: Key has reached maximum age 
(${body.ageInDays} >= {{key.max.age.days}} days)"
+                  - setProperty:
+                      name: expirationReason
+                      simple: "age (${body.ageInDays} days)"
+                  - to: direct:auto-rotate-key
+            otherwise:
+              steps:
+                - setBody:
+                    simple: "Key is valid (usage: 
${body.usageCount}/{{key.max.usage.count}}, age: 
${body.ageInDays}/{{key.max.age.days}} days)"
+
+- route:
+    id: auto-rotate-key
+    from:
+      uri: direct:auto-rotate-key
+      steps:
+        - log: ">>> AUTOMATIC KEY ROTATION TRIGGERED <<<"
+        - log: "Reason: Key expired due to 
${exchangeProperty.expirationReason}"
+        - setProperty:
+            name: timestamp
+            simple: "${date:now:yyyyMMdd-HHmmss}"
+        - setProperty:
+            name: newKeyId
+            simple: "document-signing-key"
+        - log: "Rotating key from 'document-signing-key' to 
'${exchangeProperty.newKeyId}'"
+        - bean:
+            ref: keyLifecycleManager
+            method: "rotateKey('document-signing-key', 
'${exchangeProperty.newKeyId}', 'DILITHIUM')"
+        - log: ">>> KEY ROTATION COMPLETED <<<"
+        - log: "Old key 'document-signing-key' status: DEPRECATED"
+        - log: "New key '${exchangeProperty.newKeyId}' status: ACTIVE"
+        - setProperty:
+            name: activeKeyId
+            simple: "${exchangeProperty.newKeyId}"
+        - bean:
+            ref: keyLifecycleManager
+            method: "getKeyMetadata('${exchangeProperty.newKeyId}')"
+        - setBody:
+            simple: |
+              {
+                "rotationTriggered": true,
+                "reason": "${exchangeProperty.expirationReason}",
+                "oldKey": "document-signing-key",
+                "oldKeyStatus": "DEPRECATED",
+                "newKey": "${exchangeProperty.newKeyId}",
+                "newKeyStatus": "ACTIVE",
+                "newKeyCreatedAt": "${body.createdAt}",
+                "message": "Key automatically rotated due to 
${exchangeProperty.expirationReason}"
+              }
+        - log: "Rotation details: ${body}"


Reply via email to