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

oscerd pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.18.x by this push:
     new d8a929534f63 [backport camel-4.18.x] CAMEL-23844: Camel-PQC - use the 
mapped JCE algorithm name when restoring the secret key (#24679)
d8a929534f63 is described below

commit d8a929534f63149f4ac56c5a04499f1a919616e8
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Jul 14 14:40:36 2026 +0200

    [backport camel-4.18.x] CAMEL-23844: Camel-PQC - use the mapped JCE 
algorithm name when restoring the secret key (#24679)
    
    [backport camel-4.18.x] CAMEL-23844: Camel-PQC use the mapped JCE algorithm 
name when restoring the secret key
    
    extractSecretKeyFromEncapsulation built the restored SecretKeySpec with the 
raw
    PQCSymmetricAlgorithms enum name instead of the mapped JCE algorithm name, 
unlike
    extractEncapsulation which maps it correctly.
    
    For GOST3412_2015 the JCE name is GOST3412-2015, so 
Cipher.getInstance("GOST3412_2015")
    throws NoSuchAlgorithmException and the restored key was unusable 
downstream. DESEDE vs
    DESede is only a case difference, which the case-insensitive JCA lookup 
tolerates.
    
    Backport of #24678. The hybrid extract path does not exist on this branch, 
so only the
    standard path is affected.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../apache/camel/component/pqc/PQCProducer.java    |   7 +-
 .../pqc/PQCExtractSecretKeyAlgorithmNameTest.java  | 147 +++++++++++++++++++++
 2 files changed, 153 insertions(+), 1 deletion(-)

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 21b652656773..a008b38966dd 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
@@ -196,7 +196,12 @@ public class PQCProducer extends DefaultProducer {
             throw new IllegalArgumentException("Symmetric Algorithm needs to 
be specified");
         }
 
-        SecretKey restoredKey = new SecretKeySpec(payload.getEncoded(), 
getConfiguration().getSymmetricKeyAlgorithm());
+        // Use the mapped JCE algorithm name (as extractEncapsulation does), 
not the raw enum name: for algorithms
+        // whose enum name differs from the JCE name (for example 
GOST3412_2015 -> GOST3412-2015) the raw name is not a
+        // resolvable cipher algorithm, so the restored key would carry an 
unusable algorithm label
+        SecretKey restoredKey = new SecretKeySpec(
+                payload.getEncoded(),
+                
PQCSymmetricAlgorithms.valueOf(getConfiguration().getSymmetricKeyAlgorithm()).getAlgorithm());
 
         if (!getConfiguration().isStoreExtractedSecretKeyAsHeader()) {
             exchange.getMessage().setBody(restoredKey, SecretKey.class);
diff --git 
a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCExtractSecretKeyAlgorithmNameTest.java
 
b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCExtractSecretKeyAlgorithmNameTest.java
new file mode 100644
index 000000000000..e4cb16000d5a
--- /dev/null
+++ 
b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCExtractSecretKeyAlgorithmNameTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * The secret key restored by extractSecretKeyFromEncapsulation must carry the 
mapped JCE algorithm name, not the raw
+ * PQCSymmetricAlgorithms enum name. For GOST3412_2015 the raw name is not a 
resolvable cipher algorithm at all.
+ */
+public class PQCExtractSecretKeyAlgorithmNameTest extends CamelTestSupport {
+
+    @EndpointInject("mock:gost")
+    protected MockEndpoint resultGost;
+
+    @EndpointInject("mock:desede")
+    protected MockEndpoint resultDesede;
+
+    @BeforeAll
+    public static void startup() {
+        ensureProviders();
+    }
+
+    /**
+     * PQCEndpoint removes the BouncyCastle providers from the JVM when it 
stops, so with more than one test method in
+     * the class they must be (re-)registered before each CamelContext is 
built.
+     */
+    private static void ensureProviders() {
+        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+        if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == 
null) {
+            Security.addProvider(new BouncyCastlePQCProvider());
+        }
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:gost")
+                        
.to("pqc:keyenc?operation=generateSecretKeyEncapsulation"
+                            + 
"&symmetricKeyAlgorithm=GOST3412_2015&symmetricKeyLength=256")
+                        
.to("pqc:keyenc?operation=extractSecretKeyEncapsulation"
+                            + 
"&symmetricKeyAlgorithm=GOST3412_2015&symmetricKeyLength=256")
+                        
.to("pqc:keyenc?operation=extractSecretKeyFromEncapsulation"
+                            + 
"&symmetricKeyAlgorithm=GOST3412_2015&symmetricKeyLength=256")
+                        .to("mock:gost");
+
+                from("direct:desede")
+                        
.to("pqc:keyenc?operation=generateSecretKeyEncapsulation"
+                            + 
"&symmetricKeyAlgorithm=DESEDE&symmetricKeyLength=192")
+                        
.to("pqc:keyenc?operation=extractSecretKeyEncapsulation"
+                            + 
"&symmetricKeyAlgorithm=DESEDE&symmetricKeyLength=192")
+                        
.to("pqc:keyenc?operation=extractSecretKeyFromEncapsulation"
+                            + 
"&symmetricKeyAlgorithm=DESEDE&symmetricKeyLength=192")
+                        .to("mock:desede");
+            }
+        };
+    }
+
+    @Test
+    void testGost3412RestoredKeyUsesJceAlgorithmName() throws Exception {
+        resultGost.expectedMessageCount(1);
+        template.sendBody("direct:gost", "Hello");
+        resultGost.assertIsSatisfied();
+
+        SecretKey restored = 
resultGost.getExchanges().get(0).getMessage().getBody(SecretKey.class);
+        assertNotNull(restored);
+        // the enum name is GOST3412_2015 but the JCE name is GOST3412-2015
+        assertEquals(PQCSymmetricAlgorithms.GOST3412_2015.getAlgorithm(), 
restored.getAlgorithm());
+        assertEquals("GOST3412-2015", restored.getAlgorithm());
+        // with the raw enum name this throws NoSuchAlgorithmException, so the 
restored key was unusable
+        assertDoesNotThrow(() -> Cipher.getInstance(restored.getAlgorithm(), 
BouncyCastleProvider.PROVIDER_NAME));
+    }
+
+    @Test
+    void testDesedeRestoredKeyUsesJceAlgorithmName() throws Exception {
+        resultDesede.expectedMessageCount(1);
+        template.sendBody("direct:desede", "Hello");
+        resultDesede.assertIsSatisfied();
+
+        SecretKey restored = 
resultDesede.getExchanges().get(0).getMessage().getBody(SecretKey.class);
+        assertNotNull(restored);
+        assertEquals(PQCSymmetricAlgorithms.DESEDE.getAlgorithm(), 
restored.getAlgorithm());
+        assertEquals("DESede", restored.getAlgorithm());
+        assertDoesNotThrow(() -> Cipher.getInstance(restored.getAlgorithm(), 
BouncyCastleProvider.PROVIDER_NAME));
+    }
+
+    @BindToRegistry("Keypair")
+    public KeyPair setKeyPair()
+            throws NoSuchAlgorithmException, NoSuchProviderException,
+            InvalidAlgorithmParameterException {
+        ensureProviders();
+        KeyPairGenerator kpg = 
KeyPairGenerator.getInstance(PQCKeyEncapsulationAlgorithms.MLKEM.getAlgorithm(),
+                PQCKeyEncapsulationAlgorithms.MLKEM.getBcProvider());
+        kpg.initialize(MLKEMParameterSpec.ml_kem_512, new SecureRandom());
+        return kpg.generateKeyPair();
+    }
+
+    @BindToRegistry("KeyGenerator")
+    public KeyGenerator setKeyGenerator() throws NoSuchAlgorithmException, 
NoSuchProviderException {
+        ensureProviders();
+        return 
KeyGenerator.getInstance(PQCKeyEncapsulationAlgorithms.MLKEM.getAlgorithm(),
+                PQCKeyEncapsulationAlgorithms.MLKEM.getBcProvider());
+    }
+}

Reply via email to