This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch CAMEL-22084 in repository https://gitbox.apache.org/repos/asf/camel.git
commit ca7dbf2949eec5eb91d2184a8c04d72f1d3bbbd8 Author: Andrea Cosentino <anco...@gmail.com> AuthorDate: Mon May 19 15:03:41 2025 +0200 CAMEL-22084 - Camel-PQC: Support KeyStore and Alias for KeyPair for signing and verifying Signed-off-by: Andrea Cosentino <anco...@gmail.com> --- .../component/pqc/PQCComponentConfigurer.java | 20 ++- .../camel/component/pqc/PQCEndpointConfigurer.java | 20 ++- .../camel/component/pqc/PQCEndpointUriFactory.java | 9 +- .../org/apache/camel/component/pqc/pqc.json | 30 +++-- .../apache/camel/component/pqc/PQCComponent.java | 6 +- .../camel/component/pqc/PQCConfiguration.java | 43 ++++++ .../apache/camel/component/pqc/PQCProducer.java | 23 +++- .../pqc/PQCSignatureOnlyKeyStoreTest.java | 148 ++++++++++++++++++++ .../pqc/PQCSignatureSLHDSAKeystoreTest.java | 149 +++++++++++++++++++++ 9 files changed, 426 insertions(+), 22 deletions(-) diff --git a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCComponentConfigurer.java b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCComponentConfigurer.java index e73482ba596..3776524360e 100644 --- a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCComponentConfigurer.java +++ b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCComponentConfigurer.java @@ -43,6 +43,12 @@ public class PQCComponentConfigurer extends PropertyConfigurerSupport implements case "keyGenerator": getOrCreateConfiguration(target).setKeyGenerator(property(camelContext, javax.crypto.KeyGenerator.class, value)); return true; case "keypair": case "keyPair": getOrCreateConfiguration(target).setKeyPair(property(camelContext, java.security.KeyPair.class, value)); return true; + case "keypairalias": + case "keyPairAlias": getOrCreateConfiguration(target).setKeyPairAlias(property(camelContext, java.lang.String.class, value)); return true; + case "keystore": + case "keyStore": getOrCreateConfiguration(target).setKeyStore(property(camelContext, java.security.KeyStore.class, value)); return true; + case "keystorepassword": + case "keyStorePassword": getOrCreateConfiguration(target).setKeyStorePassword(property(camelContext, java.lang.String.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; case "operation": getOrCreateConfiguration(target).setOperation(property(camelContext, org.apache.camel.component.pqc.PQCOperations.class, value)); return true; @@ -61,7 +67,7 @@ public class PQCComponentConfigurer extends PropertyConfigurerSupport implements @Override public String[] getAutowiredNames() { - return new String[]{"keyGenerator", "keyPair", "signer"}; + return new String[]{"keyGenerator", "keyPair", "keyStore", "signer"}; } @Override @@ -80,6 +86,12 @@ public class PQCComponentConfigurer extends PropertyConfigurerSupport implements case "keyGenerator": return javax.crypto.KeyGenerator.class; case "keypair": case "keyPair": return java.security.KeyPair.class; + case "keypairalias": + case "keyPairAlias": return java.lang.String.class; + case "keystore": + case "keyStore": return java.security.KeyStore.class; + case "keystorepassword": + case "keyStorePassword": return java.lang.String.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; case "operation": return org.apache.camel.component.pqc.PQCOperations.class; @@ -113,6 +125,12 @@ public class PQCComponentConfigurer extends PropertyConfigurerSupport implements case "keyGenerator": return getOrCreateConfiguration(target).getKeyGenerator(); case "keypair": case "keyPair": return getOrCreateConfiguration(target).getKeyPair(); + case "keypairalias": + case "keyPairAlias": return getOrCreateConfiguration(target).getKeyPairAlias(); + case "keystore": + case "keyStore": return getOrCreateConfiguration(target).getKeyStore(); + case "keystorepassword": + case "keyStorePassword": return getOrCreateConfiguration(target).getKeyStorePassword(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); case "operation": return getOrCreateConfiguration(target).getOperation(); diff --git a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointConfigurer.java b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointConfigurer.java index 5b10acac77a..6123056a470 100644 --- a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointConfigurer.java +++ b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointConfigurer.java @@ -29,6 +29,12 @@ public class PQCEndpointConfigurer extends PropertyConfigurerSupport implements case "keyGenerator": target.getConfiguration().setKeyGenerator(property(camelContext, javax.crypto.KeyGenerator.class, value)); return true; case "keypair": case "keyPair": target.getConfiguration().setKeyPair(property(camelContext, java.security.KeyPair.class, value)); return true; + case "keypairalias": + case "keyPairAlias": target.getConfiguration().setKeyPairAlias(property(camelContext, java.lang.String.class, value)); return true; + case "keystore": + case "keyStore": target.getConfiguration().setKeyStore(property(camelContext, java.security.KeyStore.class, value)); return true; + case "keystorepassword": + case "keyStorePassword": target.getConfiguration().setKeyStorePassword(property(camelContext, java.lang.String.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; case "operation": target.getConfiguration().setOperation(property(camelContext, org.apache.camel.component.pqc.PQCOperations.class, value)); return true; @@ -47,7 +53,7 @@ public class PQCEndpointConfigurer extends PropertyConfigurerSupport implements @Override public String[] getAutowiredNames() { - return new String[]{"keyGenerator", "keyPair", "signer"}; + return new String[]{"keyGenerator", "keyPair", "keyStore", "signer"}; } @Override @@ -59,6 +65,12 @@ public class PQCEndpointConfigurer extends PropertyConfigurerSupport implements case "keyGenerator": return javax.crypto.KeyGenerator.class; case "keypair": case "keyPair": return java.security.KeyPair.class; + case "keypairalias": + case "keyPairAlias": return java.lang.String.class; + case "keystore": + case "keyStore": return java.security.KeyStore.class; + case "keystorepassword": + case "keyStorePassword": return java.lang.String.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; case "operation": return org.apache.camel.component.pqc.PQCOperations.class; @@ -85,6 +97,12 @@ public class PQCEndpointConfigurer extends PropertyConfigurerSupport implements case "keyGenerator": return target.getConfiguration().getKeyGenerator(); case "keypair": case "keyPair": return target.getConfiguration().getKeyPair(); + case "keypairalias": + case "keyPairAlias": return target.getConfiguration().getKeyPairAlias(); + case "keystore": + case "keyStore": return target.getConfiguration().getKeyStore(); + case "keystorepassword": + case "keyStorePassword": return target.getConfiguration().getKeyStorePassword(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); case "operation": return target.getConfiguration().getOperation(); diff --git a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointUriFactory.java b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointUriFactory.java index 2cb2277e03f..9198976e4aa 100644 --- a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointUriFactory.java +++ b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/PQCEndpointUriFactory.java @@ -23,10 +23,13 @@ public class PQCEndpointUriFactory extends org.apache.camel.support.component.En private static final Set<String> SECRET_PROPERTY_NAMES; private static final Set<String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(11); + Set<String> props = new HashSet<>(14); props.add("keyEncapsulationAlgorithm"); props.add("keyGenerator"); props.add("keyPair"); + props.add("keyPairAlias"); + props.add("keyStore"); + props.add("keyStorePassword"); props.add("label"); props.add("lazyStartProducer"); props.add("operation"); @@ -36,7 +39,9 @@ public class PQCEndpointUriFactory extends org.apache.camel.support.component.En props.add("symmetricKeyAlgorithm"); props.add("symmetricKeyLength"); PROPERTY_NAMES = Collections.unmodifiableSet(props); - SECRET_PROPERTY_NAMES = Collections.emptySet(); + Set<String> secretProps = new HashSet<>(1); + secretProps.add("keyStorePassword"); + SECRET_PROPERTY_NAMES = Collections.unmodifiableSet(secretProps); MULTI_VALUE_PREFIXES = Collections.emptySet(); } diff --git a/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/pqc.json b/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/pqc.json index 601b3c0cce1..0b79b74e446 100644 --- a/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/pqc.json +++ b/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/pqc.json @@ -31,13 +31,16 @@ "keyEncapsulationAlgorithm": { "index": 4, "kind": "property", "displayName": "Key Encapsulation Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "MLKEM", "BIKE", "HQC", "CMCE", "SABER", "FRODO", "NTRU", "NTRULPRime" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configu [...] "keyGenerator": { "index": 5, "kind": "property", "displayName": "Key Generator", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The Key Generator to be used in encapsulation and extraction" }, "keyPair": { "index": 6, "kind": "property", "displayName": "Key Pair", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.KeyPair", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The KeyPair to be used" }, - "signatureAlgorithm": { "index": 7, "kind": "property", "displayName": "Signature Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "MLDSA", "SLHDSA", "LMS", "XMSS", "FALCON", "PICNIC", "RAINBOW" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description" [...] - "signer": { "index": 8, "kind": "property", "displayName": "Signer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.Signature", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The Signer to be used" }, - "storeExtractedSecretKeyAsHeader": { "index": 9, "kind": "property", "displayName": "Store Extracted Secret Key As Header", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "In the context of extractSecr [...] - "symmetricKeyAlgorithm": { "index": 10, "kind": "property", "displayName": "Symmetric Key Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5", "CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147", "GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4", "DESEDE" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, " [...] - "symmetricKeyLength": { "index": 11, "kind": "property", "displayName": "Symmetric Key Length", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 128, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The required length of the symmetric key used" }, - "healthCheckConsumerEnabled": { "index": 12, "kind": "property", "displayName": "Health Check Consumer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all consumer based health checks from this component" }, - "healthCheckProducerEnabled": { "index": 13, "kind": "property", "displayName": "Health Check Producer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all producer based health checks from this component. Notice: Camel has by default disabled all producer based health-checks. You can turn on produce [...] + "keyPairAlias": { "index": 7, "kind": "property", "displayName": "Key Pair Alias", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "A KeyPair alias to use in combination with KeyStore parameter" }, + "keyStore": { "index": 8, "kind": "property", "displayName": "Key Store", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.KeyStore", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "A KeyStore where we could get Cryptographic material" }, + "keyStorePassword": { "index": 9, "kind": "property", "displayName": "Key Store Password", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The KeyStore password to use in combination with KeyStore Parameter" }, + "signatureAlgorithm": { "index": 10, "kind": "property", "displayName": "Signature Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "MLDSA", "SLHDSA", "LMS", "XMSS", "FALCON", "PICNIC", "RAINBOW" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description [...] + "signer": { "index": 11, "kind": "property", "displayName": "Signer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.Signature", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The Signer to be used" }, + "storeExtractedSecretKeyAsHeader": { "index": 12, "kind": "property", "displayName": "Store Extracted Secret Key As Header", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "In the context of extractSec [...] + "symmetricKeyAlgorithm": { "index": 13, "kind": "property", "displayName": "Symmetric Key Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5", "CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147", "GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4", "DESEDE" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, " [...] + "symmetricKeyLength": { "index": 14, "kind": "property", "displayName": "Symmetric Key Length", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 128, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The required length of the symmetric key used" }, + "healthCheckConsumerEnabled": { "index": 15, "kind": "property", "displayName": "Health Check Consumer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all consumer based health checks from this component" }, + "healthCheckProducerEnabled": { "index": 16, "kind": "property", "displayName": "Health Check Producer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all producer based health checks from this component. Notice: Camel has by default disabled all producer based health-checks. You can turn on produce [...] }, "headers": { "CamelPQCOperation": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation we want to perform", "constantName": "org.apache.camel.component.pqc.PQCConstants#OPERATION" }, @@ -52,10 +55,13 @@ "keyEncapsulationAlgorithm": { "index": 3, "kind": "parameter", "displayName": "Key Encapsulation Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "MLKEM", "BIKE", "HQC", "CMCE", "SABER", "FRODO", "NTRU", "NTRULPRime" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "config [...] "keyGenerator": { "index": 4, "kind": "parameter", "displayName": "Key Generator", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The Key Generator to be used in encapsulation and extraction" }, "keyPair": { "index": 5, "kind": "parameter", "displayName": "Key Pair", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.KeyPair", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The KeyPair to be used" }, - "signatureAlgorithm": { "index": 6, "kind": "parameter", "displayName": "Signature Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "MLDSA", "SLHDSA", "LMS", "XMSS", "FALCON", "PICNIC", "RAINBOW" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description [...] - "signer": { "index": 7, "kind": "parameter", "displayName": "Signer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.Signature", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The Signer to be used" }, - "storeExtractedSecretKeyAsHeader": { "index": 8, "kind": "parameter", "displayName": "Store Extracted Secret Key As Header", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "In the context of extractSec [...] - "symmetricKeyAlgorithm": { "index": 9, "kind": "parameter", "displayName": "Symmetric Key Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5", "CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147", "GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4", "DESEDE" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, " [...] - "symmetricKeyLength": { "index": 10, "kind": "parameter", "displayName": "Symmetric Key Length", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 128, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The required length of the symmetric key used" } + "keyPairAlias": { "index": 6, "kind": "parameter", "displayName": "Key Pair Alias", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "A KeyPair alias to use in combination with KeyStore parameter" }, + "keyStore": { "index": 7, "kind": "parameter", "displayName": "Key Store", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.KeyStore", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "A KeyStore where we could get Cryptographic material" }, + "keyStorePassword": { "index": 8, "kind": "parameter", "displayName": "Key Store Password", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The KeyStore password to use in combination with KeyStore Parameter" }, + "signatureAlgorithm": { "index": 9, "kind": "parameter", "displayName": "Signature Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "MLDSA", "SLHDSA", "LMS", "XMSS", "FALCON", "PICNIC", "RAINBOW" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description [...] + "signer": { "index": 10, "kind": "parameter", "displayName": "Signer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.security.Signature", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The Signer to be used" }, + "storeExtractedSecretKeyAsHeader": { "index": 11, "kind": "parameter", "displayName": "Store Extracted Secret Key As Header", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "In the context of extractSe [...] + "symmetricKeyAlgorithm": { "index": 12, "kind": "parameter", "displayName": "Symmetric Key Algorithm", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5", "CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147", "GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4", "DESEDE" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, [...] + "symmetricKeyLength": { "index": 13, "kind": "parameter", "displayName": "Symmetric Key Length", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 128, "configurationClass": "org.apache.camel.component.pqc.PQCConfiguration", "configurationField": "configuration", "description": "The required length of the symmetric key used" } } } diff --git a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java index c0f672f33e2..78239db496a 100644 --- a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java +++ b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java @@ -58,7 +58,8 @@ public class PQCComponent extends HealthCheckComponent { PQCEndpoint endpoint = new PQCEndpoint(uri, this, configuration); setProperties(endpoint, parameters); - if (ObjectHelper.isEmpty(configuration.getSigner()) && ObjectHelper.isEmpty(configuration.getKeyPair())) { + if (ObjectHelper.isEmpty(configuration.getSigner()) && ObjectHelper.isEmpty(configuration.getKeyPair()) + && ObjectHelper.isEmpty(configuration.getKeyStore()) && ObjectHelper.isEmpty(configuration.getKeyPairAlias())) { if (ObjectHelper.isNotEmpty(configuration.getSignatureAlgorithm())) { switch (configuration.getSignatureAlgorithm()) { case "MLDSA": @@ -95,7 +96,8 @@ public class PQCComponent extends HealthCheckComponent { } } - if (ObjectHelper.isEmpty(configuration.getKeyGenerator()) && ObjectHelper.isEmpty(configuration.getKeyPair())) { + if (ObjectHelper.isEmpty(configuration.getKeyGenerator()) && ObjectHelper.isEmpty(configuration.getKeyPair()) + && ObjectHelper.isEmpty(configuration.getKeyStore()) && ObjectHelper.isEmpty(configuration.getKeyPairAlias())) { if (ObjectHelper.isNotEmpty(configuration.getKeyEncapsulationAlgorithm())) { switch (configuration.getKeyEncapsulationAlgorithm()) { case "MLKEM": diff --git a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCConfiguration.java b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCConfiguration.java index 820930f0927..454e0049f77 100644 --- a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCConfiguration.java +++ b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCConfiguration.java @@ -17,6 +17,7 @@ package org.apache.camel.component.pqc; import java.security.KeyPair; +import java.security.KeyStore; import java.security.Signature; import javax.crypto.KeyGenerator; @@ -60,6 +61,15 @@ public class PQCConfiguration implements Cloneable { @UriParam @Metadata(label = "advanced", defaultValue = "false") private boolean storeExtractedSecretKeyAsHeader = false; + @UriParam + @Metadata(label = "advanced", autowired = true) + private KeyStore keyStore; + @UriParam + @Metadata(label = "advanced") + private String keyPairAlias; + @UriParam + @Metadata(label = "advanced", secret = true) + private String keyStorePassword; public PQCOperations getOperation() { return operation; @@ -161,6 +171,39 @@ public class PQCConfiguration implements Cloneable { this.storeExtractedSecretKeyAsHeader = storeExtractedSecretKeyAsHeader; } + public KeyStore getKeyStore() { + return keyStore; + } + + /** + * A KeyStore where we could get Cryptographic material + */ + public void setKeyStore(KeyStore keyStore) { + this.keyStore = keyStore; + } + + public String getKeyStorePassword() { + return keyStorePassword; + } + + /** + * The KeyStore password to use in combination with KeyStore Parameter + */ + public void setKeyStorePassword(String keyStorePassword) { + this.keyStorePassword = keyStorePassword; + } + + public String getKeyPairAlias() { + return keyPairAlias; + } + + /** + * A KeyPair alias to use in combination with KeyStore parameter + */ + public void setKeyPairAlias(String keyPairAlias) { + this.keyPairAlias = keyPairAlias; + } + // ************************************************* // // ************************************************* diff --git a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCProducer.java b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCProducer.java index 1b6e76fb572..336ff5bd14b 100644 --- a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCProducer.java +++ b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCProducer.java @@ -17,6 +17,7 @@ package org.apache.camel.component.pqc; import java.security.*; +import java.security.cert.Certificate; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -42,6 +43,7 @@ public class PQCProducer extends DefaultProducer { private Signature signer; private KeyGenerator keyGenerator; + private KeyPair keyPair; public PQCProducer(Endpoint endpoint) { super(endpoint); @@ -111,13 +113,26 @@ public class PQCProducer extends DefaultProducer { .getAlgorithm()); } } + + if (ObjectHelper.isNotEmpty(getConfiguration().getKeyStore()) + && ObjectHelper.isNotEmpty(getConfiguration().getKeyPairAlias()) + && ObjectHelper.isNotEmpty(getConfiguration().getKeyStorePassword())) { + KeyStore keyStore = getConfiguration().getKeyStore(); + PrivateKey privateKey = (PrivateKey) keyStore.getKey(getConfiguration().getKeyPairAlias(), + getConfiguration().getKeyStorePassword().toCharArray()); + Certificate cert = keyStore.getCertificate(getConfiguration().getKeyPairAlias()); + PublicKey publicKey = cert.getPublicKey(); + keyPair = new KeyPair(publicKey, privateKey); + } else { + keyPair = getConfiguration().getKeyPair(); + } } private void signature(Exchange exchange) throws InvalidPayloadException, InvalidKeyException, SignatureException { String payload = exchange.getMessage().getMandatoryBody(String.class); - signer.initSign(getEndpoint().getConfiguration().getKeyPair().getPrivate()); + signer.initSign(keyPair.getPrivate()); signer.update(payload.getBytes()); byte[] signature = signer.sign(); @@ -128,7 +143,7 @@ public class PQCProducer extends DefaultProducer { throws InvalidPayloadException, InvalidKeyException, SignatureException { String payload = exchange.getMessage().getMandatoryBody(String.class); - signer.initVerify(getEndpoint().getConfiguration().getKeyPair().getPublic()); + signer.initVerify(keyPair.getPublic()); signer.update(payload.getBytes()); if (signer.verify(exchange.getMessage().getHeader(PQCConstants.SIGNATURE, byte[].class))) { exchange.getMessage().setHeader(PQCConstants.VERIFY, true); @@ -142,7 +157,7 @@ public class PQCProducer extends DefaultProducer { // initialise for creating an encapsulation and shared secret. keyGenerator.init( new KEMGenerateSpec( - getEndpoint().getConfiguration().getKeyPair().getPublic(), + keyPair.getPublic(), getEndpoint().getConfiguration().getSymmetricKeyAlgorithm(), getEndpoint().getConfiguration().getSymmetricKeyLength()), new SecureRandom()); @@ -165,7 +180,7 @@ public class PQCProducer extends DefaultProducer { keyGenerator.init( new KEMExtractSpec( - getEndpoint().getConfiguration().getKeyPair().getPrivate(), payload.getEncapsulation(), + keyPair.getPrivate(), payload.getEncapsulation(), PQCSymmetricAlgorithms.valueOf(getConfiguration().getSymmetricKeyAlgorithm()).getAlgorithm(), getEndpoint().getConfiguration().getSymmetricKeyLength()), new SecureRandom()); diff --git a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCSignatureOnlyKeyStoreTest.java b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCSignatureOnlyKeyStoreTest.java new file mode 100644 index 00000000000..e6ce0659e68 --- /dev/null +++ b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCSignatureOnlyKeyStoreTest.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.pqc; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Date; + +import org.apache.camel.BindToRegistry; +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PQCSignatureOnlyKeyStoreTest extends CamelTestSupport { + + @EndpointInject("mock:sign") + protected MockEndpoint resultSign; + + @EndpointInject("mock:verify") + protected MockEndpoint resultVerify; + + @Produce("direct:sign") + protected ProducerTemplate templateSign; + + public PQCSignatureOnlyKeyStoreTest() throws NoSuchAlgorithmException { + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:sign").to("pqc:sign?operation=sign&keyPairAlias=mykey&keyStorePassword=changeit").to("mock:sign") + .to("pqc:verify?operation=verify&keyPairAlias=mykey&keyStorePassword=changeit") + .to("mock:verify"); + } + }; + } + + @BeforeAll + public static void startup() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + } + + @AfterAll + public static void teardown() throws Exception { + Files.deleteIfExists(Path.of("keystore.jks")); + } + + @Test + void testSignAndVerify() throws Exception { + resultSign.expectedMessageCount(1); + resultVerify.expectedMessageCount(1); + templateSign.sendBody("Hello"); + resultSign.assertIsSatisfied(); + resultVerify.assertIsSatisfied(); + assertTrue(resultVerify.getExchanges().get(0).getMessage().getHeader(PQCConstants.VERIFY, Boolean.class)); + } + + @BindToRegistry("Keystore") + public KeyStore setKeyStore() + throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, KeyStoreException, + CertificateException, IOException, OperatorCreationException, UnrecoverableKeyException { + KeyPairGenerator kpGen = KeyPairGenerator.getInstance(PQCSignatureAlgorithms.MLDSA.getAlgorithm(), + PQCSignatureAlgorithms.MLDSA.getBcProvider()); + kpGen.initialize(MLDSAParameterSpec.ml_dsa_65); + KeyPair kp = kpGen.generateKeyPair(); + + // Validity + Date startDate = new Date(); + Date endDate = new Date(startDate.getTime() + 365L * 24 * 60 * 60 * 1000); // 1 year + + // Serial Number + BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); + + X500Name dnName = new X500Name("CN=Test User"); + // Build the certificate + X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( + dnName, + serialNumber, + startDate, + endDate, + dnName, + kp.getPublic()); + + ContentSigner contentSigner = new JcaContentSignerBuilder(PQCSignatureAlgorithms.MLDSA.getAlgorithm()) + .build(kp.getPrivate()); + + X509Certificate certificate = new JcaX509CertificateConverter() + .setProvider("BC") + .getCertificate(certBuilder.build(contentSigner)); + + KeyStore keyStore = KeyStore.getInstance("JKS"); + char[] password = "changeit".toCharArray(); + keyStore.load(null, password); // initialize new keystore + keyStore.setKeyEntry("mykey", kp.getPrivate(), password, new Certificate[] { certificate }); + + // Save keystore to file + try (FileOutputStream fos = new FileOutputStream("keystore.jks")) { + keyStore.store(fos, password); + } + return keyStore; + } + + @BindToRegistry("Signer") + public Signature getSigner() throws NoSuchAlgorithmException { + Signature mlDsa = Signature.getInstance(PQCSignatureAlgorithms.MLDSA.getAlgorithm()); + return mlDsa; + } +} diff --git a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCSignatureSLHDSAKeystoreTest.java b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCSignatureSLHDSAKeystoreTest.java new file mode 100644 index 00000000000..933478a560e --- /dev/null +++ b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCSignatureSLHDSAKeystoreTest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.pqc; + +import org.apache.camel.BindToRegistry; +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; +import org.bouncycastle.jcajce.spec.SLHDSAParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PQCSignatureSLHDSAKeystoreTest extends CamelTestSupport { + + @EndpointInject("mock:sign") + protected MockEndpoint resultSign; + + @EndpointInject("mock:verify") + protected MockEndpoint resultVerify; + + @Produce("direct:sign") + protected ProducerTemplate templateSign; + + public PQCSignatureSLHDSAKeystoreTest() throws NoSuchAlgorithmException { + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:sign").to("pqc:sign?operation=sign&keyPairAlias=mykey&keyStorePassword=changeit").to("mock:sign") + .to("pqc:verify?operation=verify&keyPairAlias=mykey&keyStorePassword=changeit") + .to("mock:verify"); + } + }; + } + + @BeforeAll + public static void startup() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + } + + @AfterAll + public static void teardown() throws Exception { + Files.deleteIfExists(Path.of("keystore.jks")); + } + + @Test + void testSignAndVerify() throws Exception { + resultSign.expectedMessageCount(1); + resultVerify.expectedMessageCount(1); + templateSign.sendBody("Hello"); + resultSign.assertIsSatisfied(); + resultVerify.assertIsSatisfied(); + assertTrue(resultVerify.getExchanges().get(0).getMessage().getHeader(PQCConstants.VERIFY, Boolean.class)); + } + + @BindToRegistry("Keystore") + public KeyStore setKeyStore() + throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, KeyStoreException, + CertificateException, IOException, OperatorCreationException, UnrecoverableKeyException { + KeyPairGenerator kpGen = KeyPairGenerator.getInstance(PQCSignatureAlgorithms.SLHDSA.getAlgorithm(), + PQCSignatureAlgorithms.SLHDSA.getBcProvider()); + kpGen.initialize(SLHDSAParameterSpec.slh_dsa_shake_192s); + KeyPair kp = kpGen.generateKeyPair(); + + // Validity + Date startDate = new Date(); + Date endDate = new Date(startDate.getTime() + 365L * 24 * 60 * 60 * 1000); // 1 year + + // Serial Number + BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); + + X500Name dnName = new X500Name("CN=Test User"); + // Build the certificate + X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( + dnName, + serialNumber, + startDate, + endDate, + dnName, + kp.getPublic()); + + ContentSigner contentSigner = new JcaContentSignerBuilder(PQCSignatureAlgorithms.SLHDSA.getAlgorithm()) + .build(kp.getPrivate()); + + X509Certificate certificate = new JcaX509CertificateConverter() + .setProvider("BC") + .getCertificate(certBuilder.build(contentSigner)); + + KeyStore keyStore = KeyStore.getInstance("JKS"); + char[] password = "changeit".toCharArray(); + keyStore.load(null, password); // initialize new keystore + keyStore.setKeyEntry("mykey", kp.getPrivate(), password, new Certificate[] { certificate }); + + // Save keystore to file + try (FileOutputStream fos = new FileOutputStream("keystore.jks")) { + keyStore.store(fos, password); + } + return keyStore; + } + + @BindToRegistry("Signer") + public Signature getSigner() throws NoSuchAlgorithmException { + Signature mlDsa = Signature.getInstance(PQCSignatureAlgorithms.SLHDSA.getAlgorithm()); + return mlDsa; + } +}