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 dba42d79f80a9ed1ac54b76e14a28efd4e1c3cbc
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Mar 9 11:04:33 2026 +0100

    CAMEL-23067: Capture raw request body for signature verification
    
    Store the raw (unparsed) request body bytes in AS2BHttpServerConnection
    before MIME parsing. This allows downstream handlers to verify
    signatures against the original bytes, which may differ from
    re-serialized entities due to header folding and whitespace
    normalization.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../as2/api/io/AS2BHttpServerConnection.java       | 38 ++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpServerConnection.java
 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpServerConnection.java
index 8da0903c6979..ddd61ef68cb5 100644
--- 
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpServerConnection.java
+++ 
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpServerConnection.java
@@ -16,22 +16,37 @@
  */
 package org.apache.camel.component.as2.api.io;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CharsetEncoder;
 
 import org.apache.camel.component.as2.api.entity.EntityParser;
+import org.apache.camel.component.as2.api.util.EntityUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ContentLengthStrategy;
+import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.config.Http1Config;
 import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection;
 import org.apache.hc.core5.http.io.HttpMessageParserFactory;
 import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
+import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class AS2BHttpServerConnection extends DefaultBHttpServerConnection {
 
+    private static final Logger LOG = 
LoggerFactory.getLogger(AS2BHttpServerConnection.class);
+
+    /**
+     * The raw (unparsed) request body bytes captured before MIME parsing. 
This allows downstream handlers to perform
+     * signature verification against the original bytes, which may differ 
from the re-serialized parsed entity due to
+     * MIME header folding, whitespace normalization, and line ending changes.
+     */
+    private byte[] rawRequestBody;
+
     public AS2BHttpServerConnection(Http1Config http1Config) {
         this(http1Config, null, null);
     }
@@ -53,9 +68,32 @@ public class AS2BHttpServerConnection extends 
DefaultBHttpServerConnection {
               outgoingContentStrategy, requestParserFactory, 
responseWriterFactory);
     }
 
+    /**
+     * Returns the raw (unparsed) request body bytes captured before MIME 
parsing.
+     *
+     * @return the raw body bytes, or null if no entity was received
+     */
+    public byte[] getRawRequestBody() {
+        return rawRequestBody;
+    }
+
     @Override
     public void receiveRequestEntity(ClassicHttpRequest request) throws 
HttpException, IOException {
         super.receiveRequestEntity(request);
+        // Capture raw body bytes before parsing modifies them.
+        // This is essential for signature verification of async MDNs, where 
the parsed
+        // MIME entity may have different bytes due to header folding and 
whitespace normalization.
+        if (request.getEntity() != null) {
+            try {
+                rawRequestBody = EntityUtils.getContent(request.getEntity());
+                // Reset the entity with the captured bytes so it can be parsed
+                ContentType contentType = 
ContentType.parse(request.getEntity().getContentType());
+                request.setEntity(
+                        new BasicHttpEntity(new 
ByteArrayInputStream(rawRequestBody), rawRequestBody.length, contentType));
+            } catch (Exception e) {
+                LOG.debug("Failed to capture raw request body bytes", e);
+            }
+        }
         EntityParser.parseAS2MessageEntity(request);
     }
 

Reply via email to