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

gnodet pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 439a4e7f498e CAMEL-24716: fix serialization regression - resolve 
algorithm names lazily at crypto call-sites (#26671)
439a4e7f498e is described below

commit 439a4e7f498e75ac4b499948cbdd30eb546cd4a0
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Sep 21 13:24:16 2026 +0200

    CAMEL-24716: fix serialization regression - resolve algorithm names lazily 
at crypto call-sites (#26671)
    
    CAMEL-24716: fix serialization regression - resolve algorithm names lazily
    
    Resolving constant names (e.g. AES_256_GCM) to W3C URIs eagerly in
    the four setters caused a serialization regression: the ModelWriter
    compares getters against the constant-name defaults (e.g. "AES_256_GCM")
    to decide whether to suppress the attribute in the marshalled XML.
    After eager resolution, getters always returned W3C URIs, so the
    equality check never matched and all four algorithm attributes were
    written unconditionally, breaking round-trip serialization.
    
    Fix: remove resolveAlgorithm() from the four setters so getters return
    what was set. Add private resolvedXxx() helpers that apply
    resolveAlgorithm() lazily and call those helpers at every actual crypto
    call-site (marshal/unmarshal path).
    
    This was caught during review of the camel-4.22.x backport of #26467.
---
 .../xmlsecurity/XMLSecurityDataFormat.java         | 100 +++++++++++++--------
 .../xmlsecurity/XmlSecurityConstantNameTest.java   |  30 +++----
 2 files changed, 72 insertions(+), 58 deletions(-)

diff --git 
a/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
 
b/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
index cfd735447c95..d49891116b3e 100644
--- 
a/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
+++ 
b/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
@@ -120,6 +120,26 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
         return ALGORITHM_NAME_TO_URI.getOrDefault(nameOrUri, nameOrUri);
     }
 
+    /** Returns the resolved (W3C URI) form of {@link #xmlCipherAlgorithm} for 
use in crypto operations. */
+    private String resolvedXmlCipherAlgorithm() {
+        return resolveAlgorithm(xmlCipherAlgorithm);
+    }
+
+    /** Returns the resolved (W3C URI) form of {@link #keyCipherAlgorithm} for 
use in crypto operations. */
+    private String resolvedKeyCipherAlgorithm() {
+        return resolveAlgorithm(keyCipherAlgorithm);
+    }
+
+    /** Returns the resolved (W3C URI) form of {@link #digestAlgorithm} for 
use in crypto operations. */
+    private String resolvedDigestAlgorithm() {
+        return resolveAlgorithm(digestAlgorithm);
+    }
+
+    /** Returns the resolved (W3C URI) form of {@link #mgfAlgorithm} for use 
in crypto operations. */
+    private String resolvedMgfAlgorithm() {
+        return resolveAlgorithm(mgfAlgorithm);
+    }
+
     private String xmlCipherAlgorithm;
     private String keyCipherAlgorithm;
 
@@ -334,8 +354,9 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
         Document document = 
exchange.getContext().getTypeConverter().convertTo(Document.class, exchange, 
is);
 
         if (null != keyCipherAlgorithm
-                && (keyCipherAlgorithm.equals(XMLCipher.RSA_v1dot5) || 
keyCipherAlgorithm.equals(XMLCipher.RSA_OAEP)
-                        || keyCipherAlgorithm.equals(XMLCipher.RSA_OAEP_11))) {
+                && (resolvedKeyCipherAlgorithm().equals(XMLCipher.RSA_v1dot5)
+                        || 
resolvedKeyCipherAlgorithm().equals(XMLCipher.RSA_OAEP)
+                        || 
resolvedKeyCipherAlgorithm().equals(XMLCipher.RSA_OAEP_11))) {
             encryptAsymmetric(exchange, document, stream);
         } else if (null != recipientKeyAlias) {
             encryptAsymmetric(exchange, document, stream);
@@ -381,9 +402,9 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
 
         XMLCipher keyCipher;
         if (null != this.getKeyCipherAlgorithm()) {
-            keyCipher = XMLCipher.getInstance(this.getKeyCipherAlgorithm(), 
null, digestAlgorithm);
+            keyCipher = XMLCipher.getInstance(resolvedKeyCipherAlgorithm(), 
null, resolvedDigestAlgorithm());
         } else {
-            keyCipher = XMLCipher.getInstance(XMLCipher.RSA_OAEP, null, 
digestAlgorithm);
+            keyCipher = XMLCipher.getInstance(XMLCipher.RSA_OAEP, null, 
resolvedDigestAlgorithm());
         }
 
         keyCipher.init(XMLCipher.WRAP_MODE, keyEncryptionKey);
@@ -400,13 +421,13 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
     private void encryptSymmetric(Exchange exchange, Document document, 
OutputStream stream) throws Exception {
         SecretKey keyEncryptionKey;
         SecretKey dataEncryptionKey;
-        if (xmlCipherAlgorithm.equals(XMLCipher.TRIPLEDES)) {
+        if (resolvedXmlCipherAlgorithm().equals(XMLCipher.TRIPLEDES)) {
             keyEncryptionKey = generateKeyEncryptionKey("DESede");
             dataEncryptionKey = generateDataEncryptionKey();
-        } else if (xmlCipherAlgorithm.equals(XMLCipher.SEED_128)) {
+        } else if (resolvedXmlCipherAlgorithm().equals(XMLCipher.SEED_128)) {
             keyEncryptionKey = generateKeyEncryptionKey("SEED");
             dataEncryptionKey = generateDataEncryptionKey();
-        } else if (xmlCipherAlgorithm.contains("camellia")) {
+        } else if (resolvedXmlCipherAlgorithm().contains("camellia")) {
             keyEncryptionKey = generateKeyEncryptionKey("CAMELLIA");
             dataEncryptionKey = generateDataEncryptionKey();
         } else {
@@ -463,7 +484,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
             Exchange exchange, Document document, OutputStream stream, Key 
dataEncryptionKey,
             XMLCipher keyCipher, Key keyEncryptionKey)
             throws Exception {
-        XMLCipher xmlCipher = XMLCipher.getInstance(xmlCipherAlgorithm);
+        XMLCipher xmlCipher = 
XMLCipher.getInstance(resolvedXmlCipherAlgorithm());
         xmlCipher.init(XMLCipher.ENCRYPT_MODE, dataEncryptionKey);
 
         if (secureTag.equalsIgnoreCase("")) {
@@ -511,8 +532,9 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
         }
 
         if (null != keyCipherAlgorithm
-                && (keyCipherAlgorithm.equals(XMLCipher.RSA_v1dot5) || 
keyCipherAlgorithm.equals(XMLCipher.RSA_OAEP)
-                        || keyCipherAlgorithm.equals(XMLCipher.RSA_OAEP_11))) {
+                && (resolvedKeyCipherAlgorithm().equals(XMLCipher.RSA_v1dot5)
+                        || 
resolvedKeyCipherAlgorithm().equals(XMLCipher.RSA_OAEP)
+                        || 
resolvedKeyCipherAlgorithm().equals(XMLCipher.RSA_OAEP_11))) {
             return decodeWithAsymmetricKey(exchange, encodedDocument);
         } else {
             LOG.debug("No (known) asymmetric keyCipherAlgorithm specified. 
Attempting to "
@@ -523,7 +545,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
 
     private Object decodeWithSymmetricKey(Exchange exchange, Document 
encodedDocument) throws Exception {
         SecretKey keyEncryptionKey;
-        if (xmlCipherAlgorithm.equals(XMLCipher.TRIPLEDES)) {
+        if (resolvedXmlCipherAlgorithm().equals(XMLCipher.TRIPLEDES)) {
             keyEncryptionKey = generateKeyEncryptionKey("DESede");
         } else {
             keyEncryptionKey = generateKeyEncryptionKey("AES");
@@ -677,23 +699,23 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
 
     private SecretKey generateDataEncryptionKey() throws Exception {
         KeyGenerator keyGenerator = null;
-        if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.TRIPLEDES)) {
+        if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.TRIPLEDES)) {
             keyGenerator = KeyGenerator.getInstance("DESede");
         } else {
             keyGenerator = KeyGenerator.getInstance("AES");
 
-            if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128)
-                    || 
xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128_GCM)
-                    || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.SEED_128)
-                    || 
xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.CAMELLIA_128)) {
+            if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_128)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_128_GCM)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.SEED_128)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.CAMELLIA_128)) {
                 keyGenerator.init(128);
-            } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192)
-                    || 
xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192_GCM)
-                    || 
xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.CAMELLIA_192)) {
+            } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_192)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_192_GCM)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.CAMELLIA_192)) {
                 keyGenerator.init(192);
-            } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256)
-                    || 
xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256_GCM)
-                    || 
xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.CAMELLIA_256)) {
+            } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_256)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_256_GCM)
+                    || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.CAMELLIA_256)) {
                 keyGenerator.init(256);
             }
         }
