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

gnodet pushed a commit to branch fix/as2-issues
in repository https://gitbox.apache.org/repos/asf/camel.git

commit b8432cd0ef169b947c4f857ad3b77cbe3e1888f5
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Mar 9 11:04:39 2026 +0100

    CAMEL-23068: Fix CRLF line endings in MDN message template
    
    Change DEFAULT_MDN_MESSAGE_TEMPLATE from Java text block (LF line
    endings) to string concatenation with explicit CRLF. This ensures
    consistent digest computation for signed MDNs, where headers are
    written through CanonicalOutputStream (CRLF) but body content was
    written directly with LF-only line endings.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../component/as2/api/protocol/ResponseMDN.java    | 22 +++---
 .../as2/api/protocol/ResponseMDNTest.java          | 79 ++++++++++++++++++++++
 2 files changed, 91 insertions(+), 10 deletions(-)

diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
index 852ff8e3c0d9..59ad21bf618e 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
@@ -73,16 +73,18 @@ public class ResponseMDN implements HttpResponseInterceptor 
{
 
     public static final String DISPOSITION_MODIFIER = "Disposition-Modifier";
 
-    private static final String DEFAULT_MDN_MESSAGE_TEMPLATE = """
-            MDN for -
-             Message ID: $requestHeaders["Message-Id"]
-              Subject: $requestHeaders["Subject"]
-              Date: $requestHeaders["Date"]
-              From: $requestHeaders["AS2-From"]
-              To: $requestHeaders["AS2-To"]
-              Received on: $responseHeaders["Date"]
-             Status: $dispositionType
-            """;
+    // Use explicit CRLF line endings to ensure consistent digest computation 
for signed MDNs.
+    // Java text blocks use LF, which causes CRLF/LF mismatch when headers are 
written through
+    // CanonicalOutputStream (CRLF) but body content is written directly (LF).
+    private static final String DEFAULT_MDN_MESSAGE_TEMPLATE
+            = "MDN for -\r\n"
+              + " Message ID: $requestHeaders[\"Message-Id\"]\r\n"
+              + "  Subject: $requestHeaders[\"Subject\"]\r\n"
+              + "  Date: $requestHeaders[\"Date\"]\r\n"
+              + "  From: $requestHeaders[\"AS2-From\"]\r\n"
+              + "  To: $requestHeaders[\"AS2-To\"]\r\n"
+              + "  Received on: $responseHeaders[\"Date\"]\r\n"
+              + " Status: $dispositionType\r\n";
 
     private static final Logger LOG = 
LoggerFactory.getLogger(ResponseMDN.class);
 
diff --git 
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/protocol/ResponseMDNTest.java
 
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/protocol/ResponseMDNTest.java
new file mode 100644
index 000000000000..2267ab687dd7
--- /dev/null
+++ 
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/protocol/ResponseMDNTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.as2.api.protocol;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.component.as2.api.entity.TextPlainEntity;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ResponseMDNTest {
+
+    @Test
+    void mdnTextPlainEntityWithCrlfProducesConsistentDigest() throws 
IOException {
+        // Simulate MDN text with explicit CRLF (as in the fixed 
DEFAULT_MDN_MESSAGE_TEMPLATE)
+        String mdnText = "MDN for -\r\n"
+                         + " Message ID: test-id\r\n"
+                         + " Status: processed\r\n";
+
+        TextPlainEntity entity = new TextPlainEntity(mdnText, 
StandardCharsets.US_ASCII.name(), "7bit", false);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        entity.writeTo(out);
+        byte[] output = out.toByteArray();
+
+        // Verify that the body content preserves CRLF
+        String outputStr = new String(output, StandardCharsets.US_ASCII);
+        // The body part (after headers + blank line) should contain CRLF
+        String body = outputStr.substring(outputStr.indexOf("\r\n\r\n") + 4);
+        assertTrue(body.contains("\r\n"), "MDN body should contain CRLF line 
endings");
+        assertFalse(body.contains("\n") && !body.contains("\r\n"),
+                "MDN body should not contain bare LF (without CR)");
+    }
+
+    @Test
+    void mdnTextPlainEntityWithLfProducesMismatch() throws IOException {
+        // Simulate MDN text with LF only (the old broken 
DEFAULT_MDN_MESSAGE_TEMPLATE behavior)
+        String mdnText = "MDN for -\n"
+                         + " Message ID: test-id\n"
+                         + " Status: processed\n";
+
+        TextPlainEntity entity = new TextPlainEntity(mdnText, 
StandardCharsets.US_ASCII.name(), "7bit", false);
+
+        ByteArrayOutputStream out1 = new ByteArrayOutputStream();
+        entity.writeTo(out1);
+
+        // Now create with CRLF
+        String mdnTextCrlf = "MDN for -\r\n"
+                             + " Message ID: test-id\r\n"
+                             + " Status: processed\r\n";
+
+        TextPlainEntity entityCrlf = new TextPlainEntity(mdnTextCrlf, 
StandardCharsets.US_ASCII.name(), "7bit", false);
+
+        ByteArrayOutputStream out2 = new ByteArrayOutputStream();
+        entityCrlf.writeTo(out2);
+
+        // Headers should be identical (both go through CanonicalOutputStream)
+        // but body content will differ in line endings
+        assertNotEquals(out1.size(), out2.size(),
+                "LF and CRLF bodies should produce different byte lengths");
+    }
+}

Reply via email to