[ 
https://issues.apache.org/jira/browse/CAMEL-23067?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Reto Peter updated CAMEL-23067:
-------------------------------
    Priority: Major  (was: Minor)

> Async MDN signature verification fails — receipt API modifies raw body bytes 
> before signature validation
> --------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-23067
>                 URL: https://issues.apache.org/jira/browse/CAMEL-23067
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-as2
>    Affects Versions: 4.18.0
>            Reporter: Reto Peter
>            Priority: Major
>
> When receiving async MDNs via the Camel AS2 receipt API 
> (\{{as2://receipt/receive}}), the HTTP body is parsed and processed by 
> Camel's MIME parser before the application can access it. During parsing, the 
> original byte representation is modified (header
>   folding, whitespace normalization, line ending changes). This means the 
> exact bytes that were signed by the sender can no longer be reconstructed, 
> causing S/MIME signature verification to fail with a digest mismatch.
>   This affects all signed async MDNs. The signature was computed by the 
> sender over the exact raw HTTP body bytes. If even a single byte changes 
> (e.g., a whitespace or line ending normalization), the digest no longer 
> matches and the signature is invalid.
>   \{panel}
>   h3. Background
>   In the AS2 protocol, when async MDN is configured:
>   Sender sends an AS2 message to the receiver
>   Receiver returns HTTP 200 immediately (no MDN in response)
>   Receiver processes the message and sends the MDN back to the sender via a 
> separate HTTP POST to the sender's MDN callback URL
>   Sender receives the async MDN and validates the signature to confirm it was 
> not tampered with
>   The async MDN is typically a \{{multipart/signed}} MIME message. The 
> signature covers the exact bytes of the MDN body. To verify the signature, 
> the receiver (in this case Camel) must have access to the exact raw bytes of 
> the HTTP body as they arrived over the
>   wire.
>   h3. Root Cause
>   Camel's AS2 receipt API endpoint (\{{as2://receipt/receive}}) uses 
> HttpCore5's \{{HttpService}} to receive the HTTP request. The request body is 
> parsed through Camel's \{{EntityParser}} which processes the MIME structure. 
> During this parsing:
>   - MIME headers may be folded/unfolded differently
>   - Whitespace may be normalized
>   - Line endings may be changed (\{{\r\n}} vs \{{\n}})
>   - The parsed entity's \{{writeTo()}} output differs from the original raw 
> bytes
>   By the time the application's route processor receives the exchange, the 
> body is already a parsed \{{MultipartSignedEntity}} or 
> \{{DispositionNotificationMultipartReportEntity}} object — the original raw 
> bytes are lost. When attempting S/MIME signature
>   verification on these parsed objects, the digest computed over the 
> re-serialized bytes does not match the digest in the signature.
>   h3. Steps to Reproduce
>   Configure a Camel AS2 server with async MDN enabled (separate MDN callback 
> URL)
>   Send a message to a partner that returns signed async MDNs (e.g., OpenAS2)
>   Receive the async MDN via the Camel receipt API route
>   Attempt to verify the MDN signature
>   Observe: \{{CMSSignerDigestMismatchException}} — the digest computed over 
> the parsed/re-serialized body does not match the digest in the signature
>   h3. Expected Behavior
>   The Camel AS2 receipt API should provide access to the raw HTTP body bytes 
> before MIME parsing, so that signature verification can be performed on the 
> original bytes.
>   h3. Proposed Fix
>   Provide a mechanism to capture the raw HTTP body bytes before any parsing 
> occurs. This could be implemented as:
>   Option A: Raw body capture in HttpCore5 handler
>   Intercept the HTTP request body at the \{{HttpService}} / 
> \{{BasicHttpServerRequestHandler}} level, capture the raw bytes into a 
> buffer, and expose them via an exchange property (e.g., 
> \{{CamelAs2.rawMdnBody}}) alongside the parsed entity.
>   \{code:java}
>   // In the request handler, before parsing:
>   byte[] rawBody = inputStream.readAllBytes();
>   httpContext.setAttribute("CamelAs2.rawMdnBody", rawBody);
>   // Then parse from the captured bytes:
>   InputStream parseStream = new ByteArrayInputStream(rawBody);
>   // ... continue with existing MIME parsing ...
>   \{code}
>   Option B: Expose raw body on the exchange
>   After Camel processes the receipt, set the raw bytes as an exchange 
> property:
>   \{code:java}
>   exchange.setProperty("CamelAs2.asyncMdnRawBody", rawBodyBytes);
>   \{code}
>   This would allow application code to verify the signature using the 
> original bytes:
>   \{code:java}
>   byte[] rawBody = exchange.getProperty("CamelAs2.asyncMdnRawBody", 
> byte[].class);
>   // Use rawBody for S/MIME signature verification via BouncyCastle 
> SMIMESignedParser
>   \{code}
>   h3. Our Workaround
>   We had to bypass Camel's receipt API entirely and implement a Spring 
> \{{@RestController}} that receives async MDNs on the same HTTP port. We use a 
> servlet filter (\{{RawBodyCaptureFilter}}) to capture the raw HTTP body bytes 
> before any processing, then perform
>   signature verification manually using BouncyCastle's \{{SMIMESignedParser}} 
> on the captured raw bytes.
>   This works but requires:
>   - A separate Spring controller duplicating MDN processing logic
>   - A servlet filter to intercept and cache the raw request body
>   - Manual MIME parsing and MDN field extraction (Original-Message-ID, 
> Disposition, Received-Content-MIC)
>   - Manual signature verification via BouncyCastle
>   The entire Camel receipt API with its built-in MDN processing, signature 
> handling, and exchange properties is unused because the raw body is not 
> accessible.
>   h3. Test Scenario
>   Tested with OpenAS2 sending signed async MDNs.
>   Via Camel receipt API: \{{CMSSignerDigestMismatchException}} — signature 
> verification always fails because the re-serialized body bytes differ from 
> the original signed bytes.
>   Via Spring controller with raw body capture: Signature verification passes 
> because we use the exact bytes the sender signed.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to