@@ -706,7 +728,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
             Key keyEncryptionKey)
             throws XMLEncryptionException {
 
-        EncryptedKey encryptedKey = keyCipher.encryptKey(document, 
dataEncryptionkey, mgfAlgorithm, null);
+        EncryptedKey encryptedKey = keyCipher.encryptKey(document, 
dataEncryptionkey, resolvedMgfAlgorithm(), null);
         if (addKeyValueForEncryptedKey && keyEncryptionKey instanceof 
PublicKey) {
             KeyInfo keyInfo = new KeyInfo(document);
             keyInfo.add((PublicKey) keyEncryptionKey);
@@ -721,24 +743,24 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
 
     private String generateXmlCipherAlgorithmKeyWrap() {
         String algorithmKeyWrap = null;
-        if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.TRIPLEDES)) {
+        if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.TRIPLEDES)) {
             algorithmKeyWrap = XMLCipher.TRIPLEDES_KeyWrap;
-        } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128)
-                || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128_GCM)) 
{
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_128)
+                || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_128_GCM)) {
             algorithmKeyWrap = XMLCipher.AES_128_KeyWrap;
-        } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192)
-                || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192_GCM)) 
{
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_192)
+                || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_192_GCM)) {
             algorithmKeyWrap = XMLCipher.AES_192_KeyWrap;
-        } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256)
-                || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256_GCM)) 
{
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_256)
+                || 
resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.AES_256_GCM)) {
             algorithmKeyWrap = XMLCipher.AES_256_KeyWrap;
-        } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.SEED_128)) {
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.SEED_128)) {
             algorithmKeyWrap = XMLCipher.SEED_128_KeyWrap;
-        } else if 
(xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.CAMELLIA_128)) {
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.CAMELLIA_128)) {
             algorithmKeyWrap = XMLCipher.CAMELLIA_128_KeyWrap;
-        } else if 
(xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.CAMELLIA_192)) {
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.CAMELLIA_192)) {
             algorithmKeyWrap = XMLCipher.CAMELLIA_192_KeyWrap;
-        } else if 
(xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.CAMELLIA_256)) {
+        } else if 
(resolvedXmlCipherAlgorithm().equalsIgnoreCase(XMLCipher.CAMELLIA_256)) {
             algorithmKeyWrap = XMLCipher.CAMELLIA_256_KeyWrap;
         }
 
@@ -747,7 +769,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
 
     // Check to see if the asymmetric key transport algorithm is allowed
     private void checkEncryptionAlgorithm(Key keyEncryptionKey, Element 
parentElement) throws Exception {
-        if (XMLCipher.RSA_v1dot5.equals(keyCipherAlgorithm)
+        if (XMLCipher.RSA_v1dot5.equals(resolvedKeyCipherAlgorithm())
                 || keyCipherAlgorithm == null
                 || !(keyEncryptionKey instanceof PrivateKey)) {
             // This only applies for Asymmetric Encryption
@@ -840,7 +862,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
     }
 
     public void setXmlCipherAlgorithm(String xmlCipherAlgorithm) {
-        this.xmlCipherAlgorithm = resolveAlgorithm(xmlCipherAlgorithm);
+        this.xmlCipherAlgorithm = xmlCipherAlgorithm;
     }
 
     public String getKeyCipherAlgorithm() {
@@ -848,7 +870,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
     }
 
     public void setKeyCipherAlgorithm(String keyCipherAlgorithm) {
-        this.keyCipherAlgorithm = resolveAlgorithm(keyCipherAlgorithm);
+        this.keyCipherAlgorithm = keyCipherAlgorithm;
     }
 
     public String getRecipientKeyAlias() {
@@ -908,7 +930,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
     }
 
     public void setDigestAlgorithm(String digestAlgorithm) {
-        this.digestAlgorithm = resolveAlgorithm(digestAlgorithm);
+        this.digestAlgorithm = digestAlgorithm;
     }
 
     public String getMgfAlgorithm() {
@@ -916,7 +938,7 @@ public class XMLSecurityDataFormat extends ServiceSupport 
implements DataFormat,
     }
 
     public void setMgfAlgorithm(String mgfAlgorithm) {
-        this.mgfAlgorithm = resolveAlgorithm(mgfAlgorithm);
+        this.mgfAlgorithm = mgfAlgorithm;
     }
 
     public boolean isAddKeyValueForEncryptedKey() {
diff --git 
a/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XmlSecurityConstantNameTest.java
 
b/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XmlSecurityConstantNameTest.java
index cfd842dd77a9..c5164a3d1c9b 100644
--- 
a/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XmlSecurityConstantNameTest.java
+++ 
b/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XmlSecurityConstantNameTest.java
@@ -106,15 +106,15 @@ class XmlSecurityConstantNameTest extends 
CamelTestSupport {
         df.setSecureTag("//cheesesites/italy/cheese");
         // Use the constant name, not the URI
         df.setXmlCipherAlgorithm("AES_256_GCM");
-
-        // verify the setter resolved it to the URI
-        assertEquals(XMLCipher.AES_256_GCM, df.getXmlCipherAlgorithm());
+        // The setter is a plain assignment; resolution from constant name to 
W3C URI happens lazily at crypto
+        // call-sites via the private resolvedXxx() helpers.  The 
encrypt/decrypt at the end of
+        // this method is the real functional validation.
 
         context.addRoutes(new RouteBuilder() {
             public void configure() {
                 from("direct:start")
                         .marshal(df).to("mock:encrypted")
-                        .log("Body: + ${body}")
+                        .log("Body: ${body}")
                         .unmarshal(df).to("mock:decrypted");
             }
         });
@@ -135,14 +135,13 @@ class XmlSecurityConstantNameTest extends 
CamelTestSupport {
         df.setSecureTagContents(true);
         df.setSecureTag("//cheesesites/italy/cheese");
         df.setXmlCipherAlgorithm("AES_128");
-
-        assertEquals(XMLCipher.AES_128, df.getXmlCipherAlgorithm());
+        // Lazy resolution — getter returns the constant name; functional 
validation is below.
 
         context.addRoutes(new RouteBuilder() {
             public void configure() {
                 from("direct:start")
                         .marshal(df).to("mock:encrypted")
-                        .log("Body: + ${body}")
+                        .log("Body: ${body}")
                         .unmarshal(df).to("mock:decrypted");
             }
         });
@@ -160,9 +159,7 @@ class XmlSecurityConstantNameTest extends CamelTestSupport {
         sendingDataFormat.setXmlCipherAlgorithm("AES_128");      // constant 
name
         sendingDataFormat.setKeyCipherAlgorithm("RSA_OAEP");     // constant 
name
         sendingDataFormat.setRecipientKeyAlias("recipient");
-
-        assertEquals(XMLCipher.AES_128, 
sendingDataFormat.getXmlCipherAlgorithm());
-        assertEquals(XMLCipher.RSA_OAEP, 
sendingDataFormat.getKeyCipherAlgorithm());
+        // Lazy resolution — getters return constant names; functional 
validation is the encrypt/decrypt below.
 
         KeyStoreParameters tsParameters = new KeyStoreParameters();
         tsParameters.setPassword("password");
@@ -173,8 +170,7 @@ class XmlSecurityConstantNameTest extends CamelTestSupport {
         receivingDataFormat.setKeyCipherAlgorithm("RSA_OAEP");   // constant 
name
         receivingDataFormat.setRecipientKeyAlias("recipient");
         receivingDataFormat.setSecureTag("//cheesesites/italy/cheese");
-
-        assertEquals(XMLCipher.RSA_OAEP, 
receivingDataFormat.getKeyCipherAlgorithm());
+        // Lazy resolution — getter returns constant name; functional 
validation is the encrypt/decrypt below.
 
         KeyStoreParameters ksParameters = new KeyStoreParameters();
         ksParameters.setPassword("password");
@@ -185,7 +181,7 @@ class XmlSecurityConstantNameTest extends CamelTestSupport {
             public void configure() {
                 from("direct:start")
                         .marshal(sendingDataFormat).to("mock:encrypted")
-                        .log("Body: + ${body}")
+                        .log("Body: ${body}")
                         .unmarshal(receivingDataFormat).to("mock:decrypted");
             }
         });
@@ -205,11 +201,7 @@ class XmlSecurityConstantNameTest extends CamelTestSupport 
{
         sendingDataFormat.setDigestAlgorithm("SHA256");          // constant 
name
         sendingDataFormat.setMgfAlgorithm("MGF1_SHA256");        // constant 
name
         sendingDataFormat.setRecipientKeyAlias("recipient");
-
-        assertEquals(XMLCipher.AES_128, 
sendingDataFormat.getXmlCipherAlgorithm());
-        assertEquals(XMLCipher.RSA_OAEP_11, 
sendingDataFormat.getKeyCipherAlgorithm());
-        assertEquals(XMLCipher.SHA256, sendingDataFormat.getDigestAlgorithm());
-        assertEquals(EncryptionConstants.MGF1_SHA256, 
sendingDataFormat.getMgfAlgorithm());
+        // Lazy resolution — getters return constant names; functional 
validation is the encrypt/decrypt below.
 
         KeyStoreParameters tsParameters = new KeyStoreParameters();
         tsParameters.setPassword("password");
@@ -232,7 +224,7 @@ class XmlSecurityConstantNameTest extends CamelTestSupport {
             public void configure() {
                 from("direct:start")
                         .marshal(sendingDataFormat).to("mock:encrypted")
-                        .log("Body: + ${body}")
+                        .log("Body: ${body}")
                         .unmarshal(receivingDataFormat).to("mock:decrypted");
             }
         });

Reply via email to