You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2018/04/19 07:27:10 UTC

[camel] branch master updated: [CAMEL-11257] First Phase of AS2 Camel Component (#2300)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 486bae5  [CAMEL-11257] First Phase of AS2 Camel Component (#2300)
486bae5 is described below

commit 486bae5cda200f4c8b0e74f18ed25af3b4bbb988
Author: William Collins <wc...@redhat.com>
AuthorDate: Thu Apr 19 03:27:06 2018 -0400

    [CAMEL-11257] First Phase of AS2 Camel Component (#2300)
    
    * [CAMEL-11257] Initial commit AS2 Component
    
    * [CAMEL-11257] Refactored connection logic into
    AS2 client and server connection objects
    
    * Updated Camel version for AS2 Component
    
    * [CAMEL-11257] Updated Integration Tests
    
    * [ENTESB-6202] Refactored client and server connection
    code and added api unit test.
    
    * [CAMEL-11257] Added unit test
    
    * [CAMEL-11257] Updated api layer to receive parameters via HTTP context.
    
    * [CAMEL-11257] Updated AS2MessageTest
    
    * [CAMEL-11257] Updated unit tests
    
    * [CAMEL-11257] Updated Client Send Test, refactored Application EDI Entity parsing and added partial implementation of Multipart Signed Entity parsing.
    
    * [CAMEL-11257] Partial implementation of signature validation
    
    * [CAMEL-11257] Fixes and testing of signature validation
    
    * [CAMEL-11257] Fixes for signature validation
    
    * [CAMEL-11257] Refactored Entity parsing logic
    
    * [CAMEL-11257] Updated tests and configuration.
    
    * [CAMEL-11257] Updated component tests
    
    * [CAMEL-11257] Refactored ConnectionHelper and updated Server Manager Integration test.
    
    * [CAMEL-11257] Updated Server Manager integration tests.
    
    * [CAMEL-11257] Added MDN request protocol support
    
    * [CAMEL-11257] Initial work on MDN entities
    
    * [CAMEL-11257] Continuing work on MDN entities
    
    * [CAMEL-11257] Continuing work on MDN entities
    
    * [CAMEL-11257] Continuing work on MDN entities
    
    * [CAMEL-11257] Continuing work on MDN entities
    
    * [CAMEL-11257] Continuing work on MDN entities
    
    * [CAMEL-11257] Added unit test.
    
    * [CAMEL-11257] Added and updated unit tests.
    
    * [CAMEL-11257] Adding MDN response handling to AS2 Server connections
    
    * [CAMEL-11257] Further work on MDN response handling for AS2 Server connections
    
    * [CAMEL-11257] Further work on MDN response handling for AS2 Server connections and refactoring
    
    * [CAMEL-11257] Further work on MDN response handling for AS2 Server connections
    
    * [CAMEL-11257] Added MDN entity parsing logic
    
    * [CAMEL-11257] Updated Disposition Notification Content Utils Tests
    
    * [CAMEL-11257] Refactored MDN parsing logic
    
    * [CAMEL-11257] Added Entity Parser Test
    
    * [CAMEL-11257] Updating component integration tests
    
    * [CAMEL-11257] Updating component integration tests
    
    * [CAMEL-11257] Updating component integration tests
    
    * [CAMEL-11257] Fixed unit test.
    
    * [CAMEL-11257] Further refactoring
    
    * [CAMEL-11257] Further refactoring
    
    * [CAMEL-11257] Further refactoring
    
    * [CAMEL-11257] Moved HTTP connection class to io package.
    
    * [CAMEL-11257] Adding initial code for signed receipts
    
    * [CAMEL-11257] Added code for signed receipts
    
    * [CAMEL-11257] Clean up after rebase with upstream.
    
    * [CAMEL-11257] Checkstyle clean up
    
    * [CAMEL-11257] Updated poms
    
    * [CAMEL-11257] Added comments as placeholders for future code.
---
 components/camel-as2/camel-as2-api/keystore.pfx    | Bin 0 -> 4536 bytes
 components/camel-as2/camel-as2-api/pom.xml         | 132 ++++
 .../apache/camel/component/as2/api/AS2Charset.java |  30 +
 .../component/as2/api/AS2ClientConnection.java     | 103 +++
 .../camel/component/as2/api/AS2ClientManager.java  | 265 +++++++
 .../camel/component/as2/api/AS2Constants.java      |  90 +++
 .../apache/camel/component/as2/api/AS2Header.java  | 116 +++
 .../camel/component/as2/api/AS2MediaType.java      |  45 ++
 .../component/as2/api/AS2MessageStructure.java     |  44 ++
 .../camel/component/as2/api/AS2MicAlgorithm.java   |  69 ++
 .../camel/component/as2/api/AS2MimeType.java       |  57 ++
 .../camel/component/as2/api/AS2ReportType.java     |  26 +
 .../component/as2/api/AS2ServerConnection.java     | 217 ++++++
 .../camel/component/as2/api/AS2ServerManager.java  |  86 +++
 .../component/as2/api/AS2SignedDataGenerator.java  | 191 +++++
 .../component/as2/api/AS2TransferEncoding.java     |  23 +
 .../component/as2/api/CanonicalOutputStream.java   |  80 ++
 .../component/as2/api/InvalidAS2NameException.java |  73 ++
 .../apache/camel/component/as2/api/MDNField.java   |  66 ++
 .../org/apache/camel/component/as2/api/Util.java   | 183 +++++
 .../as2/api/entity/AS2DispositionModifier.java     |  91 +++
 .../as2/api/entity/AS2DispositionType.java         |  48 ++
 .../AS2MessageDispositionNotificationEntity.java   | 262 +++++++
 .../api/entity/ApplicationEDIConsentEntity.java    |  29 +
 .../as2/api/entity/ApplicationEDIEntity.java       |  64 ++
 .../as2/api/entity/ApplicationEDIFACTEntity.java   |  29 +
 .../as2/api/entity/ApplicationEDIX12Entity.java    |  29 +
 .../entity/ApplicationPkcs7SignatureEntity.java    | 125 +++
 .../component/as2/api/entity/DispositionMode.java  |  60 ++
 ...spositionNotificationMultipartReportEntity.java | 101 +++
 .../api/entity/DispositionNotificationOptions.java |  39 +
 .../DispositionNotificationOptionsParser.java      |  69 ++
 .../component/as2/api/entity/EntityParser.java     | 854 +++++++++++++++++++++
 .../camel/component/as2/api/entity/Importance.java |  53 ++
 .../camel/component/as2/api/entity/MimeEntity.java | 277 +++++++
 .../as2/api/entity/MultipartMimeEntity.java        | 121 +++
 .../as2/api/entity/MultipartReportEntity.java      |  39 +
 .../as2/api/entity/MultipartSignedEntity.java      | 109 +++
 .../component/as2/api/entity/TextPlainEntity.java  |  66 ++
 .../as2/api/io/AS2BHttpClientConnection.java       |  64 ++
 .../as2/api/io/AS2BHttpServerConnection.java       |  66 ++
 .../as2/api/io/AS2SessionInputBuffer.java          | 364 +++++++++
 .../component/as2/api/protocol/RequestAS2.java     |  84 ++
 .../component/as2/api/protocol/RequestMDN.java     |  62 ++
 .../component/as2/api/protocol/ResponseMDN.java    | 181 +++++
 .../component/as2/api/util/AS2HeaderUtils.java     | 165 ++++
 .../component/as2/api/util/ContentTypeUtils.java   |  50 ++
 .../util/DispositionNotificationContentUtils.java  | 300 ++++++++
 .../camel/component/as2/api/util/EntityUtils.java  | 247 ++++++
 .../component/as2/api/util/HttpMessageUtils.java   | 108 +++
 .../camel/component/as2/api/util/MicUtils.java     | 151 ++++
 .../camel/component/as2/api/util/SigningUtils.java |  92 +++
 .../camel/component/as2/api/AS2MessageTest.java    | 367 +++++++++
 .../org/apache/camel/component/as2/api/Utils.java  |  83 ++
 .../DispositionNotificationOptionsParserTest.java  |  53 ++
 .../component/as2/api/entity/EntityParserTest.java | 239 ++++++
 .../component/as2/api/util/AS2HeaderUtilsTest.java |  55 ++
 .../DispositionNotificationContentUtilsTest.java   | 106 +++
 .../camel/component/as2/api/util/MicUtilsTest.java | 111 +++
 .../src/test/resources/log4j2.properties           |  23 +
 components/camel-as2/camel-as2-component/pom.xml   | 268 +++++++
 .../src/main/java/META-INF/MANIFEST.MF             |   3 +
 .../apache/camel/component/as2/AS2Component.java   |  59 ++
 .../camel/component/as2/AS2Configuration.java      | 432 +++++++++++
 .../apache/camel/component/as2/AS2Consumer.java    | 116 +++
 .../apache/camel/component/as2/AS2Endpoint.java    | 162 ++++
 .../apache/camel/component/as2/AS2Producer.java    |  31 +
 .../as2/internal/AS2ConnectionHelper.java          |  73 ++
 .../camel/component/as2/internal/AS2Constants.java |  29 +
 .../as2/internal/AS2PropertiesHelper.java          |  39 +
 .../services/org/apache/camel/component/as2        |   1 +
 .../as2/AS2ClientManagerIntegrationTest.java       | 210 +++++
 .../as2/AS2ServerManagerIntegrationTest.java       | 310 ++++++++
 .../component/as2/AbstractAS2TestSupport.java      |  82 ++
 .../java/org/apache/camel/component/as2/Utils.java |  83 ++
 .../src/test/resources/log4j2.properties           |  23 +
 .../src/test/resources/test-options.properties     |  36 +
 components/camel-as2/pom.xml                       |  48 ++
 components/pom.xml                                 |   1 +
 platforms/spring-boot/components-starter/pom.xml   |   1 +
 80 files changed, 9339 insertions(+)

diff --git a/components/camel-as2/camel-as2-api/keystore.pfx b/components/camel-as2/camel-as2-api/keystore.pfx
new file mode 100644
index 0000000..d3804d6
Binary files /dev/null and b/components/camel-as2/camel-as2-api/keystore.pfx differ
diff --git a/components/camel-as2/camel-as2-api/pom.xml b/components/camel-as2/camel-as2-api/pom.xml
new file mode 100644
index 0000000..de1c893
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.camel</groupId>
+		<artifactId>camel-as2-parent</artifactId>
+		<version>2.22.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>camel-as2-api</artifactId>
+	<name>Camel :: AS2 :: API</name>
+	<description>Camel AS2 API</description>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpcore</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpmime</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.bouncycastle</groupId>
+			<artifactId>bcprov-debug-jdk15on</artifactId>
+			<version>1.58</version>
+		</dependency>
+		<dependency>
+			<groupId>org.bouncycastle</groupId>
+			<artifactId>bcpkix-jdk15on</artifactId>
+			<version>1.58</version>
+		</dependency>
+
+		<!-- logging -->
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-slf4j-impl</artifactId>
+		</dependency>
+
+		<!-- testing -->
+		<dependency>
+			<groupId>org.apache.camel</groupId>
+			<artifactId>camel-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<defaultGoal>install</defaultGoal>
+
+		<plugins>
+
+			<!-- to generate API Javadoc -->
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-javadoc-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>add-javadoc</id>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+						<configuration>
+							<attach>true</attach>
+							<source>1.8</source>
+							<quiet>true</quiet>
+							<detectOfflineLinks>false</detectOfflineLinks>
+							<javadocVersion>1.7</javadocVersion>
+							<encoding>UTF-8</encoding>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+		</plugins>
+	</build>
+
+	<!-- Disable Java 8 doclint checks to avoid Javadoc plugin failures -->
+	<profiles>
+		<profile>
+			<id>doclint-java8-disable</id>
+			<activation>
+				<jdk>[1.8,</jdk>
+			</activation>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-javadoc-plugin</artifactId>
+						<configuration>
+							<additionalparam>-Xdoclint:none</additionalparam>
+						</configuration>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
+</project>
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Charset.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Charset.java
new file mode 100644
index 0000000..2b43afd
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Charset.java
@@ -0,0 +1,30 @@
+/**
+ * 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;
+
+public interface AS2Charset {
+    
+    /**
+     * Name of charset parameter in Content-Type header values.
+     */
+    public static final String PARAM = "charset";
+    
+    /**
+     * Character Set Name for US ASCII
+     */
+    public static final String US_ASCII = "US-ASCII";
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientConnection.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientConnection.java
new file mode 100644
index 0000000..672f25d
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientConnection.java
@@ -0,0 +1,103 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.camel.component.as2.api.io.AS2BHttpClientConnection;
+import org.apache.camel.component.as2.api.protocol.RequestAS2;
+import org.apache.camel.component.as2.api.protocol.RequestMDN;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.impl.DefaultBHttpClientConnection;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpProcessorBuilder;
+import org.apache.http.protocol.HttpRequestExecutor;
+import org.apache.http.protocol.RequestConnControl;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestDate;
+import org.apache.http.protocol.RequestExpectContinue;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.Args;
+
+public class AS2ClientConnection {
+    
+    private HttpHost targetHost;
+    private HttpProcessor httpProcessor;
+    private DefaultBHttpClientConnection httpConnection;
+    private String as2Version;
+    private String userAgent;
+    private String clientFqdn;
+    
+    public AS2ClientConnection(String as2Version, String userAgent, String clientFqdn, String targetHostName, Integer targetPortNumber) throws UnknownHostException, IOException {
+
+        this.as2Version = Args.notNull(as2Version, "as2Version");
+        this.userAgent = Args.notNull(userAgent, "userAgent");
+        this.clientFqdn = Args.notNull(clientFqdn, "clientFqdn");
+        this.targetHost = new HttpHost(Args.notNull(targetHostName, "targetHostName"), Args.notNull(targetPortNumber, "targetPortNumber"));
+                
+        // Build Processor
+        httpProcessor = HttpProcessorBuilder.create()
+                .add(new RequestAS2(as2Version, clientFqdn))
+                .add(new RequestMDN())
+                .add(new RequestTargetHost())
+                .add(new RequestUserAgent(this.userAgent))
+                .add(new RequestDate())
+                .add(new RequestContent(true))
+                .add(new RequestConnControl())
+                .add(new RequestExpectContinue(true)).build();
+        
+        // Create Socket
+        Socket socket = new Socket(targetHost.getHostName(), targetHost.getPort());
+
+        // Create Connection
+        httpConnection = new AS2BHttpClientConnection(8 * 1024);
+        httpConnection.bind(socket);
+    }
+    
+    public String getAs2Version() {
+        return as2Version;
+    }
+
+    public String getUserAgent() {
+        return userAgent;
+    }
+
+    public String getClientFqdn() {
+        return clientFqdn;
+    }
+
+    public HttpResponse send(HttpRequest request, HttpCoreContext httpContext) throws HttpException, IOException {
+        
+        httpContext.setTargetHost(targetHost);
+
+        // Execute Request
+        HttpRequestExecutor httpexecutor = new HttpRequestExecutor();
+        httpexecutor.preProcess(request, httpProcessor, httpContext);
+        HttpResponse response = httpexecutor.execute(request, httpConnection, httpContext);   
+        httpexecutor.postProcess(response, httpProcessor, httpContext);
+
+        return response;
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java
new file mode 100644
index 0000000..8d62876
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java
@@ -0,0 +1,265 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+import org.apache.camel.component.as2.api.entity.ApplicationEDIEntity;
+import org.apache.camel.component.as2.api.entity.EntityParser;
+import org.apache.camel.component.as2.api.entity.MultipartSignedEntity;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.camel.component.as2.api.util.SigningUtils;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.util.Args;
+
+/**
+ * AS2 Client Manager
+ * 
+ * <p>
+ * Sends EDI Messages over HTTP
+ *
+ */
+public class AS2ClientManager {
+
+    //
+    // AS2 HTTP Context Attribute Keys
+    //
+
+    /**
+     * Prefix for all AS2 HTTP Context Attributes used by the Http Client
+     * Manager.
+     */
+    public static final String CAMEL_AS2_CLIENT_PREFIX = "camel-as2.client";
+
+    /**
+     * The HTTP Context Attribute indicating the AS2 message structure to be sent.
+     */
+    public static final String AS2_MESSAGE_STRUCTURE = CAMEL_AS2_CLIENT_PREFIX + "as2-message-structure";
+    
+    /**
+     * The HTTP Context Attribute indicating the EDI message content type to be sent.
+     */
+    public static final String EDI_MESSAGE_CONTENT_TYPE = CAMEL_AS2_CLIENT_PREFIX + "edi-message-content-type";
+    
+    /**
+     * The HTTP Context Attribute indicating the EDI message transfer encoding to be sent.
+     */
+    public static final String EDI_MESSAGE_TRANSFER_ENCODING = CAMEL_AS2_CLIENT_PREFIX + "edi-message-transfer-encoding";
+    
+    /**
+     * The HTTP Context Attribute containing the HTTP request message
+     * transporting the EDI message
+     */
+    public static final String HTTP_REQUEST = HttpCoreContext.HTTP_REQUEST;
+
+    /**
+     * The HTTP Context Attribute containing the HTTP response message
+     * transporting the EDI message
+     */
+    public static final String HTTP_RESPONSE = HttpCoreContext.HTTP_RESPONSE;
+
+    /**
+     * The HTTP Context Attribute containing the AS2 Connection used to send
+     * request message.
+     */
+    public static final String AS2_CONNECTION = CAMEL_AS2_CLIENT_PREFIX + "as2-connection";
+
+    /**
+     * The HTTP Context Attribute containing the request URI identifying the
+     * process on the receiving system responsible for unpacking and handling of
+     * message data and generating a reply for the sending system that contains
+     * a Message Disposition Acknowledgement (MDN)
+     */
+    public static final String REQUEST_URI = CAMEL_AS2_CLIENT_PREFIX + "request-uri";
+
+    /**
+     * The HTTP Context Attribute containing the subject header sent in an AS2
+     * message.
+     */
+    public static final String SUBJECT = CAMEL_AS2_CLIENT_PREFIX + "subject";
+
+    /**
+     * The HTTP Context Attribute containing the internet e-mail address of
+     * sending system
+     */
+    public static final String FROM = CAMEL_AS2_CLIENT_PREFIX + "from";
+
+    /**
+     * The HTTP Context Attribute containing the AS2 System Identifier of the
+     * sending system
+     */
+    public static final String AS2_FROM = CAMEL_AS2_CLIENT_PREFIX + "as2-from";
+
+    /**
+     * The HTTP Context Attribute containing the AS2 System Identifier of the
+     * receiving system
+     */
+    public static final String AS2_TO = CAMEL_AS2_CLIENT_PREFIX + "as2-to";
+
+    /**
+     * The HTTP Context Attribute containing the algorithm name used to sign EDI
+     * message
+     */
+    public static final String SIGNING_ALGORITHM_NAME = CAMEL_AS2_CLIENT_PREFIX + "signing-algorithm-name";
+
+    /**
+     * The HTTP Context Attribute containing the certificate chain used to sign
+     * EDI message
+     */
+    public static final String SIGNING_CERTIFICATE_CHAIN = CAMEL_AS2_CLIENT_PREFIX + "signing-certificate-chain";
+
+    /**
+     * The HTTP Context Attribute containing the private key used to sign EDI
+     * message
+     */
+    public static final String SIGNING_PRIVATE_KEY = CAMEL_AS2_CLIENT_PREFIX + "signing-private-key";
+
+    /**
+     * The HTTP Context Attribute containing the internet e-mail address of
+     * sending system requesting a message disposition notification. 
+     */
+    public static final String DISPOSITION_NOTIFICATION_TO = CAMEL_AS2_CLIENT_PREFIX + "disposition-notification-to";
+
+    /**
+     * The HTTP Context Attribute containing the list of names of the requested MIC algorithms to be used 
+     * by the receiving system to construct a message disposition notification. 
+     */
+    public static final String SIGNED_RECEIPT_MIC_ALGORITHMS = CAMEL_AS2_CLIENT_PREFIX + "signed-receipt-mic-algorithms";
+
+    //
+
+    private AS2ClientConnection as2ClientConnection;
+
+    public AS2ClientManager(AS2ClientConnection as2ClientConnection) {
+        this.as2ClientConnection = as2ClientConnection;
+    }
+
+    /**
+     * Send <code>ediMessage</code> to trading partner.
+     * 
+     * @param ediMessage
+     *            - EDI message to transport
+     * @param httpContext
+     *            - the subject sent in the interchange request.
+     * @throws HttpException
+     */
+    public HttpCoreContext send(String ediMessage,
+                                String requestUri,
+                                String subject,
+                                String from,
+                                String as2From,
+                                String as2To,
+                                AS2MessageStructure as2MessageStructure,
+                                ContentType ediMessageContentType,
+                                String ediMessageTransferEncoding,
+                                Certificate[] signingCertificateChain,
+                                PrivateKey signingPrivateKey,
+                                String dispositionNotificationTo,
+                                String[] signedReceiptMicAlgorithms)
+            throws HttpException {
+        
+        Args.notNull(ediMessage, "EDI Message");
+        Args.notNull(as2MessageStructure, "AS2 Message Structure");
+        Args.notNull(requestUri, "Request URI");
+        Args.notNull(ediMessageContentType, "EDI Message Content Type");
+        
+        // Add Context attributes
+        HttpCoreContext httpContext = HttpCoreContext.create();
+        httpContext.setAttribute(AS2ClientManager.REQUEST_URI, requestUri);
+        httpContext.setAttribute(AS2ClientManager.SUBJECT, subject);
+        httpContext.setAttribute(AS2ClientManager.FROM, from);
+        httpContext.setAttribute(AS2ClientManager.AS2_FROM, as2From);
+        httpContext.setAttribute(AS2ClientManager.AS2_TO, as2To);
+        httpContext.setAttribute(AS2ClientManager.AS2_MESSAGE_STRUCTURE, as2MessageStructure);
+        httpContext.setAttribute(AS2ClientManager.EDI_MESSAGE_CONTENT_TYPE, ediMessageContentType);
+        httpContext.setAttribute(AS2ClientManager.EDI_MESSAGE_TRANSFER_ENCODING, ediMessageTransferEncoding);
+        httpContext.setAttribute(AS2ClientManager.SIGNING_CERTIFICATE_CHAIN, signingCertificateChain);
+        httpContext.setAttribute(AS2ClientManager.SIGNING_PRIVATE_KEY, signingPrivateKey);
+        httpContext.setAttribute(AS2ClientManager.DISPOSITION_NOTIFICATION_TO, dispositionNotificationTo);
+        httpContext.setAttribute(AS2ClientManager.SIGNED_RECEIPT_MIC_ALGORITHMS, signedReceiptMicAlgorithms);
+        
+        BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", requestUri);
+        httpContext.setAttribute(HTTP_REQUEST, request);
+
+        // Create Message Body
+        ApplicationEDIEntity applicationEDIEntity;
+        try {
+            applicationEDIEntity = EntityUtils.createEDIEntity(ediMessage, ediMessageContentType, ediMessageTransferEncoding, false);
+        } catch (Exception e) {
+            throw new HttpException("Failed to create EDI message entity", e);
+        }
+        switch (as2MessageStructure) {
+        case PLAIN:
+            applicationEDIEntity.setMainBody(true);
+            EntityUtils.setMessageEntity(request, applicationEDIEntity);
+            break;
+        case SIGNED:
+            AS2SignedDataGenerator gen = createSigningGenerator(httpContext);
+            // Create Multipart Signed Entity
+            try {
+                MultipartSignedEntity multipartSignedEntity = new MultipartSignedEntity(applicationEDIEntity, gen,
+                        AS2Charset.US_ASCII, AS2TransferEncoding.BASE64, true, null);
+                EntityUtils.setMessageEntity(request, multipartSignedEntity);
+            } catch (Exception e) {
+                throw new HttpException("Failed to sign message", e);
+            }
+            break;
+        case ENCRYPTED:
+            // TODO : Add code here to add application/pkcs7-mime entity when encryption facility available.
+            break;
+        case ENCRYPTED_SIGNED:
+            // TODO : Add code here to add application/pkcs7-mime entity when encryption facility available.
+            break;
+        default:
+            throw new HttpException("Unknown AS2 Message Structure");
+        }
+
+        HttpResponse response;
+        try {
+            httpContext.setAttribute(AS2_CONNECTION, as2ClientConnection);
+            response = as2ClientConnection.send(request, httpContext);
+            EntityParser.parseAS2MessageEntity(response);
+        } catch (IOException e) {
+            throw new HttpException("Failed to send http request message", e);
+        }
+        httpContext.setAttribute(HTTP_RESPONSE, response);
+        return httpContext;
+    }
+
+    public AS2SignedDataGenerator createSigningGenerator(HttpCoreContext httpContext) throws HttpException {
+
+        Certificate[] certificateChain = httpContext.getAttribute(SIGNING_CERTIFICATE_CHAIN, Certificate[].class);
+        if (certificateChain == null) {
+            throw new HttpException("Signing certificate chain missing");
+        }
+
+        PrivateKey privateKey = httpContext.getAttribute(SIGNING_PRIVATE_KEY, PrivateKey.class);
+        if (privateKey == null) {
+            throw new HttpException("Signing private key missing");
+        }
+        
+        return SigningUtils.createSigningGenerator(certificateChain, privateKey);
+
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Constants.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Constants.java
new file mode 100644
index 0000000..5a9faf8
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Constants.java
@@ -0,0 +1,90 @@
+/**
+ * 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;
+
+import org.apache.http.protocol.HttpCoreContext;
+
+/**
+ * Constants for AS2 component.
+ */
+public interface AS2Constants {
+    
+    /**
+     * The Value of User Agent Header used by AS2 Camel Component.
+     */
+    public static final String HTTP_USER_AGENT = "Camel AS2 Component";
+    
+    /**
+     * The Value of Origin Server Header used by AS2 Camel Component.
+     */
+    public static final String HTTP_ORIGIN_SERVER = "Camel AS2 Component";
+    
+    /**
+     * Fully Qualified Domain Name used by AS2 Camel Component in Message ID Header.
+     */
+    public static final String HTTP_MESSAGE_ID_FQDN = "camel.apache.org";
+    
+    /**
+     * The Value of User Agent Header used by AS2 Camel Component.
+     */
+    public static final String MIME_VERSION = "1.0";
+    
+    //
+    // HTTP Context Attribute Names
+    //
+
+    /**
+     * HTTP Context Attribute Name for HTTP Client Connection object stored in context.
+     */
+    public static final String HTTP_CLIENT_CONNECTION = HttpCoreContext.HTTP_CONNECTION;
+    
+    /**
+     * HTTP Context Attribute Name for HTTP Client Processor object stored in context.
+     */
+    public static final String HTTP_CLIENT_PROCESSOR = "http.processor";
+    
+    /**
+     * HTTP Context Attribute Name for HTTP Client Fully Qualified Domain Name (FQDN) stored in context.
+     */
+    public static final String HTTP_CLIENT_FQDN = "client.fqdn";
+    
+    /**
+     * HTTP Context Attribute Name for HTTP Server Connection object stored in context.
+     */
+    public static final String HTTP_SERVER_CONNECTION = "http.server.connection";
+    
+    /**
+     * HTTP Context Attribute Name for HTTP Server Processor object stored in context.
+     */
+    public static final String HTTP_SERVER_PROCESSOR = "http.server.processor";
+    
+    /**
+     * HTTP Context Attribute Name for HTTP Server Service object stored in context.
+     */
+    public static final String HTTP_SERVER_SERVICE = "http.server.service";
+    
+    
+    //
+    // AS2 MIME Content Types
+    //
+    
+    /**
+     * Application EDIFACT content type
+     */
+    public static final String APPLICATION_EDIFACT_MIME_TYPE  = "Application/EDIFACT";
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Header.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Header.java
new file mode 100644
index 0000000..d31d04e
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2Header.java
@@ -0,0 +1,116 @@
+/**
+ * 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;
+
+import org.apache.http.protocol.HTTP;
+
+public interface AS2Header {
+    
+    /**
+     * Message Header Name for MIME Version
+     */
+    public static final String MIME_VERSION = "MIME-Version";
+    /**
+     * Message Header Name for AS2 From
+     */
+    public static final String AS2_FROM = "AS2-From";
+    /**
+     * Message Header Name for AS2 Version
+     */
+    public static final String AS2_VERSION = "AS2-Version";
+    /**
+     * Message Header Name for Content Type
+     */
+    public static final String CONTENT_TYPE = "Content-Type";
+    /**
+     * Message Header Name for AS2 To
+     */
+    public static final String AS2_TO = "AS2-To";
+    /**
+     * Message Header Name for From
+     */
+    public static final String FROM = "From";
+    /**
+     * Message Header Name for Subject
+     */
+    public static final String SUBJECT = "Subject";
+    /**
+     * Message Header Name for Message ID
+     */
+    public static final String MESSAGE_ID = "Message-Id";
+    /**
+     * Message Header Name for Target Host
+     */
+    public static final String TARGET_HOST = HTTP.TARGET_HOST;
+    /**
+     * Message Header Name for User Agent
+     */
+    public static final String USER_AGENT = HTTP.USER_AGENT;
+    /**
+     * Message Header Name for Server Name
+     */
+    public static final String SERVER = HTTP.SERVER_HEADER;
+    /**
+     * Message Header Name for Date
+     */
+    public static final String DATE = HTTP.DATE_HEADER;
+    /**
+     * Message Header Name for Content Length
+     */
+    public static final String CONTENT_LENGTH = HTTP.CONTENT_LEN;
+    /**
+     * Message Header Name for Connection
+     */
+    public static final String CONNECTION = HTTP.CONN_DIRECTIVE;
+    /**
+     * Message Header Name for Expect
+     */
+    public static final String EXPECT = HTTP.EXPECT_DIRECTIVE;
+    /**
+     * Message Header name for Content Transfer Encoding
+     */
+    public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
+    /**
+     * Message Header name for Content Disposition
+     */
+    public static final String CONTENT_DISPOSITION = "Content-Disposition";
+    /**
+     * Message Header name for Content Description
+     */
+    public static final String CONTENT_DESCRIPTION = "Content-Description";
+    /**
+     * Message Header name for Disposition Notification To
+     */
+    public static final String DISPOSITION_NOTIFICATION_TO = "Disposition-Notification-To";
+    /**
+     * Message Header name for Receipt Delivery Option
+     */
+    public static final String RECEIPT_DELIVERY_OPTION = "Receipt-Delivery-Option";
+    /**
+     * Message Header name for Receipt Address
+     */
+    public static final String RECEIPT_ADDRESS = "Receipt-Address";
+    /**
+     * Message Header name for Disposition Notification Options
+     */
+    public static final String DISPOSITION_NOTIFICATION_OPTIONS = "Disposition-Notification-Options";
+    /**
+     * Message Header name for Disposition Notification Options
+     */
+    public static final String REPORT_TYPE = "Report-Type";
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MediaType.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MediaType.java
new file mode 100644
index 0000000..08fbcd9
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MediaType.java
@@ -0,0 +1,45 @@
+/**
+ * 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;
+
+public interface AS2MediaType {
+
+    /**
+     * Media Type for Multipart Signed Data
+     */
+    public static final String MULTIPART_SIGNED = "multipart/signed; protocol=\"application/pkcs7-signature\"";
+    /**
+     * Media Type for Application PKCS7 Signature
+     */
+    public static final String APPLICATION_PKCS7_SIGNATURE = "application/pkcs7-signature; name=smime.p7s; smime-type=signed-data";
+    /**
+     * Media Type for Text/Plain Data
+     */
+    public static final String TEXT_PLAIN = "text/plain";
+    /**
+     * Media Type for Application/EDIFACT
+     */
+    public static final String APPLICATION_EDIFACT = "application/edifact";
+    /**
+     * Media Type for Application/EDI-X12
+     */
+    public static final String APPLICATION_EDI_X12 = "application/edi-x12";
+    /**
+     * Media Type for Application/EDI-consent
+     */
+    public static final String APPLICATION_EDI_CONSENT = "application/edi-consent";
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MessageStructure.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MessageStructure.java
new file mode 100644
index 0000000..0bb9202
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MessageStructure.java
@@ -0,0 +1,44 @@
+/**
+ * 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;
+
+public enum AS2MessageStructure {
+    PLAIN(false, false, false), 
+    SIGNED(false, false, false),
+    ENCRYPTED(false, false, false),
+    ENCRYPTED_SIGNED(false, false, false);
+    
+    private final boolean isSigned;
+    private final boolean isEncrypted;
+    private final boolean isCompressed;
+    
+    private AS2MessageStructure(boolean isSigned, boolean isEncrypted, boolean isCompressed) {
+        this.isSigned = isSigned;
+        this.isEncrypted = isEncrypted;
+        this.isCompressed = isCompressed;
+    }
+    
+    public boolean isSigned() {
+        return isSigned;
+    }
+    public boolean isEncrypted() {
+        return isEncrypted;
+    }
+    public boolean isCompressed() {
+        return isCompressed;
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MicAlgorithm.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MicAlgorithm.java
new file mode 100644
index 0000000..b6c9082
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MicAlgorithm.java
@@ -0,0 +1,69 @@
+/**
+ * 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;
+
+interface Constants {
+    static final String SHA_1_AS2_ALGORITHM_NAME = "sha1";
+    static final String SHA_1_JDK_ALGORITHM_NAME = "SHA-1";
+
+    static final String MD5_AS2_ALGORITHM_NAME = "md5";
+    static final String MD5_JDK_ALGORITHM_NAME = "MD5";
+}
+
+public enum AS2MicAlgorithm {
+    SHA_1(Constants.SHA_1_JDK_ALGORITHM_NAME, Constants.SHA_1_AS2_ALGORITHM_NAME),
+    MD5(Constants.MD5_JDK_ALGORITHM_NAME, Constants.MD5_AS2_ALGORITHM_NAME);
+
+    private String jdkAlgorithmName;
+    private String as2AlgorithmName;
+    
+    private AS2MicAlgorithm(String jdkAlgorithmName, String as2AlgorithmName) {
+        this.jdkAlgorithmName = jdkAlgorithmName;
+        this.as2AlgorithmName = as2AlgorithmName;
+    }
+
+    public String getJdkAlgorithmName() {
+        return jdkAlgorithmName;
+    }
+
+    public String getAs2AlgorithmName() {
+        return as2AlgorithmName;
+    }
+
+    public static String getJdkAlgorithmName(String as2AlgorithmName) {
+        switch(as2AlgorithmName) {
+        case Constants.SHA_1_AS2_ALGORITHM_NAME:
+            return Constants.SHA_1_JDK_ALGORITHM_NAME;
+        case Constants.MD5_AS2_ALGORITHM_NAME:
+            return Constants.MD5_JDK_ALGORITHM_NAME;
+        default:
+            return null;
+        }
+    }
+    
+    public static String getAS2AlgorithmName(String jdkAlgorithmName) {
+        switch(jdkAlgorithmName) {
+        case Constants.MD5_JDK_ALGORITHM_NAME:
+            return Constants.MD5_AS2_ALGORITHM_NAME;
+        case Constants.SHA_1_JDK_ALGORITHM_NAME:
+            return Constants.SHA_1_AS2_ALGORITHM_NAME;
+        default:
+            return null;
+        }
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MimeType.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MimeType.java
new file mode 100644
index 0000000..a1fcd8d
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2MimeType.java
@@ -0,0 +1,57 @@
+/**
+ * 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;
+
+public interface AS2MimeType {
+    /**
+     * Mime Type for Multipart Signed Data
+     */
+    public static final String MULTIPART_SIGNED = "multipart/signed";
+    /**
+     * Mime Type for Application PKCS7 Signature
+     */
+    public static final String APPLICATION_PKCS7_SIGNATURE = "application/pkcs7-signature";
+    /**
+     * Mime Type for Application PKCS7 Signature
+     */
+    public static final String APPLICATION_PKCS7_MIME = "application/pkcs7-mime";
+    /**
+     * Mime Type for Text/Plain Data
+     */
+    public static final String TEXT_PLAIN = "text/plain";
+    /**
+     * Mime Type for Application/EDIFACT
+     */
+    public static final String APPLICATION_EDIFACT = "application/edifact";
+    /**
+     * Mime Type for Application/EDI-X12
+     */
+    public static final String APPLICATION_EDI_X12 = "application/edi-x12";
+    /**
+     * Mime Type for Application/EDI-consent
+     */
+    public static final String APPLICATION_EDI_CONSENT = "application/edi-consent";
+    /**
+     * Mime Type for Multipart/Report
+     */
+    public static final String MULTIPART_REPORT = "multipart/report";
+    /**
+     * Mime Type for Message/Disposition-Notification
+     */
+    public static final String MESSAGE_DISPOSITION_NOTIFICATION = "message/disposition-notification";
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ReportType.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ReportType.java
new file mode 100644
index 0000000..2f3c969
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ReportType.java
@@ -0,0 +1,26 @@
+/**
+ * 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;
+
+public interface AS2ReportType {
+    
+    /**
+     * Disposition Notification Report Type
+     */
+    public static final String DISPOSITION_NOTIFICATION = "disposition-notification";
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
new file mode 100644
index 0000000..9e40149
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
@@ -0,0 +1,217 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+import org.apache.camel.component.as2.api.io.AS2BHttpServerConnection;
+import org.apache.camel.component.as2.api.protocol.ResponseMDN;
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.HttpException;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.HttpService;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
+import org.apache.http.protocol.UriHttpRequestHandlerMapper;
+import org.apache.http.util.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AS2ServerConnection {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AS2ServerConnection.class);
+
+    private static final String REQUEST_LISTENER_THREAD_NAME_PREFIX = "AS2Svr-";
+    private static final String REQUEST_HANDLER_THREAD_NAME_PREFIX = "AS2Hdlr-";
+    
+    static class RequestListenerThread extends Thread {
+
+        private final ServerSocket serversocket;
+        private final HttpService httpService;
+        private UriHttpRequestHandlerMapper reqistry;
+
+        public RequestListenerThread(String as2Version, String originServer, String serverFqdn, int port, Certificate[] signingCertificateChain, PrivateKey signingPrivateKey) throws IOException {
+            setName(REQUEST_LISTENER_THREAD_NAME_PREFIX + port);
+            serversocket = new ServerSocket(port);
+
+            // Set up HTTP protocol processor for incoming connections
+            final HttpProcessor inhttpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] {
+            new ResponseContent(true),
+            new ResponseServer(originServer),
+            new ResponseDate(),
+            new ResponseConnControl(),
+            new ResponseMDN(as2Version, serverFqdn, signingCertificateChain, signingPrivateKey)
+            });
+
+            reqistry = new UriHttpRequestHandlerMapper();
+
+            // Set up the HTTP service
+            httpService = new HttpService(inhttpproc, reqistry);
+        }
+
+        @Override
+        public void run() {
+            LOG.info("Listening on port " + this.serversocket.getLocalPort());
+            while (!Thread.interrupted()) {
+                try {
+                    final int bufsize = 8 * 1024;
+                    // Set up incoming HTTP connection
+                    final Socket insocket = this.serversocket.accept();
+                    final AS2BHttpServerConnection inconn = new AS2BHttpServerConnection(bufsize);
+                    LOG.info("Incoming connection from " + insocket.getInetAddress());
+                    inconn.bind(insocket);
+
+                    // Start worker thread
+                    final Thread t = new RequestHandlerThread(this.httpService, inconn);
+                    t.setDaemon(true);
+                    t.start();
+                } catch (final InterruptedIOException ex) {
+                    break;
+                } catch (final SocketException e) {
+                    // Server socket closed
+                    break;
+                } catch (final IOException e) {
+                    LOG.error("I/O error initialising connection thread: " + e.getMessage());
+                    break;
+                }
+            }
+        }
+        
+        void registerHandler(String requestUriPattern, HttpRequestHandler httpRequestHandler) {
+            reqistry.register(requestUriPattern, httpRequestHandler);
+        }
+        
+        void unregisterHandler(String requestUri) {
+            reqistry.unregister(requestUri);
+        }
+
+    }
+
+    static class RequestHandlerThread extends Thread {
+        private HttpService httpService;
+        private HttpServerConnection serverConnection;
+
+        public RequestHandlerThread(HttpService httpService, HttpServerConnection serverConnection) {
+            if (serverConnection instanceof HttpInetConnection) {
+                HttpInetConnection inetConnection = (HttpInetConnection) serverConnection;
+                setName(REQUEST_HANDLER_THREAD_NAME_PREFIX + inetConnection.getLocalPort());
+            } else {
+                setName(REQUEST_HANDLER_THREAD_NAME_PREFIX + getId());
+            }
+            this.httpService = httpService;
+            this.serverConnection = serverConnection;
+        }
+
+        @Override
+        public void run() {
+            LOG.info("Processing new AS2 request");
+            final HttpContext context = new BasicHttpContext(null);
+
+            try {
+                while (!Thread.interrupted()) {
+
+                    this.httpService.handleRequest(this.serverConnection, context);
+
+                }
+            } catch (final ConnectionClosedException ex) {
+                LOG.info("Client closed connection");
+            } catch (final IOException ex) {
+                LOG.error("I/O error: " + ex.getMessage());
+            } catch (final HttpException ex) {
+                LOG.error("Unrecoverable HTTP protocol violation: " + ex.getMessage());
+            } finally {
+                try {
+                    this.serverConnection.shutdown();
+                } catch (final IOException ignore) {
+                }
+            }
+        }
+        
+    }
+    
+    private RequestListenerThread listenerThread;
+    private String as2Version;
+    private String originServer;
+    private String serverFqdn;
+    private Integer serverPortNumber;
+    private Certificate[] signingCertificateChain;
+    private PrivateKey signingPrivateKey;
+
+    public AS2ServerConnection(String as2Version,
+                               String originServer,
+                               String serverFqdn,
+                               Integer serverPortNumber,
+                               Certificate[] signingCertificateChain,
+                               PrivateKey signingPrivateKey)
+            throws IOException {
+        this.as2Version = Args.notNull(as2Version, "as2Version");
+        this.originServer = Args.notNull(originServer, "userAgent");
+        this.serverFqdn = Args.notNull(serverFqdn, "serverFqdn");
+        this.serverPortNumber = Args.notNull(serverPortNumber, "serverPortNumber");
+        this.signingCertificateChain = signingCertificateChain;
+        this.signingPrivateKey = signingPrivateKey;
+
+        listenerThread = new RequestListenerThread(this.as2Version, this.originServer, this.serverFqdn, this.serverPortNumber, this.signingCertificateChain, this.signingPrivateKey);
+        listenerThread.setDaemon(true);
+        listenerThread.start();
+    }
+    
+    public void close() {
+        if (listenerThread != null) {
+            synchronized (listenerThread) {
+                try {
+                    listenerThread.serversocket.close();
+                } catch (IOException e) {
+                    LOG.debug(e.getMessage(), e);
+                } finally {
+                    listenerThread = null;
+                }
+            }
+        }
+    }
+
+    public void listen(String requestUri, HttpRequestHandler handler) throws IOException {
+        if (listenerThread != null) {
+            synchronized (listenerThread) {
+                listenerThread.registerHandler(requestUri, handler);
+            }
+        }
+    }
+    
+    public void stopListening(String requestUri) {
+        
+        if (listenerThread != null) {
+            listenerThread.unregisterHandler(requestUri);
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerManager.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerManager.java
new file mode 100644
index 0000000..dfcf973
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerManager.java
@@ -0,0 +1,86 @@
+/**
+ * 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;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * AS2 Server Manager 
+ * 
+ * <p>Receives EDI Messages over HTTP  
+ *
+ */
+public class AS2ServerManager {
+    
+    //
+    // AS2 HTTP Context Attribute Keys
+    //
+
+    /**
+     * Prefix for all AS2 HTTP Context Attributes used by the Http Server
+     * Manager.
+     */
+    public static final String CAMEL_AS2_SERVER_PREFIX = "camel-as2.server";
+
+    /**
+     * The HTTP Context Attribute containing the subject header sent in an AS2
+     * response.
+     */
+    public static final String SUBJECT = CAMEL_AS2_SERVER_PREFIX + "subject";
+
+    /**
+     * The HTTP Context Attribute containing the internet e-mail address of
+     * responding system
+     */
+    public static final String FROM = CAMEL_AS2_SERVER_PREFIX + "from";
+
+    private static final Logger LOG = LoggerFactory.getLogger(AS2ServerManager.class);
+    
+    private AS2ServerConnection as2ServerConnection;
+    
+    public AS2ServerManager(AS2ServerConnection as2ServerConnection) {
+        this.as2ServerConnection = as2ServerConnection;
+    }
+    
+    public void listen(String requestUriPattern, HttpRequestHandler handler) {
+        try {
+            as2ServerConnection.listen(requestUriPattern, handler);
+        } catch (IOException e) {
+            LOG.error("Failed to listen for '" + requestUriPattern + "' requests: " + e.getMessage(), e);
+            throw new RuntimeException("Failed to listen for '" + requestUriPattern + "' requests: " + e.getMessage(), e);
+        }
+                
+    }
+    
+    public void stopListening(String requestUri) {
+        as2ServerConnection.stopListening(requestUri);
+    }
+    
+    public void handleMDNResponse(HttpEntityEnclosingRequest request, HttpResponse response, HttpContext httpContext, String subject, String from) throws HttpException {
+        // Add Context attributes for Response 
+        httpContext.setAttribute(SUBJECT, subject);
+        httpContext.setAttribute(FROM, from);
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java
new file mode 100644
index 0000000..791d98f
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java
@@ -0,0 +1,191 @@
+/**
+ * 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;
+
+import java.security.Key;
+/**
+ * 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.
+ */
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.http.entity.ContentType;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.cms.SignerInformation;
+
+public class AS2SignedDataGenerator extends CMSSignedDataGenerator {
+
+    public static final Map<ASN1ObjectIdentifier, String> STANDARD_MICALGS;
+
+    static {
+        Map<ASN1ObjectIdentifier, String> stdMicAlgs = new HashMap<ASN1ObjectIdentifier, String>();
+
+        stdMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        stdMicAlgs.put(CMSAlgorithm.SHA1, "sha-1");
+        stdMicAlgs.put(CMSAlgorithm.SHA224, "sha-224");
+        stdMicAlgs.put(CMSAlgorithm.SHA256, "sha-256");
+        stdMicAlgs.put(CMSAlgorithm.SHA384, "sha-384");
+        stdMicAlgs.put(CMSAlgorithm.SHA512, "sha-512");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        STANDARD_MICALGS = Collections.unmodifiableMap(stdMicAlgs);
+    }
+    
+    /**
+     * Signing algorithms for DSA keys in order of preference
+     */
+    public static final String[] DSA_SIGNING_ALGORITHMS = {
+    "SHA512WITHDSA",
+    "SHA384WITHDSA",
+    "SHA256WITHDSA",
+    "SHA224WITHDSA",
+    "SHA1WITHDSA",
+    };
+
+    /**
+     * Signing algorithms for RSA keys in order of preference
+     */
+    public static final String[] RSA_SIGNING_ALGORITHMS = {
+    "SHA512WITHRSA",
+    "SHA384WITHRSA",
+    "SHA256WITHRSA",
+    "SHA224WITHRSA",
+    "SHA1WITHRSA",
+    "MD5WITHRSA",
+    "MD2WITHRSA",
+    };
+    
+    /**
+     * Signing algorithms for EC keys in order of preference
+     */
+    public static final String[] EC_SIGNING_ALGORITHMS = {
+    "SHA512WITHECDSA",
+    "SHA384WITHECDSA",
+    "SHA256WITHECDSA",
+    "SHA224WITHECDSA",
+    "SHA1WITHECDSA",
+    };
+
+    public AS2SignedDataGenerator() {
+    }
+    
+    /**
+     * Creates a <code>multipart/signed</code> content type containing the algorithms used by this generator.
+     * 
+     * @return A <code>multipart/signed</code> content type
+     */
+    public ContentType createMultipartSignedContentType(String boundary) {
+        StringBuffer header = new StringBuffer(AS2MediaType.MULTIPART_SIGNED);
+        header.append("; boundary=" + boundary);
+        Set<String> micAlgSet = new HashSet<String>();
+
+        // Collect algorithm names used by pre-calculated signers
+        for (@SuppressWarnings("rawtypes")
+        Iterator it = _signers.iterator(); it.hasNext();) {
+            SignerInformation signer = (SignerInformation) it.next();
+            ASN1ObjectIdentifier digestOID = signer.getDigestAlgorithmID().getAlgorithm();
+
+            String micAlg = (String) STANDARD_MICALGS.get(digestOID);
+
+            if (micAlg == null) {
+                micAlgSet.add("unknown");
+            } else {
+                micAlgSet.add(micAlg);
+            }
+        }
+
+        // Collect algorithm names used by signer generators
+        for (@SuppressWarnings("rawtypes")
+        Iterator it = signerGens.iterator(); it.hasNext();) {
+            SignerInfoGenerator signerInfoGen = (SignerInfoGenerator) it.next();
+            ASN1ObjectIdentifier digestOID = signerInfoGen.getDigestAlgorithm().getAlgorithm();
+
+            String micAlg = (String) STANDARD_MICALGS.get(digestOID);
+
+            if (micAlg == null) {
+                micAlgSet.add("unknown");
+            } else {
+                micAlgSet.add(micAlg);
+            }
+        }
+
+        // Add algorithm names to multipart signed header.
+        int count = 0;
+        for (Iterator<String> it = micAlgSet.iterator(); it.hasNext();) {
+            String alg = it.next();
+
+            if (count == 0) {
+                if (micAlgSet.size() != 1) {
+                    header.append("; micalg=\"");
+                } else {
+                    header.append("; micalg=");
+                }
+            } else {
+                header.append(',');
+            }
+
+            header.append(alg);
+
+            count++;
+        }
+
+        if (count != 0) {
+            if (micAlgSet.size() != 1) {
+                header.append('\"');
+            }
+        }
+        
+        return ContentType.parse(header.toString());
+    }
+    
+    public static String[] getSupportedSignatureAlgorithmNamesForKey(Key key) {
+        
+        switch (key.getAlgorithm()) {
+        case "DSA":
+            return DSA_SIGNING_ALGORITHMS;
+        case "RSA":
+            return RSA_SIGNING_ALGORITHMS;
+        case "EC":
+            return EC_SIGNING_ALGORITHMS;
+        default:
+            return new String[0];
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2TransferEncoding.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2TransferEncoding.java
new file mode 100644
index 0000000..801c63e
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2TransferEncoding.java
@@ -0,0 +1,23 @@
+/**
+ * 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;
+
+public interface AS2TransferEncoding {
+    public static final String NONE = null; 
+    public static final String BASE64 = "base64";
+    public static final String SEVENBIT = "7bit";
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/CanonicalOutputStream.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/CanonicalOutputStream.java
new file mode 100644
index 0000000..e926422
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/CanonicalOutputStream.java
@@ -0,0 +1,80 @@
+/**
+ * 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;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class CanonicalOutputStream extends FilterOutputStream {
+
+    private static byte[] newline;
+    private int lastByte;
+
+    static {
+        newline = new byte[2];
+        newline[0] = (byte) '\r';
+        newline[1] = (byte) '\n';
+    }
+
+    private String charset; 
+
+    public CanonicalOutputStream(OutputStream out, String charset) {
+        super(out);
+        this.charset = charset;
+        lastByte = -1;
+    }
+
+    public void write(int i) throws IOException {
+        if (i == '\r') {
+            // convert all carriage-return characters into line-break sequence
+            out.write(newline);
+        } else if (i == '\n') {
+            // convert line-feed character into line-break sequence if not
+            // preceded by carriage-return
+            if (lastByte != '\r') {
+                out.write(newline);
+            }
+            // otherwise the line-feed was preceded by carriage-return so ignore it.
+        } else {
+            out.write(i);
+        }
+
+        lastByte = i;
+    }
+
+    public void write(byte[] buf) throws IOException {
+        this.write(buf, 0, buf.length);
+    }
+
+    public void write(byte buf[], int off, int len) throws IOException {
+        for (int i = off; i != off + len; i++) {
+            this.write(buf[i]);
+        }
+    }
+
+    public void writeln(String s) throws IOException {
+        byte[] bytes = s.getBytes(charset);
+        write(bytes);
+        write(newline);
+    }
+
+    public void writeln() throws IOException {
+        write(newline);
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/InvalidAS2NameException.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/InvalidAS2NameException.java
new file mode 100644
index 0000000..6439fdc
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/InvalidAS2NameException.java
@@ -0,0 +1,73 @@
+/**
+ * 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;
+
+/**
+ * Thrown to indicated a given AS2 name is invalid.
+ */
+public class InvalidAS2NameException extends Exception {
+
+    private static final long serialVersionUID = -6284079291785073089L;
+
+    private final String name;
+    
+    private final int index;
+    
+    /**
+     * Constructs an <code>InvalidAS2NameException</code> for the
+     * specified name and index.
+     * 
+     * @param name - the AS2 name that is invalid.
+     * @param index - the index in the <code>name</code> of the invalid character 
+     */
+    public InvalidAS2NameException(String name, int index) {
+        super();
+        this.name = name;
+        this.index = index;
+    }
+    
+    /* (non-Javadoc)
+     * @see java.lang.Throwable#getMessage()
+     */
+    @Override
+    public String getMessage() {
+        char character = name.charAt(index);
+        String invalidChar = "" + character;
+        if (Character.isISOControl(character)) {
+            invalidChar = String.format("\\u%04x", (int) character);
+        }
+        return "Invalid character '" + invalidChar + "' at index " + index;
+    }
+
+    /**
+     * Returns the invalid AS2 name
+     * 
+     * @return the invalid AS2 name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the index of the invalid character in <code>name</code>
+     * 
+     * @return the index of the invalid character in <code>name</code>
+     */
+    public int getIndex() {
+        return index;
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/MDNField.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/MDNField.java
new file mode 100644
index 0000000..6352740
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/MDNField.java
@@ -0,0 +1,66 @@
+/**
+ * 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;
+
+public interface MDNField {
+    
+    /**
+     * Field Name for Reporting UA
+     */
+    public static final String REPORTING_UA = "Reporting-UA";
+
+    /**
+     * Field Name for MDN Gateway
+     */
+    public static final String MDN_GATEWAY = "MDN-Gateway";
+
+    /**
+     * Field Name for Final Recipient
+     */
+    public static final String FINAL_RECIPIENT = "Final-Recipient";
+
+    /**
+     * Field Name for Original Message IDX
+     */
+    public static final String ORIGINAL_MESSAGE_ID = "Original-Message-ID";
+
+    /**
+     * Field Name for Disposition
+     */
+    public static final String DISPOSITION = "Disposition";
+
+    /**
+     * Field Name for Failure
+     */
+    public static final String FAILURE = "Failure";
+
+    /**
+     * Field Name for Error
+     */
+    public static final String ERROR = "Error";
+
+    /**
+     * Field Name for Warning
+     */
+    public static final String WARNING = "Warning";
+
+    /**
+     * Field Name for Received Content MIC
+     */
+    public static final String RECEIVED_CONTENT_MIC = "Received-content-MIC";
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/Util.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/Util.java
new file mode 100644
index 0000000..ce1c613
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/Util.java
@@ -0,0 +1,183 @@
+/**
+ * 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;
+
+import java.awt.event.KeyEvent;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+
+/**
+ * Utility Methods used in AS2 Component
+ */
+public final class Util {
+    
+    public static final String DQUOTE = "\"";
+    public static final String BACKSLASH = "\\\\";
+    public static final String AS2_TEXT_CHAR_SET = "[\u0021\u0023-\\\u005B\\\u005D-\u007E]";
+    public static final String AS2_QUOTED_TEXT_CHAR_SET = "[\u0020\u0021\u0023-\\\u005B\\\u005D-\u007E]";
+    public static final String AS2_QUOTED_PAIR =  BACKSLASH + DQUOTE + "|" + BACKSLASH + BACKSLASH; 
+    
+    public static final String AS2_QUOTED_NAME = DQUOTE + "(" + AS2_QUOTED_TEXT_CHAR_SET + "|" + AS2_QUOTED_PAIR + "){1,128}" + DQUOTE;
+    public static final String AS2_ATOMIC_NAME = "(" + AS2_TEXT_CHAR_SET + "){1,128}";
+    public static final String AS2_NAME = AS2_ATOMIC_NAME + "|" + AS2_QUOTED_NAME;
+    
+    public static final Pattern AS_NAME_PATTERN = Pattern.compile(AS2_NAME);
+    
+    private static Random generator = new Random();
+    
+    private Util() {
+    }
+    
+    /**
+     * Validates if the given <code>name</code> is a valid AS2 Name
+     * 
+     * @param name - the name to validate.
+     * @throws InvalidAS2NameException 
+     */
+    public static void validateAS2Name(String name) throws InvalidAS2NameException {
+        Matcher matcher = AS_NAME_PATTERN.matcher(name);
+        if (!matcher.matches()) {
+            // if name does not match, determine where it fails to match.
+            int i = 0;
+            for (i = name.length() - 1; i > 0; i--) {
+                Matcher region = matcher.region(0, i);
+                if (region.matches() || region.hitEnd()) {
+                    break;
+                }
+            }
+            throw new InvalidAS2NameException(name, i);
+        }
+    }
+    
+    /**
+     * Generates a globally unique message ID which includes <code>fqdn</code>: a fully qualified domain name (FQDN)
+     * @param fqdn - the fully qualified domain name to use in message id.
+     * @return The generated message id.
+     */
+    public static String createMessageId(String fqdn) {
+                    /* Wall Clock Time in Nanoseconds */          /* 64 Bit Random Number */                      /* Fully Qualified Domain Name */
+        return "<" + Long.toString(System.nanoTime(), 36) + "." + Long.toString(generator.nextLong(), 36) + "@" + fqdn + ">";
+    }
+    
+    
+   /**
+     * Determines if <code>c</code> is a printable character.
+     * @param c - the character to test
+     * @return <code>true</code> if <code>c</code> is a printable character; <code>false</code> otherwise.
+     */
+    public static boolean isPrintableChar(char c) {
+        Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
+        return (!Character.isISOControl(c)) && c != KeyEvent.CHAR_UNDEFINED && block != null
+                && block != Character.UnicodeBlock.SPECIALS;
+    }
+    
+    public static String printRequest(HttpRequest request) throws IOException {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos, true, "utf-8")) {
+            printRequest(ps, request);
+            String content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
+            return content;
+        }
+    }
+    
+    public static String printMessage(HttpMessage message) throws IOException {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos, true, "utf-8")) {
+            printMessage(ps, message);
+            String content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
+            return content;
+        }
+    }
+    
+    /**
+     * Prints the contents of request to given print stream.
+     * 
+     * @param out
+     *            - the stream printed to.
+     * @param request
+     *            - the request printed.
+     * @throws IOException
+     */
+    public static void printRequest(PrintStream out, HttpRequest request) throws IOException {
+        // Print request line
+        RequestLine requestLine = request.getRequestLine();
+        out.println(requestLine.getMethod() + ' ' + requestLine.getUri() + ' ' + requestLine.getProtocolVersion());
+
+        // Write headers
+        for (final HeaderIterator it = request.headerIterator(); it.hasNext();) {
+            Header header = it.nextHeader();
+            out.println(header.getName() + ": " + (header.getValue() == null ? "" : header.getValue()));
+        }
+        out.println(); // write empty line separating header from body.
+
+        if (request instanceof HttpEntityEnclosingRequest) {
+            // Write entity
+            HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+            entity.writeTo(out);
+        }
+    }
+
+    /**
+     * Prints the contents of an Http Message to given print stream.
+     * 
+     * @param out - the stream printed to.
+     * @param message - the request printed.
+     * @throws IOException
+     */
+    public static void printMessage(PrintStream out, HttpMessage message) throws IOException {
+        // Print request line
+        if (message instanceof HttpRequest) {
+            RequestLine requestLine = ((HttpRequest)message).getRequestLine();
+            out.println(requestLine.getMethod() + ' ' + requestLine.getUri() + ' ' + requestLine.getProtocolVersion());
+        } else { // HttpResponse
+            StatusLine statusLine = ((HttpResponse)message).getStatusLine();
+            out.println(statusLine.toString());
+        }
+        // Write headers
+        for (final HeaderIterator it = message.headerIterator(); it.hasNext();) {
+            Header header = it.nextHeader();
+            out.println(header.getName() + ": " + (header.getValue() == null ? "" : header.getValue()));
+        }
+        out.println(); // write empty line separating header from body.
+        
+        if (message instanceof HttpEntityEnclosingRequest) {
+            // Write entity
+            HttpEntity entity = ((HttpEntityEnclosingRequest)message).getEntity();
+            entity.writeTo(out);
+        } else if (message instanceof HttpResponse) {
+            // Write entity
+            HttpEntity entity = ((HttpResponse)message).getEntity();
+            entity.writeTo(out);
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2DispositionModifier.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2DispositionModifier.java
new file mode 100644
index 0000000..b4ed117
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2DispositionModifier.java
@@ -0,0 +1,91 @@
+/**
+ * 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.entity;
+
+public final class AS2DispositionModifier {
+    
+    public static final AS2DispositionModifier ERROR = new AS2DispositionModifier("error");
+    public static final AS2DispositionModifier ERROR_AUTHENTICATION_FAILED = new AS2DispositionModifier("error: authentication-failed");
+    public static final AS2DispositionModifier ERROR_DECOMPRESSION_FAILED = new AS2DispositionModifier("error: decompression-failed");
+    public static final AS2DispositionModifier ERROR_DECRYPTION_FAILED = new AS2DispositionModifier("error: decryption-failed");
+    public static final AS2DispositionModifier ERROR_INSUFFICIENT_MESSAGE_SECURITY = new AS2DispositionModifier("error: insufficient-message-security");
+    public static final AS2DispositionModifier ERROR_INTEGRITY_CHECK_FAILED = new AS2DispositionModifier("error: integrity-check-failed");
+    public static final AS2DispositionModifier ERROR_UNEXPECTED_PROCESSING_ERROR = new AS2DispositionModifier("error: unexpected-processing-error");
+    public static final AS2DispositionModifier WARNING = new AS2DispositionModifier("warning");
+    
+    private String modifier;
+    
+    private AS2DispositionModifier(String modifier) {
+        this.modifier = modifier;
+    }
+    
+    public String getModifier() {
+        return modifier;
+    }
+    
+    public boolean isError() {
+        return modifier.startsWith("error: ");
+    }
+
+    public boolean isFailuer() {
+        return modifier.startsWith("failure: ");
+    }
+
+    public boolean isWarning() {
+        return modifier.startsWith("warning: ");
+    }
+
+    @Override
+    public String toString() {
+        return modifier;
+    }
+
+    public static AS2DispositionModifier createWarning(String description) {
+        return new AS2DispositionModifier("warning: " + description);
+    }
+    
+    public static AS2DispositionModifier createFailure(String description) {
+        return new AS2DispositionModifier("failure: " + description);
+    }
+    
+    public static AS2DispositionModifier parseDispositionType(String dispositionModifierString) {
+        switch(dispositionModifierString) {
+        case "error":
+            return ERROR;
+        case "error: authentication-failed":
+            return ERROR_AUTHENTICATION_FAILED;
+        case "error: decompression-failed\"":
+            return ERROR_DECOMPRESSION_FAILED;
+        case "error: decryption-failed":
+            return ERROR_DECRYPTION_FAILED;
+        case "error: insufficient-message-security":
+            return ERROR_INSUFFICIENT_MESSAGE_SECURITY;
+        case "error: integrity-check-failed":
+            return ERROR_INTEGRITY_CHECK_FAILED;
+        case "error: unexpected-processing-error":
+            return ERROR_UNEXPECTED_PROCESSING_ERROR;
+        case "warning":
+            return WARNING;
+        default:
+            if (dispositionModifierString.startsWith("warning: ") || dispositionModifierString.startsWith("failure: ")) {
+                return new AS2DispositionModifier(dispositionModifierString);
+            }
+            return null;
+        }
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2DispositionType.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2DispositionType.java
new file mode 100644
index 0000000..13b1fff
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2DispositionType.java
@@ -0,0 +1,48 @@
+/**
+ * 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.entity;
+
+public enum AS2DispositionType {
+    PROCESSED("processed"),
+    FAILED("failed");
+    
+    private String type;
+    
+    private AS2DispositionType(String type) {
+        this.type = type;
+    }
+
+    public String getType() {
+        return type;
+    }
+    
+    @Override
+    public String toString() {
+        return type;
+    }
+    
+    public static AS2DispositionType parseDispositionType(String dispositionTypeString) {
+        switch(dispositionTypeString) {
+        case "processed":
+            return PROCESSED;
+        case "failed":
+            return FAILED;
+        default:
+            return null;
+        }
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java
new file mode 100644
index 0000000..b1a4bf2
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/AS2MessageDispositionNotificationEntity.java
@@ -0,0 +1,262 @@
+/**
+ * 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.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.CanonicalOutputStream;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.camel.component.as2.api.util.MicUtils;
+import org.apache.camel.component.as2.api.util.MicUtils.ReceivedContentMic;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.Args;
+
+public class AS2MessageDispositionNotificationEntity extends MimeEntity {
+
+    private static final String ADDRESS_TYPE_PREFIX = "rfc822;";
+    private static final String MTA_NAME_TYPE_PREFIX = "dns;";
+    private static final String REPORTING_UA = "Reporting-UA";
+    private static final String MDN_GATEWAY = "MDN-Gateway";
+    private static final String FINAL_RECIPIENT = "Final-Recipient";
+    private static final String ORIGINAL_MESSAGE_ID = "Original-Message-ID";
+    private static final String AS2_DISPOSITION = "Disposition";
+    private static final String FAILURE = "Failure";
+    private static final String ERROR = "Error";
+    private static final String WARNING = "Warning";
+    private static final String RECEIVED_CONTENT_MIC = "Received-content-MIC";
+
+    private String reportingUA;
+    // TODO determine if we need to support this field.
+    private String mtnName;
+    private String finalRecipient;
+    private String originalMessageId;
+    private DispositionMode dispositionMode;
+    private AS2DispositionType dispositionType;
+    private AS2DispositionModifier dispositionModifier;
+    private String[] failureFields;
+    private String[] errorFields;
+    private String[] warningFields;
+    private Map<String, String> extensionFields = new HashMap<String, String>();
+    private ReceivedContentMic receivedContentMic;
+
+    public AS2MessageDispositionNotificationEntity(HttpEntityEnclosingRequest request,
+                                                   HttpResponse response,
+                                                   DispositionMode dispositionMode,
+                                                   AS2DispositionType dispositionType,
+                                                   AS2DispositionModifier dispositionModifier,
+                                                   String[] failureFields,
+                                                   String[] errorFields,
+                                                   String[] warningFields,
+                                                   Map<String, String> extensionFields,
+                                                   String charset,
+                                                   boolean isMainBody) throws HttpException {
+        setMainBody(isMainBody);
+        setContentType(ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, charset));
+        
+        this.finalRecipient = HttpMessageUtils.getHeaderValue(request, AS2Header.AS2_TO);
+        if (this.finalRecipient == null) {
+            throw new HttpException("The " + AS2Header.AS2_TO + " is missing");
+        }
+        
+        this.originalMessageId  = HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID);
+
+        this.receivedContentMic = MicUtils.createReceivedContentMic(request);
+        
+        this.reportingUA = HttpMessageUtils.getHeaderValue(response, AS2Header.SERVER);
+
+        this.dispositionMode = Args.notNull(dispositionMode, "Disposition Mode");
+        this.dispositionType = Args.notNull(dispositionType, "Disposition Type");
+        this.dispositionModifier = dispositionModifier;
+        this.failureFields = failureFields;
+        this.errorFields = errorFields;
+        this.warningFields = warningFields;
+        if (extensionFields == null || extensionFields.isEmpty()) {
+            this.extensionFields.clear();
+        } else {
+            this.extensionFields.putAll(extensionFields);
+        }
+    }
+    
+    public AS2MessageDispositionNotificationEntity(String reportingUA,
+                                                   String mtnName,
+                                                   String finalRecipient,
+                                                   String originalMessageId,
+                                                   DispositionMode dispositionMode,
+                                                   AS2DispositionType dispositionType,
+                                                   AS2DispositionModifier dispositionModifier,
+                                                   String[] failureFields,
+                                                   String[] errorFields,
+                                                   String[] warningFields,
+                                                   Map<String, String> extensionFields,
+                                                   ReceivedContentMic receivedContentMic) {
+        super();
+        this.reportingUA = reportingUA;
+        this.mtnName = mtnName;
+        this.finalRecipient = finalRecipient;
+        this.originalMessageId = originalMessageId;
+        this.dispositionMode = dispositionMode;
+        this.dispositionType = dispositionType;
+        this.dispositionModifier = dispositionModifier;
+        this.failureFields = failureFields;
+        this.errorFields = errorFields;
+        this.warningFields = warningFields;
+        this.extensionFields = extensionFields;
+        this.receivedContentMic = receivedContentMic;
+    }
+
+    public String getReportingUA() {
+        return reportingUA;
+    }
+
+    public String getMtnName() {
+        return mtnName;
+    }
+
+    public String getFinalRecipient() {
+        return finalRecipient;
+    }
+
+    public String getOriginalMessageId() {
+        return originalMessageId;
+    }
+
+    public DispositionMode getDispositionMode() {
+        return dispositionMode;
+    }
+
+    public AS2DispositionType getDispositionType() {
+        return dispositionType;
+    }
+
+    public AS2DispositionModifier getDispositionModifier() {
+        return dispositionModifier;
+    }
+
+    public String[] getFailureFields() {
+        return failureFields;
+    }
+
+    public String[] getErrorFields() {
+        return errorFields;
+    }
+
+    public String[] getWarningFields() {
+        return warningFields;
+    }
+
+    public Map<String, String> getExtensionFields() {
+        return extensionFields;
+    }
+
+    public ReceivedContentMic getReceivedContentMic() {
+        return receivedContentMic;
+    }
+
+    @Override
+    public void writeTo(OutputStream outstream) throws IOException {
+        NoCloseOutputStream ncos = new NoCloseOutputStream(outstream);
+        try (CanonicalOutputStream canonicalOutstream = new CanonicalOutputStream(ncos, AS2Charset.US_ASCII)) {
+
+            // Write out mime part headers if this is not the main body of
+            // message.
+            if (!isMainBody()) {
+                HeaderIterator it = headerIterator();
+                while (it.hasNext()) {
+                    Header header = it.nextHeader();
+                    canonicalOutstream.writeln(header.toString());
+                }
+                canonicalOutstream.writeln(); // ensure empty line between
+                                              // headers and body; RFC2046 -
+                                              // 5.1.1
+            }
+
+            if (reportingUA != null) {
+                Header reportingUAField = new BasicHeader(REPORTING_UA, reportingUA);
+                canonicalOutstream.writeln(reportingUAField.toString());
+            }
+
+            if (mtnName != null) {
+                Header mdnGatewayField = new BasicHeader(MDN_GATEWAY, MTA_NAME_TYPE_PREFIX + mtnName);
+                canonicalOutstream.writeln(mdnGatewayField.toString());
+            }
+
+            Header finalRecipientField = new BasicHeader(FINAL_RECIPIENT, ADDRESS_TYPE_PREFIX + finalRecipient);
+            canonicalOutstream.writeln(finalRecipientField.toString());
+
+            if (originalMessageId != null) {
+                Header originalMessageIdField = new BasicHeader(ORIGINAL_MESSAGE_ID, originalMessageId);
+                canonicalOutstream.writeln(originalMessageIdField.toString());
+            }
+
+            String as2Disposition = dispositionMode.toString() + ";" + dispositionType.toString();
+            if (dispositionModifier != null) {
+                as2Disposition = as2Disposition + "/" + dispositionModifier.toString();
+            }
+            Header as2DispositionField = new BasicHeader(AS2_DISPOSITION,
+                    dispositionMode.toString() + ";" + dispositionType.toString());
+            canonicalOutstream.writeln(as2DispositionField.toString());
+
+            if (failureFields != null) {
+                for (String field : failureFields) {
+                    Header failureField = new BasicHeader(FAILURE, field);
+                    canonicalOutstream.writeln(failureField.toString());
+                }
+            }
+
+            if (errorFields != null) {
+                for (String field : errorFields) {
+                    Header errorField = new BasicHeader(ERROR, field);
+                    canonicalOutstream.writeln(errorField.toString());
+                }
+            }
+
+            if (failureFields != null) {
+                for (String field : failureFields) {
+                    Header failureField = new BasicHeader(WARNING, field);
+                    canonicalOutstream.writeln(failureField.toString());
+                }
+            }
+
+            if (extensionFields != null) {
+                for (Entry<String, String> entry : extensionFields.entrySet()) {
+                    Header failureField = new BasicHeader(entry.getKey(), entry.getValue());
+                    canonicalOutstream.writeln(failureField.toString());
+                }
+            }
+
+            if (receivedContentMic != null) {
+                Header as2ReceivedContentMicField = new BasicHeader(RECEIVED_CONTENT_MIC,
+                        receivedContentMic.toString());
+                canonicalOutstream.writeln(as2ReceivedContentMicField.toString());
+            }
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIConsentEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIConsentEntity.java
new file mode 100644
index 0000000..3b25e6a
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIConsentEntity.java
@@ -0,0 +1,29 @@
+/**
+ * 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.entity;
+
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.http.entity.ContentType;
+
+public class ApplicationEDIConsentEntity extends ApplicationEDIEntity {
+
+    public ApplicationEDIConsentEntity(String content, String charset, String contentTransferEncoding,
+            boolean isMainBody) {
+        super(content, ContentType.create(AS2MediaType.APPLICATION_EDI_CONSENT, charset), contentTransferEncoding, isMainBody);
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIEntity.java
new file mode 100644
index 0000000..a49396a
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIEntity.java
@@ -0,0 +1,64 @@
+/**
+ * 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.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.CanonicalOutputStream;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.Args;
+
+public abstract class ApplicationEDIEntity extends MimeEntity {
+    
+    private final String ediMessage;
+    
+    protected ApplicationEDIEntity(String ediMessage, ContentType contentType, String contentTransferEncoding, boolean isMainBody) {
+        this.ediMessage = Args.notNull(ediMessage, "EDI Message");
+        setContentType(Args.notNull(contentType, "Content Type").toString());
+        setContentTransferEncoding(contentTransferEncoding);
+        setMainBody(isMainBody);
+    }
+    
+    public String getEdiMessage() {
+        return ediMessage;
+    }
+
+
+    @Override
+    public void writeTo(OutputStream outstream) throws IOException {
+        NoCloseOutputStream ncos = new NoCloseOutputStream(outstream);
+        try (CanonicalOutputStream canonicalOutstream = new CanonicalOutputStream(ncos, AS2Charset.US_ASCII)) {
+
+            // Write out mime part headers if this is not the main body of message.
+            if (!isMainBody()) { 
+                HeaderIterator it = headerIterator();
+                while (it.hasNext()) {
+                    Header header = it.nextHeader();
+                    canonicalOutstream.writeln(header.toString());
+                }
+                canonicalOutstream.writeln(); // ensure empty line between headers and body; RFC2046 - 5.1.1
+            }
+            
+            canonicalOutstream.write(ediMessage.getBytes(getCharset()), 0, ediMessage.length());
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIFACTEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIFACTEntity.java
new file mode 100644
index 0000000..4e94456
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIFACTEntity.java
@@ -0,0 +1,29 @@
+/**
+ * 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.entity;
+
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.http.entity.ContentType;
+
+public class ApplicationEDIFACTEntity extends ApplicationEDIEntity {
+
+    public ApplicationEDIFACTEntity(String content, String charset, String contentTransferEncoding,
+            boolean isMainBody) {
+        super(content, ContentType.create(AS2MediaType.APPLICATION_EDIFACT, charset), contentTransferEncoding, isMainBody);
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIX12Entity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIX12Entity.java
new file mode 100644
index 0000000..24f1702
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationEDIX12Entity.java
@@ -0,0 +1,29 @@
+/**
+ * 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.entity;
+
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.http.entity.ContentType;
+
+public class ApplicationEDIX12Entity extends ApplicationEDIEntity {
+
+    public ApplicationEDIX12Entity(String content, String charset, String contentTransferEncoding,
+            boolean isMainBody) {
+        super(content, ContentType.create(AS2MediaType.APPLICATION_EDI_X12, charset), contentTransferEncoding, isMainBody);
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7SignatureEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7SignatureEntity.java
new file mode 100644
index 0000000..2b78f6e
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7SignatureEntity.java
@@ -0,0 +1,125 @@
+/**
+ * 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.entity;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.CanonicalOutputStream;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpException;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.Args;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+
+public class ApplicationPkcs7SignatureEntity extends MimeEntity {
+    
+    private static final String CONTENT_DISPOSITION = "attachment; filename=\"smime.p7s\"";
+    
+    private static final String CONTENT_DESCRIPTION = "S/MIME Cryptographic Signature";
+    
+    private byte[] signature;
+    
+    public ApplicationPkcs7SignatureEntity(MimeEntity data, CMSSignedDataGenerator signer, String charset, String contentTransferEncoding, boolean isMainBody) throws HttpException {
+        Args.notNull(data, "Data");
+        Args.notNull(signer, "Signer");
+        
+        ContentType contentType = ContentType.parse(EntityUtils.appendParameter(AS2MediaType.APPLICATION_PKCS7_SIGNATURE, "charset",  charset));
+        setContentType(contentType.toString());
+        setContentTransferEncoding(contentTransferEncoding);
+        addHeader(AS2Header.CONTENT_DISPOSITION, CONTENT_DISPOSITION);
+        addHeader(AS2Header.CONTENT_DESCRIPTION, CONTENT_DESCRIPTION);
+        setMainBody(isMainBody);
+        try {
+            this.signature = createSignature(data, signer);
+        } catch (Exception e) {
+            throw new HttpException("Failed to create signed data", e);
+        }
+    }
+    
+    public ApplicationPkcs7SignatureEntity(String charset,
+                                           String contentTransferEncoding,
+                                           byte[] signature,
+                                           boolean isMainBody)
+            throws HttpException {
+        this.signature = signature;
+        ContentType contentType = ContentType
+                .parse(EntityUtils.appendParameter(AS2MediaType.APPLICATION_PKCS7_SIGNATURE, "charset", charset));
+        setContentType(contentType.toString());
+        setContentTransferEncoding(contentTransferEncoding);
+        addHeader(AS2Header.CONTENT_DISPOSITION, CONTENT_DISPOSITION);
+        addHeader(AS2Header.CONTENT_DESCRIPTION, CONTENT_DESCRIPTION);
+        setMainBody(isMainBody);
+    }
+    
+    public byte[] getSignature() {
+        return signature;
+    }
+
+    @Override
+    public void writeTo(OutputStream outstream) throws IOException {
+        NoCloseOutputStream ncos = new NoCloseOutputStream(outstream);
+
+        // Write out mime part headers if this is not the main body of message.
+        if (!isMainBody()) {
+            try (CanonicalOutputStream canonicalOutstream = new CanonicalOutputStream(ncos, AS2Charset.US_ASCII)) {
+
+                HeaderIterator it = headerIterator();
+                while (it.hasNext()) {
+                    Header header = it.nextHeader();
+                    canonicalOutstream.writeln(header.toString());
+                }
+                canonicalOutstream.writeln(); // ensure empty line between
+                                              // headers and body; RFC2046 -
+                                              // 5.1.1
+            }
+        }
+
+        // Write out signed data.
+        String transferEncoding = getContentTransferEncoding() == null ? null : getContentTransferEncoding().getValue();
+        try (OutputStream trasnsferEncodedStream = EntityUtils.encode(ncos, transferEncoding)) {
+
+            trasnsferEncodedStream.write(signature);
+        } catch (Exception e) {
+            throw new IOException("Failed to write to output stream", e);
+        }
+    }
+    
+    private byte[] createSignature(MimeEntity data, CMSSignedDataGenerator signer) throws Exception {
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+            data.writeTo(bos);
+            bos.flush();
+
+            CMSTypedData contentData = new CMSProcessableByteArray(bos.toByteArray());
+            CMSSignedData  signedData = signer.generate(contentData, false);
+            return signedData.getEncoded();
+        } catch (Exception e) {
+            throw new Exception("", e);
+        }
+        
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionMode.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionMode.java
new file mode 100644
index 0000000..4ddb6c6
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionMode.java
@@ -0,0 +1,60 @@
+/**
+ * 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.entity;
+
+public enum DispositionMode {
+    MANUAL_ACTION_MDN_SENT_MANUALLY("manual-action", "MDN-sent-manually"),
+    MANUAL_ACTION_MDN_SENT_AUTOMATICALLY("manual-action", "MDN-sent-automatically"),
+    AUTOMATIC_ACTION_MDN_SENT_MANUALLY("automatic-action", "MDN-sent-manually"),
+    AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY("automatic-action", "MDN-sent-automatically");
+    
+    private String actionMode;
+    private String sendingMode;
+
+    private DispositionMode(String actionMode, String sendingMode) {
+        this.actionMode = actionMode;
+        this.sendingMode = sendingMode;
+    }
+    
+    public String getActionMode() {
+        return actionMode;
+    }
+
+    public String getSendingMode() {
+        return sendingMode;
+    }
+
+    @Override
+    public String toString() {
+        return actionMode + "/" + sendingMode;
+    }
+
+    public static DispositionMode parseDispositionMode(String dispositionModeString) {
+        switch(dispositionModeString) {
+        case "manual-action/MDN-sent-manually":
+            return MANUAL_ACTION_MDN_SENT_MANUALLY;
+        case "manual-actionMDN-sent-automatically":
+            return MANUAL_ACTION_MDN_SENT_AUTOMATICALLY;
+        case "automatic-action/MDN-sent-manually":
+            return AUTOMATIC_ACTION_MDN_SENT_MANUALLY;
+        case "automatic-action/MDN-sent-automatically":
+            return AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY;
+        default:
+            return null;
+        }
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationMultipartReportEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationMultipartReportEntity.java
new file mode 100644
index 0000000..30a8023
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationMultipartReportEntity.java
@@ -0,0 +1,101 @@
+/**
+ * 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.entity;
+
+import java.util.Map;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.AS2TransferEncoding;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.util.CharArrayBuffer;
+
+public class DispositionNotificationMultipartReportEntity extends MultipartReportEntity {
+    
+    public DispositionNotificationMultipartReportEntity(HttpEntityEnclosingRequest request,
+                                                        HttpResponse response,
+                                                        DispositionMode dispositionMode,
+                                                        AS2DispositionType dispositionType,
+                                                        AS2DispositionModifier dispositionModifier,
+                                                        String[] failureFields,
+                                                        String[] errorFields,
+                                                        String[] warningFields,
+                                                        Map<String, String> extensionFields,
+                                                        String charset,
+                                                        String boundary,
+                                                        boolean isMainBody)
+            throws HttpException {
+        super(charset, isMainBody, boundary);
+        this.contentType = new BasicHeader(AS2Header.CONTENT_TYPE, AS2MimeType.MULTIPART_REPORT);
+        Header reportType = new BasicHeader(AS2Header.REPORT_TYPE, getReportTypeValue(boundary));
+        addHeader(reportType);
+        
+        addPart(buildPlainTextReport(request, response, dispositionMode, dispositionType,
+                dispositionModifier, failureFields, errorFields, warningFields, extensionFields));
+        addPart(new AS2MessageDispositionNotificationEntity(request, response, dispositionMode, dispositionType,
+                dispositionModifier, failureFields, errorFields, warningFields, extensionFields, charset, false));
+    }
+    
+    protected DispositionNotificationMultipartReportEntity(String boundary, boolean isMainBody) {
+        this.boundary = boundary;
+        this.isMainBody = isMainBody;
+    }
+
+    protected TextPlainEntity buildPlainTextReport(HttpEntityEnclosingRequest request,
+                                                   HttpResponse response,
+                                                   DispositionMode dispositionMode,
+                                                   AS2DispositionType dispositionType,
+                                                   AS2DispositionModifier dispositionModifier,
+                                                   String[] failureFields,
+                                                   String[] errorFields,
+                                                   String[] warningFields,
+                                                   Map<String, String> extensionFields) throws HttpException {
+
+        CharArrayBuffer charBuffer = new CharArrayBuffer(10);
+
+        String originalMessageId  = HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID);
+        String sentDate = HttpMessageUtils.getHeaderValue(request, AS2Header.DATE);
+        String subject = HttpMessageUtils.getHeaderValue(request, AS2Header.SUBJECT);
+        
+        String receivedFrom = HttpMessageUtils.getHeaderValue(request, AS2Header.AS2_FROM);
+        String sentTo = HttpMessageUtils.getHeaderValue(request, AS2Header.AS2_TO);
+        
+        String receivedDate = HttpMessageUtils.getHeaderValue(response, AS2Header.DATE);
+        
+        charBuffer.append("MDN for -\n");
+        charBuffer.append(" Message ID: " + originalMessageId + "\n");
+        charBuffer.append("  Subject: " + (subject == null ? "" : subject) + "\n");
+        charBuffer.append("  Date: " + (sentDate == null ? "" : sentDate) + "\n");
+        charBuffer.append("  From: " + receivedFrom + "\n");
+        charBuffer.append("  To: " + sentTo + "\n");
+        charBuffer.append("  Received on: " + receivedDate + "\n");
+        charBuffer.append(" Status: " + dispositionType + "\n");
+
+        return new TextPlainEntity(charBuffer.toString(), AS2Charset.US_ASCII, AS2TransferEncoding.SEVENBIT, false);
+    }
+    
+    protected String getReportTypeValue(String boundary) {
+        return "disposition-notification; boundary=\"" + boundary + "\"";
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptions.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptions.java
new file mode 100644
index 0000000..ca9c628
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptions.java
@@ -0,0 +1,39 @@
+/**
+ * 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.entity;
+
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils.Parameter;
+
+public class DispositionNotificationOptions {
+    
+    private Parameter signedReceiptProtocol;
+    private Parameter signedReceiptMicalg;
+    
+    public DispositionNotificationOptions(Parameter signedReceiptProtocol, Parameter signedReceiptMicalg) {
+        this.signedReceiptProtocol = signedReceiptProtocol;
+        this.signedReceiptMicalg = signedReceiptMicalg;
+    }
+
+    public Parameter getSignedReceiptProtocol() {
+        return signedReceiptProtocol;
+    }
+
+    public Parameter getSignedReceiptMicalg() {
+        return signedReceiptMicalg;
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptionsParser.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptionsParser.java
new file mode 100644
index 0000000..5688c58
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptionsParser.java
@@ -0,0 +1,69 @@
+/**
+ * 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.entity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils;
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils.Parameter;
+import org.apache.http.ParseException;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.Args;
+import org.apache.http.util.CharArrayBuffer;
+
+public class DispositionNotificationOptionsParser {
+
+    public static final DispositionNotificationOptionsParser INSTANCE = new DispositionNotificationOptionsParser();
+
+    private static final String SIGNED_RECEIPT_PROTOCOL_ATTR_NAME = "signed-receipt-protocol";
+    private static final String SIGNED_RECEIPT_MICALG_ATTR_NAME = "signed-receipt-micalg";
+
+    public static DispositionNotificationOptions parseDispositionNotificationOptions(final String value,
+                                                                                     DispositionNotificationOptionsParser parser)
+            throws ParseException {
+        if (value == null) {
+            return new DispositionNotificationOptions(null, null);
+        }
+
+        final CharArrayBuffer buffer = new CharArrayBuffer(value.length());
+        buffer.append(value);
+        final ParserCursor cursor = new ParserCursor(0, value.length());
+        return (parser != null ? parser : DispositionNotificationOptionsParser.INSTANCE)
+                .parseDispositionNotificationOptions(buffer, cursor);
+    }
+
+    public DispositionNotificationOptions parseDispositionNotificationOptions(final CharArrayBuffer buffer,
+                                                                              final ParserCursor cursor) {
+        Args.notNull(buffer, "buffer");
+        Args.notNull(cursor, "cursor");
+        
+        Map<String, Parameter> parameters = new HashMap<String, Parameter>();
+        while (!cursor.atEnd()) {
+            Parameter parameter = AS2HeaderUtils.parseParameter(buffer, cursor);
+            parameters.put(parameter.getAttribute(), parameter);
+        }
+        
+        Parameter signedReceiptProtocolParameter = parameters.get(SIGNED_RECEIPT_PROTOCOL_ATTR_NAME);
+        
+        Parameter signedReceiptMicalgParameter = parameters.get(SIGNED_RECEIPT_MICALG_ATTR_NAME);
+        
+        
+        return new DispositionNotificationOptions(signedReceiptProtocolParameter, signedReceiptMicalgParameter);
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
new file mode 100644
index 0000000..28d0715
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
@@ -0,0 +1,854 @@
+/**
+ * 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.entity;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.io.AS2SessionInputBuffer;
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils;
+import org.apache.camel.component.as2.api.util.ContentTypeUtils;
+import org.apache.camel.component.as2.api.util.DispositionNotificationContentUtils;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.ParseException;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.io.AbstractMessageParser;
+import org.apache.http.impl.io.HttpTransportMetricsImpl;
+import org.apache.http.impl.io.SessionInputBufferImpl;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.Args;
+import org.apache.http.util.CharArrayBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class EntityParser {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(EntityParser.class);
+
+    private static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
+    private static final String APPLICATION_EDI_CONTENT_TYPE_PREFIX = "application/edi";
+
+    
+    private EntityParser() {
+    }
+
+    public static boolean isBoundaryCloseDelimiter(final CharArrayBuffer buffer, ParserCursor cursor, String boundary) {
+        Args.notNull(buffer, "Buffer");
+        Args.notNull(boundary, "Boundary");
+
+        String boundaryCloseDelimiter = "--" + boundary + "--"; // boundary
+                                                                // close-delimiter
+                                                                // - RFC2046
+                                                                // 5.1.1
+
+        if (cursor == null) {
+            cursor = new ParserCursor(0, boundaryCloseDelimiter.length());
+        }
+
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+
+        if ((indexFrom + boundaryCloseDelimiter.length()) > indexTo) {
+            return false;
+        }
+
+        for (int i = indexFrom; i < indexTo; ++i) {
+            if (buffer.charAt(i) != boundaryCloseDelimiter.charAt(i)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static boolean isBoundaryDelimiter(final CharArrayBuffer buffer, ParserCursor cursor, String boundary) {
+        Args.notNull(buffer, "Buffer");
+        Args.notNull(boundary, "Boundary");
+
+        String boundaryDelimiter = "--" + boundary; // boundary delimiter -
+                                                    // RFC2046 5.1.1
+
+        if (cursor == null) {
+            cursor = new ParserCursor(0, boundaryDelimiter.length());
+        }
+
+        int indexFrom = cursor.getPos();
+        int indexTo = cursor.getUpperBound();
+
+        if ((indexFrom + boundaryDelimiter.length()) > indexTo) {
+            return false;
+        }
+
+        for (int i = indexFrom; i < indexTo; ++i) {
+            if (buffer.charAt(i) != boundaryDelimiter.charAt(i)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static void skipPreambleAndStartBoundary(AS2SessionInputBuffer inbuffer, String boundary)
+            throws HttpException {
+
+        boolean foundStartBoundary;
+        try {
+            foundStartBoundary = false;
+            CharArrayBuffer lineBuffer = new CharArrayBuffer(1024);
+            while (inbuffer.readLine(lineBuffer) != -1) {
+                final ParserCursor cursor = new ParserCursor(0, lineBuffer.length());
+                if (isBoundaryDelimiter(lineBuffer, cursor, boundary)) {
+                    foundStartBoundary = true;
+                    break;
+                }
+                lineBuffer.clear();
+            }
+        } catch (Exception e) {
+            throw new HttpException("Failed to read start boundary for body part", e);
+        }
+
+        if (!foundStartBoundary) {
+            throw new HttpException("Failed to find start boundary for body part");
+        }
+
+    }
+
+    public static void skipToBoundary(AS2SessionInputBuffer inbuffer, String boundary)
+            throws HttpException {
+
+        boolean foundEndBoundary;
+        try {
+            foundEndBoundary = false;
+            CharArrayBuffer lineBuffer = new CharArrayBuffer(1024);
+            while (inbuffer.readLine(lineBuffer) != -1) {
+                final ParserCursor cursor = new ParserCursor(0, lineBuffer.length());
+                if (isBoundaryDelimiter(lineBuffer, cursor, boundary)) {
+                    foundEndBoundary = true;
+                    break;
+                }
+                lineBuffer.clear();
+            }
+        } catch (Exception e) {
+            throw new HttpException("Failed to read start boundary for body part", e);
+        }
+
+        if (!foundEndBoundary) {
+            throw new HttpException("Failed to find start boundary for body part");
+        }
+
+    }
+
+    public static void parseMultipartSignedEntity(HttpMessage message)
+            throws HttpException {
+        MultipartSignedEntity multipartSignedEntity = null;
+        HttpEntity entity = Args.notNull(EntityUtils.getMessageEntity(message), "message entity");
+
+        if (entity instanceof MultipartSignedEntity) {
+            return;
+        }
+
+        Args.check(entity.isStreaming(), "Entity is not streaming");
+
+        try {
+ 
+            // Determine and validate the Content Type
+            Header contentTypeHeader = entity.getContentType();
+            if (contentTypeHeader == null) {
+                throw new HttpException("Content-Type header is missing");
+            }
+            ContentType contentType = ContentType.parse(entity.getContentType().getValue());
+            if (!contentType.getMimeType().equals(AS2MimeType.MULTIPART_SIGNED)) {
+                throw new HttpException("Entity has invalid MIME type '" + contentType.getMimeType() + "'");
+            }
+            
+            // Determine Charset
+            String charsetName = AS2Charset.US_ASCII;
+            Charset charset = contentType.getCharset();
+            if (charset != null) {
+                charsetName = charset.name();
+            }
+            
+            // Determine content transfer encoding
+            String contentTransferEncoding = HttpMessageUtils.getHeaderValue(message, AS2Header.CONTENT_TRANSFER_ENCODING);
+
+            AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), DEFAULT_BUFFER_SIZE);
+            inbuffer.bind(entity.getContent());
+
+            // Get Boundary Value
+            String boundary = HttpMessageUtils.getBoundaryParameterValue(message, AS2Header.CONTENT_TYPE);
+            if (boundary == null) {
+                throw new HttpException("Failed to retrive boundary value");
+            }
+            
+            multipartSignedEntity = parseMultipartSignedEntityBody(inbuffer, boundary, charsetName, contentTransferEncoding);
+            
+            EntityUtils.setMessageEntity(message, multipartSignedEntity);
+            
+        } catch (HttpException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HttpException("Failed to parse entity content", e);
+        }
+    }
+    
+    public static void parseApplicationEDIEntity(HttpMessage message) throws HttpException {
+        ApplicationEDIEntity applicationEDIEntity = null;
+        HttpEntity entity = Args.notNull(EntityUtils.getMessageEntity(message), "message entity");
+
+        if (entity instanceof ApplicationEDIEntity) {
+            return;
+        }
+
+        Args.check(entity.isStreaming(), "Entity is not streaming");
+
+        try {
+
+            // Determine and validate the Content Type
+            Header contentTypeHeader = entity.getContentType();
+            if (contentTypeHeader == null) {
+                throw new HttpException("Content-Type header is missing");
+            }
+            ContentType contentType = ContentType.parse(entity.getContentType().getValue());
+            if (!contentType.getMimeType().startsWith(EntityParser.APPLICATION_EDI_CONTENT_TYPE_PREFIX)) {
+                throw new HttpException("Entity has invalid MIME type '" + contentType.getMimeType() + "'");
+            }
+
+            // Determine Transfer Encoding
+            Header transferEncoding = entity.getContentEncoding();
+            String contentTransferEncoding = transferEncoding == null ? null : transferEncoding.getValue();
+
+            SessionInputBufferImpl inBuffer = new SessionInputBufferImpl(new HttpTransportMetricsImpl(), 8 * 1024);
+            inBuffer.bind(entity.getContent());
+
+            // Extract content from stream
+            CharArrayBuffer lineBuffer = new CharArrayBuffer(1024);
+            while (inBuffer.readLine(lineBuffer) != -1) {
+                lineBuffer.append("\r\n"); // add line delimiter
+            }
+
+            // Build application EDI entity
+            applicationEDIEntity = EntityUtils.createEDIEntity(lineBuffer.toString(), contentType,
+                    contentTransferEncoding, true);
+
+            EntityUtils.setMessageEntity(message, applicationEDIEntity);
+        } catch (HttpException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HttpException("Failed to parse entity content", e);
+        }
+    }
+
+    public static void parseMessageDispositionNotificationReportEntity(HttpMessage message)
+            throws HttpException {
+        DispositionNotificationMultipartReportEntity dispositionNotificationMultipartReportEntity = null;
+        HttpEntity entity = Args.notNull(EntityUtils.getMessageEntity(message), "message entity");
+
+        if (entity instanceof DispositionNotificationMultipartReportEntity) {
+            return;
+        }
+
+        Args.check(entity.isStreaming(), "Entity is not streaming");
+
+        try {
+
+            // Determine and validate the Content Type
+            Header contentTypeHeader = entity.getContentType();
+            if (contentTypeHeader == null) {
+                throw new HttpException("Content-Type header is missing");
+            }
+            ContentType contentType = ContentType.parse(entity.getContentType().getValue());
+            if (!contentType.getMimeType().equals(AS2MimeType.MULTIPART_REPORT)) {
+                throw new HttpException("Entity has invalid MIME type '" + contentType.getMimeType() + "'");
+            }
+            
+            // Determine Charset
+            String charsetName = AS2Charset.US_ASCII;
+            Charset charset = contentType.getCharset();
+            if (charset != null) {
+                charsetName = charset.name();
+            }
+            
+            // Determine content transfer encoding
+            String contentTransferEncoding = HttpMessageUtils.getHeaderValue(message, AS2Header.CONTENT_TRANSFER_ENCODING);
+
+            AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), 8 * 1024);
+            inbuffer.bind(entity.getContent());
+
+            // Get Boundary Value
+            String boundary = HttpMessageUtils.getBoundaryParameterValue(message, AS2Header.REPORT_TYPE);
+            if (boundary == null) {
+                throw new HttpException("Failed to retrive boundary value");
+            }
+            
+            dispositionNotificationMultipartReportEntity = parseMultipartReportEntityBody(inbuffer, boundary, charsetName, contentTransferEncoding);
+
+            EntityUtils.setMessageEntity(message, dispositionNotificationMultipartReportEntity);
+
+        } catch (HttpException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HttpException("Failed to parse entity content", e);
+        }
+    }
+
+    public static void parseAS2MessageEntity(HttpMessage message) throws HttpException {
+        if (EntityUtils.hasEntity(message)) {
+            String contentTypeStr =  HttpMessageUtils.getHeaderValue(message, AS2Header.CONTENT_TYPE);
+            if (contentTypeStr != null) {
+                ContentType contentType;
+                try {
+                    contentType = ContentType.parse(contentTypeStr);
+                } catch (Exception e) {
+                    LOG.debug("Failed to get content type of message", e);
+                    return;
+                }
+                switch (contentType.getMimeType().toLowerCase()) {
+                case AS2MimeType.APPLICATION_EDIFACT:
+                case AS2MimeType.APPLICATION_EDI_X12:
+                case AS2MimeType.APPLICATION_EDI_CONSENT:
+                    parseApplicationEDIEntity(message);
+                    break;
+                case AS2MimeType.MULTIPART_SIGNED:
+                    parseMultipartSignedEntity(message);
+                    break;
+                case AS2MimeType.APPLICATION_PKCS7_MIME:
+                    break;
+                case AS2MimeType.MULTIPART_REPORT:
+                    parseMessageDispositionNotificationReportEntity(message);
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+    }
+    
+    public static MultipartSignedEntity parseMultipartSignedEntityBody(AS2SessionInputBuffer inbuffer,
+                                                                       String boundary,
+                                                                       String charsetName,
+                                                                       String contentTransferEncoding)
+            throws ParseException {
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+
+        try {
+
+            if (charsetName == null) {
+                charsetName = AS2Charset.US_ASCII;
+            }
+            Charset charset = Charset.forName(charsetName);
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+            
+            MultipartSignedEntity multipartSignedEntity = new MultipartSignedEntity(boundary, false);            
+
+            // Skip Preamble and Start Boundary line
+            skipPreambleAndStartBoundary(inbuffer, boundary);
+            
+            //
+            // Parse Signed Entity Part
+            // 
+            
+            // Read Text Report Body Part Headers
+            Header[] headers = AbstractMessageParser.parseHeaders(inbuffer, -1, -1, BasicLineParser.INSTANCE,
+                    new ArrayList<CharArrayBuffer>());
+
+            // Get Content-Type and Content-Transfer-Encoding
+            ContentType signedEntityContentType = null;
+            String signedEntityContentTransferEncoding = null;
+            for (Header header : headers) {
+                switch (header.getName()) {
+                case AS2Header.CONTENT_TYPE:
+                    signedEntityContentType = ContentType.parse(header.getValue());
+                    break;
+                case AS2Header.CONTENT_TRANSFER_ENCODING:
+                    signedEntityContentTransferEncoding = header.getValue();
+                    break;
+                default:
+                    continue;
+                }
+            }
+            if (signedEntityContentType == null) {
+                throw new HttpException("Failed to find Content-Type header in signed entity body part");
+            }
+ 
+            MimeEntity signedEntity = parseEntityBody(inbuffer, boundary, signedEntityContentType, signedEntityContentTransferEncoding, headers);
+            signedEntity.removeAllHeaders();
+            signedEntity.setHeaders(headers);
+            multipartSignedEntity.addPart(signedEntity);
+            
+            //
+            // End Signed Entity Part
+
+            //
+            // Parse Signature Body Part
+            //
+
+            // Read Signature Body Part Headers
+            headers = AbstractMessageParser.parseHeaders(inbuffer, -1, -1, BasicLineParser.INSTANCE,
+                    new ArrayList<CharArrayBuffer>());
+
+            // Get Content-Type and Content-Transfer-Encoding
+            ContentType signatureContentType = null;
+            String signatureContentTransferEncoding = null;
+            for (Header header : headers) {
+                switch (header.getName()) {
+                case AS2Header.CONTENT_TYPE:
+                    signatureContentType = ContentType.parse(header.getValue());
+                    break;
+                case AS2Header.CONTENT_TRANSFER_ENCODING:
+                    signatureContentTransferEncoding = header.getValue();
+                    break;
+                default:
+                    continue;
+                }
+            }
+            if (signatureContentType == null) {
+                throw new HttpException("Failed to find Content-Type header in signature body part");
+            }
+            if (!ContentTypeUtils.isPkcs7SignatureType(signatureContentType)) {
+                throw new HttpException(
+                        "Invalid content type '" + signatureContentType.getMimeType() + "' for signature body part");
+            }
+            
+            ApplicationPkcs7SignatureEntity applicationPkcs7SignatureEntity = parseApplicationPkcs7SignatureEntityBody(inbuffer, boundary, signatureContentType, signatureContentTransferEncoding);
+            applicationPkcs7SignatureEntity.removeAllHeaders();
+            applicationPkcs7SignatureEntity.setHeaders(headers);
+            multipartSignedEntity.addPart(applicationPkcs7SignatureEntity);
+
+            return multipartSignedEntity;
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse text entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+    }
+
+    public static DispositionNotificationMultipartReportEntity parseMultipartReportEntityBody(AS2SessionInputBuffer inbuffer,
+                                                                                                                 String boundary,
+                                                                                                                 String charsetName,
+                                                                                                                 String contentTransferEncoding)
+            throws ParseException {
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+
+        try {
+
+            if (charsetName == null) {
+                charsetName = AS2Charset.US_ASCII;
+            }
+            Charset charset = Charset.forName(charsetName);
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+
+            DispositionNotificationMultipartReportEntity dispositionNotificationMultipartReportEntity = new DispositionNotificationMultipartReportEntity(boundary, false);
+            
+            // Skip Preamble and Start Boundary line
+            skipPreambleAndStartBoundary(inbuffer, boundary);
+
+            //
+            // Parse Text Report Body Part
+            //
+
+            // Read Text Report Body Part Headers
+            Header[] headers = AbstractMessageParser.parseHeaders(inbuffer, -1, -1, BasicLineParser.INSTANCE,
+                    new ArrayList<CharArrayBuffer>());
+
+            // Get Content-Type and Content-Transfer-Encoding
+            ContentType textReportContentType = null;
+            String textReportContentTransferEncoding = null;
+            for (Header header : headers) {
+                switch (header.getName()) {
+                case AS2Header.CONTENT_TYPE:
+                    textReportContentType = ContentType.parse(header.getValue());
+                    break;
+                case AS2Header.CONTENT_TRANSFER_ENCODING:
+                    textReportContentTransferEncoding = header.getValue();
+                    break;
+                default:
+                    continue;
+                }
+            }
+            if (textReportContentType == null) {
+                throw new HttpException("Failed to find Content-Type header in EDI message body part");
+            }
+            if (!textReportContentType.getMimeType().equalsIgnoreCase(AS2MimeType.TEXT_PLAIN)) {
+                throw new HttpException("Invalid content type '" + textReportContentType.getMimeType()
+                        + "' for first body part of disposition notification");
+            }
+            
+            String textReportCharsetName = textReportContentType.getCharset() == null ? AS2Charset.US_ASCII : textReportContentType.getCharset().name();
+            TextPlainEntity textReportEntity = parseTextPlainEntityBody(inbuffer, boundary, textReportCharsetName, textReportContentTransferEncoding);
+            textReportEntity.setHeaders(headers);
+            dispositionNotificationMultipartReportEntity.addPart(textReportEntity);
+
+            //
+            // End Text Report Body Part
+
+            //
+            // Parse Disposition Notification Body Part
+            //
+
+            // Read Disposition Notification Body Part Headers
+            headers = AbstractMessageParser.parseHeaders(inbuffer, -1, -1, BasicLineParser.INSTANCE,
+                    new ArrayList<CharArrayBuffer>());
+
+            // Get Content-Type and Content-Transfer-Encoding
+            ContentType dispositionNotificationContentType = null;
+            String dispositionNotificationContentTransferEncoding = null;
+            for (Header header : headers) {
+                switch (header.getName()) {
+                case AS2Header.CONTENT_TYPE:
+                    dispositionNotificationContentType = ContentType.parse(header.getValue());
+                    break;
+                case AS2Header.CONTENT_TRANSFER_ENCODING:
+                    dispositionNotificationContentTransferEncoding = header.getValue();
+                    break;
+                default:
+                    continue;
+                }
+            }
+            if (dispositionNotificationContentType == null) {
+                throw new HttpException("Failed to find Content-Type header in body part");
+            }
+            if (!dispositionNotificationContentType.getMimeType()
+                    .equalsIgnoreCase(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION)) {
+                throw new HttpException("Invalid content type '" + dispositionNotificationContentType.getMimeType()
+                        + "' for second body part of disposition notification");
+            }
+
+            String dispositionNotificationCharsetName = dispositionNotificationContentType.getCharset() == null ? AS2Charset.US_ASCII : dispositionNotificationContentType.getCharset().name();
+            AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity = parseMessageDispositionNotificationEntityBody(
+                    inbuffer, boundary, dispositionNotificationCharsetName, dispositionNotificationContentTransferEncoding);
+            messageDispositionNotificationEntity.setHeaders(headers);
+            dispositionNotificationMultipartReportEntity.addPart(messageDispositionNotificationEntity);
+
+            //
+            // End Disposition Notification Body Part
+          
+            return dispositionNotificationMultipartReportEntity;
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse text entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+
+    }
+
+    public static TextPlainEntity parseTextPlainEntityBody(AS2SessionInputBuffer inbuffer,
+                                                       String boundary,
+                                                       String charsetName,
+                                                       String contentTransferEncoding)
+            throws ParseException {
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+
+        try {
+
+            if (charsetName == null) {
+                charsetName = AS2Charset.US_ASCII;
+            }
+            Charset charset = Charset.forName(charsetName);
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+            
+            String text = parseBodyPartText(inbuffer, boundary);
+            return new TextPlainEntity(text, charsetName, contentTransferEncoding, false);
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse text entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+    }
+
+    public static AS2MessageDispositionNotificationEntity parseMessageDispositionNotificationEntityBody(AS2SessionInputBuffer inbuffer,
+                                                                                              String boundary,
+                                                                                              String charsetName,
+                                                                                              String contentTransferEncoding)
+            throws ParseException {
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+
+        try {
+
+            if (charsetName == null) {
+                charsetName = AS2Charset.US_ASCII;
+            }
+            Charset charset = Charset.forName(charsetName);
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+
+            List<CharArrayBuffer> dispositionNotificationFields = parseBodyPartFields(inbuffer, boundary,
+                    BasicLineParser.INSTANCE, new ArrayList<CharArrayBuffer>());
+
+            AS2MessageDispositionNotificationEntity as2MessageDispositionNotificationEntity = DispositionNotificationContentUtils.parseDispositionNotification(dispositionNotificationFields);
+            ContentType contentType = ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, charset);
+            as2MessageDispositionNotificationEntity.setContentType(contentType);
+            return as2MessageDispositionNotificationEntity;
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse MDN entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+    }
+    
+    public static MimeEntity parseEntityBody(AS2SessionInputBuffer inbuffer,
+                                             String boundary,
+                                             ContentType entityContentType,
+                                             String contentTransferEncoding,
+                                             Header[] headers)
+            throws ParseException {
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+
+        try {
+            Charset charset = entityContentType.getCharset();
+            if (charset == null) {
+                charset = Charset.forName(AS2Charset.US_ASCII);
+            }
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+
+            MimeEntity entity = null;
+            switch (entityContentType.getMimeType().toLowerCase()) {
+            case AS2MimeType.APPLICATION_EDIFACT:
+            case AS2MimeType.APPLICATION_EDI_X12:
+            case AS2MimeType.APPLICATION_EDI_CONSENT:
+                entity = parseEDIEntityBody(inbuffer, boundary, entityContentType, contentTransferEncoding);
+                break;
+            case AS2MimeType.MULTIPART_SIGNED:
+                String multipartSignedBoundary = AS2HeaderUtils.getBoundaryParameterValue(headers,
+                        AS2Header.CONTENT_TYPE);
+                entity = parseMultipartSignedEntityBody(inbuffer, multipartSignedBoundary, charset.name(),
+                        contentTransferEncoding);
+                skipToBoundary(inbuffer, boundary);
+                break;
+            case AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION:
+                entity = parseMessageDispositionNotificationEntityBody(inbuffer, boundary, charset.name(),
+                        contentTransferEncoding);
+                break;
+            case AS2MimeType.MULTIPART_REPORT:
+                String multipartReportBoundary = AS2HeaderUtils.getBoundaryParameterValue(headers,
+                        AS2Header.REPORT_TYPE);
+                entity = parseMultipartReportEntityBody(inbuffer, multipartReportBoundary, charset.name(),
+                        contentTransferEncoding);
+                skipToBoundary(inbuffer, boundary);
+                break;
+            case AS2MimeType.TEXT_PLAIN:
+                entity = parseTextPlainEntityBody(inbuffer, boundary, charset.name(), previousContentTransferEncoding);
+                break;
+            case AS2MimeType.APPLICATION_PKCS7_SIGNATURE:
+                entity = parseApplicationPkcs7SignatureEntityBody(inbuffer, boundary, entityContentType,
+                        contentTransferEncoding);
+                break;
+            default:
+                break;
+            }
+
+            return entity;
+
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse EDI entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+
+    }
+    
+    public static ApplicationEDIEntity parseEDIEntityBody(AS2SessionInputBuffer inbuffer,
+                                                          String boundary,
+                                                          ContentType ediMessageContentType,
+                                                          String contentTransferEncoding)
+            throws ParseException {
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+
+        try {
+            Charset charset = ediMessageContentType.getCharset();
+            if (charset == null) {
+                charset = Charset.forName(AS2Charset.US_ASCII);
+            }
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+
+            String ediMessageBodyPartContent = parseBodyPartText(inbuffer, boundary);
+            ApplicationEDIEntity applicationEDIEntity = EntityUtils.createEDIEntity(ediMessageBodyPartContent,
+                    ediMessageContentType, contentTransferEncoding, false);
+
+            return applicationEDIEntity;
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse EDI entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+    }
+    
+    public static ApplicationPkcs7SignatureEntity parseApplicationPkcs7SignatureEntityBody(AS2SessionInputBuffer inbuffer,
+                                                                                           String boundary,
+                                                                                           ContentType contentType,
+                                                                                           String contentTransferEncoding) throws ParseException {
+        
+        CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+        String previousContentTransferEncoding = inbuffer.getTransferEncoding();
+        
+        try {
+            Charset charset = contentType.getCharset();
+            if (charset == null) {
+                charset = Charset.forName(AS2Charset.US_ASCII);
+            }
+            CharsetDecoder charsetDecoder = charset.newDecoder();
+
+            inbuffer.setCharsetDecoder(charsetDecoder);
+            inbuffer.setTransferEncoding(contentTransferEncoding);
+
+            String pkcs7SignatureBodyContent = parseBodyPartText(inbuffer, boundary);
+            
+            String charsetName = charset.toString();
+            ApplicationPkcs7SignatureEntity applicationPkcs7SignatureEntity = new ApplicationPkcs7SignatureEntity(
+                    charsetName, contentTransferEncoding, pkcs7SignatureBodyContent.getBytes(charset), false);
+            return applicationPkcs7SignatureEntity;
+        } catch (Exception e) {
+            ParseException parseException = new ParseException("failed to parse PKCS7 Signature entity");
+            parseException.initCause(e);
+            throw parseException;
+        } finally {
+            inbuffer.setCharsetDecoder(previousDecoder);
+            inbuffer.setTransferEncoding(previousContentTransferEncoding);
+        }
+    }
+    
+    public static String parseBodyPartText(final AS2SessionInputBuffer inbuffer,
+                                           final String boundary)
+            throws IOException {
+        CharArrayBuffer buffer = new CharArrayBuffer(DEFAULT_BUFFER_SIZE);
+        CharArrayBuffer line = new CharArrayBuffer(DEFAULT_BUFFER_SIZE);
+        while (true) {
+            final int l = inbuffer.readLine(line);
+            if (l == -1) {
+                break;
+            }
+
+            if (boundary != null && isBoundaryDelimiter(line, null, boundary)) {
+                // remove last CRLF from buffer which belongs to boundary
+                int length = buffer.length();
+                buffer.setLength(length - 2);
+                break;
+            }
+            
+            buffer.append(line);
+            buffer.append("\r\n");
+            line.clear();
+        }
+        
+        return buffer.toString();
+    }
+
+    public static List<CharArrayBuffer> parseBodyPartFields(final AS2SessionInputBuffer inbuffer,
+                                                           final String boundary,
+                                                           final LineParser parser,
+                                                           final List<CharArrayBuffer> headerLines)
+            throws IOException {
+        Args.notNull(parser, "parser");
+        Args.notNull(headerLines, "headerLines");
+        CharArrayBuffer current = null;
+        CharArrayBuffer previous = null;
+        while (true) {
+
+            if (current == null) {
+                current = new CharArrayBuffer(64);
+            }
+
+            final int l = inbuffer.readLine(current);
+            if (l == -1 || current.length() < 1) {
+                break;
+            }
+
+            if (boundary != null && isBoundaryDelimiter(current, null, boundary)) {
+                break;
+            }
+
+            // check if current line part of folded headers
+            if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
+                // we have continuation of folded header : append value
+                int i = 0;
+                while (i < current.length()) {
+                    final char ch = current.charAt(i);
+                    if (ch != ' ' && ch != '\t') {
+                        break;
+                    }
+                    i++;
+                }
+
+                // Just append current line to previous line
+                previous.append(' ');
+                previous.append(current, i, current.length() - i);
+
+                // leave current line buffer for reuse for next header
+                current.clear();
+            } else {
+                headerLines.add(current);
+                previous = current;
+                current = null;
+            }
+        }
+        return headerLines;
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/Importance.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/Importance.java
new file mode 100644
index 0000000..d22ac00
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/Importance.java
@@ -0,0 +1,53 @@
+/**
+ * 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.entity;
+
+public enum Importance {
+    REQUIRED("required"),
+    OPTIONAL("optional");
+    
+    private String importance;
+    
+    private Importance(String importance) {
+        this.importance = importance;
+    }
+    
+    public String getImportance() {
+        return importance;
+    }
+    
+    @Override
+    public String toString() {
+        return importance;
+    }
+
+    public static Importance get(String importance) {
+        if (importance == null) {
+            return null;
+        }
+
+        switch (importance.toLowerCase()) {
+        case "required":
+            return REQUIRED;
+        case "optional":
+            return OPTIONAL;
+        default:
+            return null;
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MimeEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MimeEntity.java
new file mode 100644
index 0000000..157d8cd
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MimeEntity.java
@@ -0,0 +1,277 @@
+/**
+ * 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.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.entity.AbstractHttpEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.HeaderGroup;
+import org.apache.http.util.Args;
+
+public abstract class MimeEntity extends AbstractHttpEntity {
+    
+    /**
+     * An OuputStream wrapper that doesn't close its underlying output stream.
+     * <p>
+     * Instances of this stream are used by entities to attach encoding streams
+     * to underlying output stream in order to write out their encoded content
+     * and then flush and close these encoding streams without closing the
+     * underlying output stream.
+     */
+    protected static class NoCloseOutputStream extends FilterOutputStream {
+        public NoCloseOutputStream(OutputStream os) {
+            super(os);
+        }
+
+        public void close() {
+            // do nothing
+        }
+    }
+
+    protected static final long UNKNOWN_CONTENT_LENGTH = -1;
+    
+    protected static final long RECALCULATE_CONTENT_LENGTH = -2;
+
+    protected boolean isMainBody;
+
+    protected Header contentTransferEncoding;
+
+    protected long contentLength = RECALCULATE_CONTENT_LENGTH;
+
+    private final HeaderGroup headergroup = new HeaderGroup();
+    
+    protected MimeEntity() {
+    }
+    
+    public boolean isMainBody() {
+        return isMainBody;
+    }
+    
+    public void setMainBody(boolean isMainBody) {
+        this.isMainBody = isMainBody;
+    }
+    
+    public String getContentTypeValue() {
+        Header contentTypeHeader = getContentType();
+        if (contentTypeHeader != null) {
+            return contentTypeHeader.getValue();
+        }
+        return null;
+    }
+    
+    public void setContentType(ContentType contentType) {
+        super.setContentType(contentType == null ? null : contentType.toString());
+    }
+    
+    @Override
+    public void setContentType(Header contentType) {
+        super.setContentType(contentType);
+        addHeader(contentType);
+    }
+    
+    public String getContentEncodingValue() {
+        Header contentEncodingHeader = getContentEncoding();
+        if (contentEncodingHeader != null) {
+            return contentEncodingHeader.getValue();
+        }
+        return null;
+    }
+    
+    @Override
+    public void setContentEncoding(Header contentEncoding) {
+        super.setContentEncoding(contentEncoding);
+        addHeader(contentEncoding);
+    }
+
+    public String getContentTransferEncodingValue() {
+        Header contentTransferEncodingHeader = getContentTransferEncoding();
+        if (contentTransferEncodingHeader != null) {
+            return contentTransferEncodingHeader.getValue();
+        }
+        return null;
+    }
+    
+    /**
+     * Obtains the Content-Transfer-Encoding header.
+     * The default implementation returns the value of the
+     * {@link #contentEncoding contentTransferEncoding} attribute.
+     *
+     * @return  the Content-Transfer-Encoding header, or {@code null}
+     */
+    public Header getContentTransferEncoding() {
+        return this.contentTransferEncoding;
+    }
+
+    /**
+     * Specifies the Content-Transfer-Encoding header.
+     * The default implementation sets the value of the
+     * {@link #contentTranferEncoding contentTransferEncoding} attribute.
+     *
+     * @param contentEncoding   the new Content-Transfer-Encoding header, or
+     *                          {@code null} to unset
+     */
+    public void setContentTranserEncoding(final Header contentEncoding) {
+        this.contentTransferEncoding = contentEncoding;
+        addHeader(contentTransferEncoding);
+    }
+
+    /**
+     * Specifies the Content-Transfer-Encoding header, as a string.
+     * The default implementation calls
+     * {@link #setContentTransferEncoding(Header) setContentEncoding(Header)}.
+     *
+     * @param ceString     the new Content-Transfer-Encoding header, or
+     *                     {@code null} to unset
+     */
+    public void setContentTransferEncoding(final String contentTranserEncoding) {
+        Header h = null;
+        if (contentTranserEncoding != null) {
+            h = new BasicHeader(AS2Header.CONTENT_TRANSFER_ENCODING, contentTranserEncoding);
+        }
+        setContentTranserEncoding(h);
+    }
+
+
+    
+    public boolean containsHeader(final String name) {
+        return this.headergroup.containsHeader(name);
+    }
+
+    public Header[] getHeaders(final String name) {
+        return this.headergroup.getHeaders(name);
+    }
+
+    public Header getFirstHeader(final String name) {
+        return this.headergroup.getFirstHeader(name);
+    }
+
+    public Header getLastHeader(final String name) {
+        return this.headergroup.getLastHeader(name);
+    }
+
+    public Header[] getAllHeaders() {
+        return this.headergroup.getAllHeaders();
+    }
+
+    public void addHeader(final Header header) {
+        this.headergroup.addHeader(header);
+    }
+
+    public void addHeader(final String name, final String value) {
+        Args.notNull(name, "Header name");
+        this.headergroup.addHeader(new BasicHeader(name, value));
+    }
+
+    public void setHeader(final Header header) {
+        this.headergroup.updateHeader(header);
+    }
+
+    public void setHeader(final String name, final String value) {
+        Args.notNull(name, "Header name");
+        this.headergroup.updateHeader(new BasicHeader(name, value));
+    }
+
+    public void setHeaders(final Header[] headers) {
+        this.headergroup.setHeaders(headers);
+    }
+
+    public void removeHeader(final Header header) {
+        this.headergroup.removeHeader(header);
+    }
+
+    public void removeHeaders(final String name) {
+        if (name == null) {
+            return;
+        }
+        for (final HeaderIterator i = this.headergroup.iterator(); i.hasNext();) {
+            final Header header = i.nextHeader();
+            if (name.equalsIgnoreCase(header.getName())) {
+                i.remove();
+            }
+        }
+    }
+    
+    public void removeAllHeaders() {
+        this.headergroup.clear();
+    }
+
+    public HeaderIterator headerIterator() {
+        return this.headergroup.iterator();
+    }
+
+    public HeaderIterator headerIterator(final String name) {
+        return this.headergroup.iterator(name);
+    }
+
+    @Override
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    @Override
+    public boolean isStreaming() {
+        return !isRepeatable();
+    }
+    
+    @Override
+    public long getContentLength() {
+        if (contentLength == RECALCULATE_CONTENT_LENGTH) {
+            // Calculate content length
+            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            try {
+                writeTo(out);
+                contentLength = out.toByteArray().length;
+            } catch (IOException e) {
+                contentLength = MimeEntity.UNKNOWN_CONTENT_LENGTH;
+            }
+        }
+        return contentLength;
+    }
+
+    @Override
+    public InputStream getContent() throws IOException, UnsupportedOperationException {
+        final ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+        writeTo(outstream);
+        outstream.flush();
+        return new ByteArrayInputStream(outstream.toByteArray());
+    }
+
+    public String getCharset() {
+        if (getContentType() == null) {
+            return AS2Charset.US_ASCII;
+        }
+        ContentType contentType = ContentType.parse(getContentType().getValue());
+        Charset charset = contentType.getCharset();
+        if (charset != null) {
+            return charset.name();
+        }
+        return AS2Charset.US_ASCII;
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java
new file mode 100644
index 0000000..d4563aa
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java
@@ -0,0 +1,121 @@
+/**
+ * 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.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.component.as2.api.CanonicalOutputStream;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.entity.ContentType;
+
+public abstract class MultipartMimeEntity extends MimeEntity {
+
+    protected String boundary;
+
+    private final List<MimeEntity> parts = new ArrayList<MimeEntity>();
+
+    public MultipartMimeEntity(ContentType contentType) {
+        this(contentType, false, null);
+    }
+
+    public MultipartMimeEntity(ContentType contentType, boolean isMainBody) {
+        this(contentType, isMainBody, null);
+    }
+
+    public MultipartMimeEntity(ContentType contentType, boolean isMainBody, String boundary) {
+        setContentType(contentType);
+        setMainBody(isMainBody);
+        
+        if (boundary != null && EntityUtils.validateBoundaryValue(boundary)) {
+            this.boundary = boundary;
+        } else {
+            this.boundary = EntityUtils.createBoundaryValue();            
+        }
+
+    }
+    
+    protected MultipartMimeEntity() {
+    }
+
+    public void addPart(MimeEntity part) {
+        parts.add(part);
+        contentLength = RECALCULATE_CONTENT_LENGTH;
+    }
+
+    public MimeEntity getPart(int index) {
+        return parts.get(index);
+    }
+
+    public int getPartCount() {
+        return parts.size();
+    }
+
+    @Override
+    public long getContentLength() {
+        if (contentLength == RECALCULATE_CONTENT_LENGTH) {
+            // Need to (re)calculate content length
+
+            // See if their are any parts with unknown content lengths.
+            for (MimeEntity part : parts) {
+                long len = part.getContentLength();
+                if (len < 0) {
+                    contentLength = MimeEntity.UNKNOWN_CONTENT_LENGTH;
+                    return contentLength;
+                }
+            }
+
+            contentLength = super.getContentLength();
+        }
+        return contentLength;
+    }
+
+    @Override
+    public void writeTo(OutputStream outstream) throws IOException {
+        NoCloseOutputStream ncos = new NoCloseOutputStream(outstream);
+        try (CanonicalOutputStream canonicalOutstream = new CanonicalOutputStream(ncos, getCharset())) {
+
+            // Write out mime part headers if this is not the main body of message.
+            if (!isMainBody()) { 
+                HeaderIterator it = headerIterator();
+                while (it.hasNext()) {
+                    Header header = it.nextHeader();
+                    canonicalOutstream.writeln(header.toString());
+                }
+                canonicalOutstream.writeln(); // ensure empty line between headers and body; RFC2046 - 5.1.1
+            }
+        
+            // Write out each part separated by a boundary delimiter line
+            String boundary = "--" + this.boundary;
+            // Write out parts
+            for (MimeEntity part : parts) {
+                canonicalOutstream.writeln(boundary);
+                part.writeTo(outstream);
+                canonicalOutstream.writeln(); // ensure boundary occurs at the beginning of a line; RFC2046 - 5.1.1
+            }
+            
+            // Write out closing boundary delimiter line
+            canonicalOutstream.writeln(boundary + "--");
+            
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartReportEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartReportEntity.java
new file mode 100644
index 0000000..20855b2
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartReportEntity.java
@@ -0,0 +1,39 @@
+/**
+ * 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.entity;
+
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.http.HttpException;
+import org.apache.http.entity.ContentType;
+
+public class MultipartReportEntity extends MultipartMimeEntity {
+
+    public MultipartReportEntity(String charset,
+                                 boolean isMainBody,
+                                 String boundary)
+            throws HttpException {
+
+        super(ContentType.create(AS2MimeType.MULTIPART_REPORT, charset), isMainBody, boundary);
+
+    }
+    
+    protected MultipartReportEntity() {
+        
+    }
+
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java
new file mode 100644
index 0000000..af8e098
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java
@@ -0,0 +1,109 @@
+/**
+ * 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.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2SignedDataGenerator;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHeader;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.util.Store;
+
+public class MultipartSignedEntity extends MultipartMimeEntity {
+
+    public MultipartSignedEntity(MimeEntity data, AS2SignedDataGenerator signer, String signatureCharSet, String signatureTransferEncoding, boolean isMainBody, String boundary) throws Exception {
+        super(null, isMainBody, boundary);
+        ContentType contentType = signer.createMultipartSignedContentType(this.boundary);
+        this.contentType = new BasicHeader(AS2Header.CONTENT_TYPE, contentType.toString());
+        addPart(data);
+        ApplicationPkcs7SignatureEntity signature = new ApplicationPkcs7SignatureEntity(data, signer, signatureCharSet, signatureTransferEncoding, false);
+        addPart(signature);
+    }
+    
+    protected MultipartSignedEntity(String boundary, boolean isMainBody) {
+        this.boundary = boundary;
+        this.isMainBody = isMainBody;
+    }
+    
+    public boolean isValid()  {
+        ApplicationEDIEntity applicationEDIEntity = getSignedDataEntity();
+        ApplicationPkcs7SignatureEntity applicationPkcs7SignatureEntity = getSignatureEntity();
+        
+        if (applicationEDIEntity == null || applicationPkcs7SignatureEntity == null) {
+            return false;
+        }
+        
+        try {
+            ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+            applicationEDIEntity.writeTo(outstream);
+            CMSProcessable signedContent = new CMSProcessableByteArray(outstream.toByteArray());
+
+            byte[] signature = applicationPkcs7SignatureEntity.getSignature();
+            InputStream is = new ByteArrayInputStream(signature);
+
+            CMSSignedData signedData = new CMSSignedData(signedContent, is);
+
+            Store<X509CertificateHolder> store = signedData.getCertificates();
+            SignerInformationStore signers = signedData.getSignerInfos();
+
+            for (SignerInformation signer : signers.getSigners()) {
+                @SuppressWarnings("unchecked")
+                Collection<X509CertificateHolder> certCollection = store.getMatches(signer.getSID());
+
+                X509CertificateHolder certHolder = certCollection.iterator().next();
+                X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
+                if (!signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
+                    return false;
+                }
+            }
+        } catch (Exception e) {
+            return false;
+        }
+        
+        return true;
+    }
+    
+    public ApplicationEDIEntity getSignedDataEntity() {
+        if (getPartCount() > 0 && getPart(0) instanceof ApplicationEDIEntity) {
+            return (ApplicationEDIEntity)  getPart(0);
+        }
+        
+        return null;
+    }
+    
+    public ApplicationPkcs7SignatureEntity getSignatureEntity() {
+        if (getPartCount() > 1 && getPart(1) instanceof ApplicationPkcs7SignatureEntity) {
+            return (ApplicationPkcs7SignatureEntity)  getPart(1);
+        }
+        
+        return null;
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/TextPlainEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/TextPlainEntity.java
new file mode 100644
index 0000000..0061619
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/TextPlainEntity.java
@@ -0,0 +1,66 @@
+/**
+ * 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.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.CanonicalOutputStream;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.Args;
+
+public class TextPlainEntity extends MimeEntity {
+    
+    private String content;
+    
+    public TextPlainEntity(String content, String charset, String contentTransferEncoding, boolean isMainBody) {
+        this.content = Args.notNull(content, "Content");
+        setContentType(ContentType.create(AS2MediaType.TEXT_PLAIN, charset));
+        setContentTransferEncoding(contentTransferEncoding);
+        setMainBody(isMainBody);
+    }
+    
+    public String getText() {
+        return content;
+    }
+
+    @Override
+    public void writeTo(OutputStream outstream) throws IOException {
+        NoCloseOutputStream ncos = new NoCloseOutputStream(outstream);
+        try (CanonicalOutputStream canonicalOutstream = new CanonicalOutputStream(ncos, AS2Charset.US_ASCII)) {
+
+            // Write out mime part headers if this is not the main body of message.
+            if (!isMainBody()) { 
+                HeaderIterator it = headerIterator();
+                while (it.hasNext()) {
+                    Header header = it.nextHeader();
+                    canonicalOutstream.writeln(header.toString());
+                }
+                canonicalOutstream.writeln(); // ensure empty line between headers and body; RFC2046 - 5.1.1
+            }
+            
+            // Write out content
+            canonicalOutstream.write(content.getBytes(AS2Charset.US_ASCII), 0, content.length());
+        }            
+    }
+
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpClientConnection.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpClientConnection.java
new file mode 100644
index 0000000..86d4a9d
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpClientConnection.java
@@ -0,0 +1,64 @@
+/**
+ * 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.io;
+
+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.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.config.MessageConstraints;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.DefaultBHttpClientConnection;
+import org.apache.http.io.HttpMessageParserFactory;
+import org.apache.http.io.HttpMessageWriterFactory;
+
+public class AS2BHttpClientConnection extends DefaultBHttpClientConnection {
+
+    public AS2BHttpClientConnection(int buffersize,
+                                    CharsetDecoder chardecoder,
+                                    CharsetEncoder charencoder,
+                                    MessageConstraints constraints) {
+        super(buffersize, chardecoder, charencoder, constraints);
+    }
+
+    public AS2BHttpClientConnection(int buffersize,
+                                    int fragmentSizeHint,
+                                    CharsetDecoder chardecoder,
+                                    CharsetEncoder charencoder,
+                                    MessageConstraints constraints,
+                                    ContentLengthStrategy incomingContentStrategy,
+                                    ContentLengthStrategy outgoingContentStrategy,
+                                    HttpMessageWriterFactory<HttpRequest> requestWriterFactory,
+                                    HttpMessageParserFactory<HttpResponse> responseParserFactory) {
+        super(buffersize, fragmentSizeHint, chardecoder, charencoder, constraints, incomingContentStrategy,
+                outgoingContentStrategy, requestWriterFactory, responseParserFactory);
+    }
+
+    public AS2BHttpClientConnection(int buffersize) {
+        super(buffersize);
+    }
+
+    @Override
+    public void receiveResponseEntity(HttpResponse response) throws HttpException, IOException {
+        super.receiveResponseEntity(response);
+        EntityParser.parseAS2MessageEntity(response);
+    }
+}
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
new file mode 100644
index 0000000..9564e9b
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2BHttpServerConnection.java
@@ -0,0 +1,66 @@
+/**
+ * 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.io;
+
+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.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.config.MessageConstraints;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.DefaultBHttpServerConnection;
+import org.apache.http.io.HttpMessageParserFactory;
+import org.apache.http.io.HttpMessageWriterFactory;
+
+public class AS2BHttpServerConnection extends DefaultBHttpServerConnection {
+
+    public AS2BHttpServerConnection(int buffersize) {
+        super(buffersize);
+    }
+
+    public AS2BHttpServerConnection(int buffersize,
+                                    CharsetDecoder chardecoder,
+                                    CharsetEncoder charencoder,
+                                    MessageConstraints constraints) {
+        super(buffersize, chardecoder, charencoder, constraints);
+    }
+
+    public AS2BHttpServerConnection(int buffersize,
+                                    int fragmentSizeHint,
+                                    CharsetDecoder chardecoder,
+                                    CharsetEncoder charencoder,
+                                    MessageConstraints constraints,
+                                    ContentLengthStrategy incomingContentStrategy,
+                                    ContentLengthStrategy outgoingContentStrategy,
+                                    HttpMessageParserFactory<HttpRequest> requestParserFactory,
+                                    HttpMessageWriterFactory<HttpResponse> responseWriterFactory) {
+        super(buffersize, fragmentSizeHint, chardecoder, charencoder, constraints, incomingContentStrategy,
+                outgoingContentStrategy, requestParserFactory, responseWriterFactory);
+    }
+    
+    @Override
+    public void receiveRequestEntity(HttpEntityEnclosingRequest request) throws HttpException, IOException {
+        super.receiveRequestEntity(request);
+        EntityParser.parseAS2MessageEntity(request);
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2SessionInputBuffer.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2SessionInputBuffer.java
new file mode 100644
index 0000000..3ddc5dd
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/io/AS2SessionInputBuffer.java
@@ -0,0 +1,364 @@
+/**
+ * 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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.http.MessageConstraintException;
+import org.apache.http.config.MessageConstraints;
+import org.apache.http.impl.io.HttpTransportMetricsImpl;
+import org.apache.http.io.BufferInfo;
+import org.apache.http.io.HttpTransportMetrics;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
+import org.apache.http.util.ByteArrayBuffer;
+import org.apache.http.util.CharArrayBuffer;
+
+public class AS2SessionInputBuffer implements SessionInputBuffer, BufferInfo {
+
+    private final HttpTransportMetricsImpl metrics;
+    private final byte[] buffer;
+    private final ByteArrayBuffer linebuffer;
+    private final int minChunkLimit;
+    private final MessageConstraints constraints;
+
+    private CharsetDecoder decoder;
+    
+    private String transferEncoding;
+
+    private InputStream instream;
+    private int bufferpos;
+    private int bufferlen;
+    private CharBuffer cbuf;
+
+    public AS2SessionInputBuffer(final HttpTransportMetricsImpl metrics,
+                                 final int buffersize,
+                                 final int minChunkLimit,
+                                 MessageConstraints constraints) {
+        this.metrics = Args.notNull(metrics, "metrics");
+        Args.positive(buffersize, "buffersize");
+        this.buffer = new byte[buffersize];
+        this.bufferpos = 0;
+        this.bufferlen = 0;
+        this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512;
+        this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
+        this.linebuffer = new ByteArrayBuffer(buffersize);
+    }
+
+    public AS2SessionInputBuffer(final HttpTransportMetricsImpl metrics, final int buffersize) {
+        this(metrics, buffersize, buffersize, null);
+    }
+
+    public CharsetDecoder getCharsetDecoder() {
+        return decoder;
+    }
+
+    public void setCharsetDecoder(CharsetDecoder chardecoder) {
+        this.decoder = chardecoder;
+    }
+    
+    public String getTransferEncoding() {
+        return transferEncoding;
+    }
+
+    public void setTransferEncoding(String transferEncoding) {
+        this.transferEncoding = transferEncoding;
+    }
+
+    public void bind(final InputStream instream) {
+        this.instream = instream;
+    }
+
+    public boolean isBound() {
+        return this.instream != null;
+    }
+
+    @Override
+    public int length() {
+        return this.bufferlen - this.bufferpos;
+    }
+
+    @Override
+    public int capacity() {
+        return this.buffer.length;
+    }
+
+    @Override
+    public int available() {
+        return capacity() - length();
+    }
+
+    public int fillBuffer() throws IOException {
+        // compact the buffer if necessary
+        if (this.bufferpos > 0) {
+            final int len = this.bufferlen - this.bufferpos;
+            if (len > 0) {
+                System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
+            }
+            this.bufferpos = 0;
+            this.bufferlen = len;
+        }
+        final int l;
+        final int off = this.bufferlen;
+        final int len = this.buffer.length - off;
+        l = streamRead(this.buffer, off, len);
+        if (l == -1) {
+            return -1;
+        } else {
+            this.bufferlen = off + l;
+            this.metrics.incrementBytesTransferred(l);
+            return l;
+        }
+    }
+
+    public boolean hasBufferedData() {
+        return this.bufferpos < this.bufferlen;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (b == null) {
+            return 0;
+        }
+        if (hasBufferedData()) {
+            final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
+            System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
+            this.bufferpos += chunk;
+            return chunk;
+        }
+        // If the remaining capacity is big enough, read directly from the
+        // underlying input stream bypassing the buffer.
+        if (len > this.minChunkLimit) {
+            final int read = streamRead(b, off, len);
+            if (read > 0) {
+                this.metrics.incrementBytesTransferred(read);
+            }
+            return read;
+        } else {
+            // otherwise read to the buffer first
+            while (!hasBufferedData()) {
+                final int noRead = fillBuffer();
+                if (noRead == -1) {
+                    return -1;
+                }
+            }
+            final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
+            System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
+            this.bufferpos += chunk;
+            return chunk;
+        }
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        if (b == null) {
+            return 0;
+        }
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read() throws IOException {
+        int noRead;
+        while (!hasBufferedData()) {
+            noRead = fillBuffer();
+            if (noRead == -1) {
+                return -1;
+            }
+        }
+        return this.buffer[this.bufferpos++] & 0xff;
+    }
+
+    @Override
+    public int readLine(CharArrayBuffer charbuffer) throws IOException {
+        Args.notNull(charbuffer, "Char array buffer");
+        final int maxLineLen = this.constraints.getMaxLineLength();
+        int noRead = 0;
+        boolean retry = true;
+        while (retry) {
+            // attempt to find end of line (LF)
+            int pos = -1;
+            for (int i = this.bufferpos; i < this.bufferlen; i++) {
+                if (this.buffer[i] == HTTP.LF) {
+                    pos = i;
+                    break;
+                }
+            }
+
+            if (maxLineLen > 0) {
+                final int currentLen = this.linebuffer.length() + (pos > 0 ? pos : this.bufferlen) - this.bufferpos;
+                if (currentLen >= maxLineLen) {
+                    throw new MessageConstraintException("Maximum line length limit exceeded");
+                }
+            }
+
+            if (pos != -1) {
+                // end of line found.
+                if (this.linebuffer.isEmpty()) {
+                    // the entire line is preset in the read buffer
+                    return lineFromReadBuffer(charbuffer, pos);
+                }
+                retry = false;
+                addTransferDecodedBytesToLinebuffer(pos);
+            } else {
+                // end of line not found
+                if (hasBufferedData()) {
+                    addTransferDecodedBytesToLinebuffer(pos);
+                }
+                noRead = fillBuffer();
+                if (noRead == -1) {
+                    retry = false;
+                }
+            }
+        }
+        if (noRead == -1 && this.linebuffer.isEmpty()) {
+            // indicate the end of stream
+            return -1;
+        }
+        return lineFromLineBuffer(charbuffer);
+    }
+    
+    @Override
+    public String readLine() throws IOException {
+        final CharArrayBuffer charbuffer = new CharArrayBuffer(64);
+        final int l = readLine(charbuffer);
+        if (l != -1) {
+            return charbuffer.toString();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean isDataAvailable(int timeout) throws IOException {
+        return hasBufferedData();
+    }
+
+    @Override
+    public HttpTransportMetrics getMetrics() {
+        return this.metrics;
+    }
+
+    private int streamRead(final byte[] b, final int off, final int len) throws IOException {
+        Asserts.notNull(this.instream, "Input stream");
+        return this.instream.read(b, off, len);
+    }
+
+    private int lineFromLineBuffer(final CharArrayBuffer charbuffer) throws IOException {
+        // discard LF if found
+        int len = this.linebuffer.length();
+        if (len > 0) {
+            if (this.linebuffer.byteAt(len - 1) == HTTP.LF) {
+                len--;
+            }
+            // discard CR if found
+            if (len > 0) {
+                if (this.linebuffer.byteAt(len - 1) == HTTP.CR) {
+                    len--;
+                }
+            }
+        }
+        if (this.decoder == null) {
+            charbuffer.append(this.linebuffer, 0, len);
+        } else {
+            final ByteBuffer bbuf = ByteBuffer.wrap(this.linebuffer.buffer(), 0, len);
+            len = appendDecoded(charbuffer, bbuf);
+        }
+        this.linebuffer.clear();
+        return len;
+    }
+
+    private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position)
+            throws IOException {
+        int pos = position;
+        final int off = this.bufferpos;
+        int len;
+        this.bufferpos = pos + 1;
+        if (pos > off && this.buffer[pos - 1] == HTTP.CR) {
+            // skip CR if found
+            pos--;
+        }
+        len = pos - off;
+        if (this.decoder == null) {
+            charbuffer.append(this.buffer, off, len);
+        } else {
+            final ByteBuffer bbuf =  ByteBuffer.wrap(this.buffer, off, len);
+            len = appendDecoded(charbuffer, bbuf);
+        }
+        return len;
+    }
+
+    private int appendDecoded(final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException {
+        if (!bbuf.hasRemaining()) {
+            return 0;
+        }
+        if (this.cbuf == null) {
+            this.cbuf = CharBuffer.allocate(1024);
+        }
+        this.decoder.reset();
+        int len = 0;
+        while (bbuf.hasRemaining()) {
+            final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true);
+            len += handleDecodingResult(result, charbuffer, bbuf);
+        }
+        final CoderResult result = this.decoder.flush(this.cbuf);
+        len += handleDecodingResult(result, charbuffer, bbuf);
+        this.cbuf.clear();
+        return len;
+    }
+
+    private int handleDecodingResult(final CoderResult result, final CharArrayBuffer charbuffer, final ByteBuffer bbuf)
+            throws IOException {
+        if (result.isError()) {
+            result.throwException();
+        }
+        this.cbuf.flip();
+        final int len = this.cbuf.remaining();
+        while (this.cbuf.hasRemaining()) {
+            charbuffer.append(this.cbuf.get());
+        }
+        this.cbuf.compact();
+        return len;
+    }
+
+    private void addTransferDecodedBytesToLinebuffer(int pos) throws IOException {
+        try {
+            int len;
+            if (pos != -1) {
+                len = pos + 1 - this.bufferpos;
+            } else {
+                len = this.bufferlen - this.bufferpos;
+            }
+            byte[] data = new byte[len];
+            System.arraycopy(this.buffer, this.bufferpos, data, 0, data.length);
+            data = EntityUtils.decode(data, transferEncoding); //
+            this.linebuffer.append(data, 0, data.length);
+            this.bufferpos = pos + 1;
+        } catch (Exception e) {
+            throw new IOException("failed to decode transfer encoding", e);
+        }
+       
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/RequestAS2.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/RequestAS2.java
new file mode 100644
index 0000000..1cef65f
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/RequestAS2.java
@@ -0,0 +1,84 @@
+/**
+ * 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.IOException;
+
+import org.apache.camel.component.as2.api.AS2ClientManager;
+import org.apache.camel.component.as2.api.AS2Constants;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.InvalidAS2NameException;
+import org.apache.camel.component.as2.api.Util;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+
+public class RequestAS2 implements HttpRequestInterceptor {
+    
+    private final String as2Version;
+    private final String clientFQDN;
+    
+    public RequestAS2(String as2Version, String clientFQDN) {
+        this.as2Version = as2Version;
+        this.clientFQDN = clientFQDN;
+    }
+
+    @Override
+    public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
+        
+        HttpCoreContext coreContext = HttpCoreContext.adapt(context);
+        
+        /* MIME header */
+        request.addHeader(AS2Header.MIME_VERSION, AS2Constants.MIME_VERSION);
+        
+        /* Subject header */
+        String subject = coreContext.getAttribute(AS2ClientManager.SUBJECT, String.class);
+        request.addHeader(AS2Header.SUBJECT, subject);
+        
+        /* From header */
+        String from = coreContext.getAttribute(AS2ClientManager.FROM, String.class);
+        request.addHeader(AS2Header.FROM, from);
+
+        /* AS2-Version header */
+        request.addHeader(AS2Header.AS2_VERSION, as2Version);
+
+        /* AS2-From header */
+        String as2From = coreContext.getAttribute(AS2ClientManager.AS2_FROM, String.class);
+        try {
+            Util.validateAS2Name(as2From);
+        } catch (InvalidAS2NameException e) {
+            throw new HttpException("Invalid AS-From name", e);
+        }
+        request.addHeader(AS2Header.AS2_FROM, as2From);
+
+        /* AS2-To header */
+        String as2To = coreContext.getAttribute(AS2ClientManager.AS2_TO, String.class);
+        try {
+            Util.validateAS2Name(as2To);
+        } catch (InvalidAS2NameException e) {
+            throw new HttpException("Invalid AS-To name", e);
+        }
+        request.addHeader(AS2Header.AS2_TO, as2To);
+        
+        /* Message-Id header*/
+        // SHOULD be set to aid in message reconciliation
+        request.addHeader(AS2Header.MESSAGE_ID, Util.createMessageId(clientFQDN));
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/RequestMDN.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/RequestMDN.java
new file mode 100644
index 0000000..618c401
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/RequestMDN.java
@@ -0,0 +1,62 @@
+/**
+ * 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.IOException;
+
+import org.apache.camel.component.as2.api.AS2ClientManager;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.util.CharArrayBuffer;
+
+public class RequestMDN implements HttpRequestInterceptor {
+    
+    private static final String SIGNED_RECEIPT_PREFIX = "signed-receipt-protocol=optional, pkcs7-signature; signed-receipt-micalg=optional";
+
+    @Override
+    public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
+        
+        HttpCoreContext coreContext = HttpCoreContext.adapt(context);
+        
+        /* Disposition-Notification-To */
+        String dispositionNotificationTo = coreContext.getAttribute(AS2ClientManager.DISPOSITION_NOTIFICATION_TO, String.class);
+        if (dispositionNotificationTo != null) {
+            request.addHeader(AS2Header.DISPOSITION_NOTIFICATION_TO, dispositionNotificationTo);
+            
+            String[] micAlgorithms = coreContext.getAttribute(AS2ClientManager.SIGNED_RECEIPT_MIC_ALGORITHMS, String[].class);
+            if (micAlgorithms == null) {
+                // requesting unsigned receipt: indicate by not setting Disposition-Notification-Options header
+            } else {
+
+                CharArrayBuffer options = new CharArrayBuffer(
+                        SIGNED_RECEIPT_PREFIX.length() + 5 * micAlgorithms.length);
+                options.append(SIGNED_RECEIPT_PREFIX);
+                for (String micAlgorithm : micAlgorithms) {
+                    options.append("," + micAlgorithm);
+                }
+
+                request.addHeader(AS2Header.DISPOSITION_NOTIFICATION_OPTIONS, options.toString());
+            }
+        }
+
+    }
+
+}
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
new file mode 100644
index 0000000..10b6056
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
@@ -0,0 +1,181 @@
+/**
+ * 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.IOException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Constants;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.AS2ReportType;
+import org.apache.camel.component.as2.api.AS2ServerManager;
+import org.apache.camel.component.as2.api.AS2SignedDataGenerator;
+import org.apache.camel.component.as2.api.AS2TransferEncoding;
+import org.apache.camel.component.as2.api.InvalidAS2NameException;
+import org.apache.camel.component.as2.api.Util;
+import org.apache.camel.component.as2.api.entity.AS2DispositionType;
+import org.apache.camel.component.as2.api.entity.DispositionMode;
+import org.apache.camel.component.as2.api.entity.DispositionNotificationMultipartReportEntity;
+import org.apache.camel.component.as2.api.entity.DispositionNotificationOptions;
+import org.apache.camel.component.as2.api.entity.DispositionNotificationOptionsParser;
+import org.apache.camel.component.as2.api.entity.MultipartSignedEntity;
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.camel.component.as2.api.util.SigningUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ResponseMDN implements HttpResponseInterceptor {
+    
+    public static final String BOUNDARY_PARAM_NAME = "boundary";
+    
+    private static final Logger LOG = LoggerFactory.getLogger(ResponseMDN.class);
+    
+    private final String as2Version;
+    private final String serverFQDN;
+    private Certificate[] signingCertificateChain;
+    private PrivateKey signingPrivateKey;
+
+    public ResponseMDN(String as2Version, String serverFQDN, Certificate[] signingCertificateChain, PrivateKey signingPrivateKey) {
+        this.as2Version = as2Version;
+        this.serverFQDN = serverFQDN;
+        this.signingCertificateChain = signingCertificateChain;
+        this.signingPrivateKey = signingPrivateKey;
+    }
+
+    @Override
+    public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
+        
+        int statusCode = response.getStatusLine().getStatusCode();
+        if (statusCode < 200 || statusCode >= 300) {
+            LOG.debug("MDN not added due to response status code: " + statusCode);
+            return;
+        }
+        LOG.debug("Adding MDN to response: " + response);
+
+        HttpCoreContext coreContext = HttpCoreContext.adapt(context);
+        
+        HttpEntityEnclosingRequest request = coreContext.getAttribute(HttpCoreContext.HTTP_REQUEST, HttpEntityEnclosingRequest.class);
+        if (request == null) {
+            LOG.debug("MDN not added due to null request");
+            return;
+        }
+        LOG.debug("Processing MDN for request: " + request);
+        
+        /* MIME header */
+        response.addHeader(AS2Header.MIME_VERSION, AS2Constants.MIME_VERSION);
+
+        /* AS2-Version header */
+        response.addHeader(AS2Header.AS2_VERSION, as2Version);
+
+        /* Subject header */
+        String subjectPrefix = coreContext.getAttribute(AS2ServerManager.SUBJECT, String.class);
+        String subject = HttpMessageUtils.getHeaderValue(request, AS2Header.SUBJECT);
+        if (subjectPrefix != null && subject != null) {
+            subject = subjectPrefix + subject;            
+        } else if (subject != null) {
+            subject = "MDN Response To:" + subject;
+        } else {
+            subject = "Your Requested MDN Response";
+        }
+        response.addHeader(AS2Header.SUBJECT, subject);
+        
+        /* From header */
+        String from = coreContext.getAttribute(AS2ServerManager.FROM, String.class);
+        response.addHeader(AS2Header.FROM, from);
+
+        /* AS2-From header */
+        String as2From = HttpMessageUtils.getHeaderValue(request, AS2Header.AS2_TO);
+        try {
+            Util.validateAS2Name(as2From);
+        } catch (InvalidAS2NameException e) {
+            throw new HttpException("Invalid AS-From name", e);
+        }
+        response.addHeader(AS2Header.AS2_FROM, as2From);
+
+        /* AS2-To header */
+        String as2To = HttpMessageUtils.getHeaderValue(request, AS2Header.AS2_FROM);
+        try {
+            Util.validateAS2Name(as2To);
+        } catch (InvalidAS2NameException e) {
+            throw new HttpException("Invalid AS-To name", e);
+        }
+        response.addHeader(AS2Header.AS2_TO, as2To);
+
+        /* Message-Id header*/
+        // SHOULD be set to aid in message reconciliation
+        response.addHeader(AS2Header.MESSAGE_ID, Util.createMessageId(serverFQDN));
+        
+        if (HttpMessageUtils.getHeaderValue(request, AS2Header.DISPOSITION_NOTIFICATION_TO) != null) {
+            // Return a Message Disposition Notification Receipt in response body 
+            String boundary = EntityUtils.createBoundaryValue();
+            DispositionNotificationMultipartReportEntity multipartReportEntity = new DispositionNotificationMultipartReportEntity(
+                    request, response, DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY,
+                    AS2DispositionType.PROCESSED, null, null, null, null, null, AS2Charset.US_ASCII, boundary, true);
+
+            DispositionNotificationOptions dispositionNotificationOptions = DispositionNotificationOptionsParser
+                    .parseDispositionNotificationOptions(
+                            HttpMessageUtils.getHeaderValue(request, AS2Header.DISPOSITION_NOTIFICATION_OPTIONS), null);
+            
+            String receiptAddress = HttpMessageUtils.getHeaderValue(request, AS2Header.RECEIPT_DELIVERY_OPTION);
+            if (receiptAddress != null) { 
+                // Asynchronous Delivery
+                // TODO Implement
+            } else { 
+                // Synchronous Delivery
+                
+                AS2SignedDataGenerator gen = null;
+                if (dispositionNotificationOptions.getSignedReceiptProtocol() != null && signingCertificateChain != null && signingPrivateKey != null) {
+                    gen = SigningUtils.createSigningGenerator(signingCertificateChain, signingPrivateKey);
+                }
+                
+                if (gen != null) {
+                    // Create signed receipt
+                    try {
+                        multipartReportEntity.setMainBody(false);
+                        MultipartSignedEntity multipartSignedEntity = new MultipartSignedEntity(multipartReportEntity, gen,
+                                AS2Charset.US_ASCII, AS2TransferEncoding.BASE64, false, null);
+                        response.setHeader(multipartSignedEntity.getContentType());
+                        EntityUtils.setMessageEntity(response, multipartSignedEntity);
+                    } catch (Exception e) {
+                        LOG.warn("failed to sign receipt");
+                    }
+                } else {
+                    // Create unsigned receipt
+                    Header reportTypeHeader = AS2HeaderUtils.createHeader(AS2Header.REPORT_TYPE, new String[][] {{AS2ReportType.DISPOSITION_NOTIFICATION}, {BOUNDARY_PARAM_NAME, boundary}});
+                    response.addHeader(reportTypeHeader);
+                    response.setHeader(AS2Header.CONTENT_TYPE, AS2MimeType.MULTIPART_REPORT);
+                    EntityUtils.setMessageEntity(response, multipartReportEntity);
+                }
+            }
+            
+        }
+        LOG.debug(Util.printMessage(response));
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java
new file mode 100644
index 0000000..0d6eebd
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2HeaderUtils.java
@@ -0,0 +1,165 @@
+/**
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+import org.apache.camel.component.as2.api.entity.Importance;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.message.TokenParser;
+import org.apache.http.util.Args;
+import org.apache.http.util.CharArrayBuffer;
+
+public final class AS2HeaderUtils {
+
+    public static class Parameter {
+        private final String attribute;
+        private final Importance importance;
+        private final String[] values;
+
+        public Parameter(String attribute, String importance, String[] values) {
+            this.attribute = Args.notNull(attribute, "attribute");
+            this.importance = Importance.get(importance);
+            this.values = values;
+        }
+
+        public String getAttribute() {
+            return attribute;
+        }
+
+        public Importance getImportance() {
+            return importance;
+        }
+
+        public String[] getValues() {
+            return values;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(attribute);
+            if (importance != null) {
+                sb.append("=" + importance.toString());
+            }
+            if (values != null) {
+                for (String value : values) {
+                    sb.append("," + value);
+                }
+            }
+            return sb.toString();
+        }
+    }
+
+    private static final char PARAM_DELIMITER = ',';
+    private static final char ELEM_DELIMITER = ';';
+    private static final char NAME_VALUE_DELIMITER = '=';
+
+    private static final TokenParser TOKEN_PARSER = TokenParser.INSTANCE;
+
+    private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET(NAME_VALUE_DELIMITER, PARAM_DELIMITER,
+            ELEM_DELIMITER);
+    private static final BitSet VALUE_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER);
+
+    private AS2HeaderUtils() {
+    }
+    
+    public static Header createHeader(String headerName, String[]... elements) {
+        StringBuilder sb = new StringBuilder();
+        
+        boolean firstElement = true;
+        for (String[] element: elements) {
+            if (element.length == 0) {
+                continue;
+            }
+            if (firstElement) {
+                firstElement = false;
+            } else {
+                sb.append(ELEM_DELIMITER);
+            }
+            sb.append(element[0]);
+            if (element.length > 1) {
+                sb.append(NAME_VALUE_DELIMITER + element[1]);
+            }
+        }
+        BasicHeader header = new BasicHeader(headerName, sb.toString());
+        return header;
+    }
+    
+    public static Parameter parseParameter(final CharArrayBuffer buffer, final ParserCursor cursor) {
+        Args.notNull(buffer, "Char array buffer");
+        Args.notNull(cursor, "Parser cursor");
+
+        final String name = TOKEN_PARSER.parseToken(buffer, cursor, TOKEN_DELIMS);
+        if (cursor.atEnd()) {
+            return new Parameter(name, null, null);
+        }
+
+        final int delim = buffer.charAt(cursor.getPos());
+        cursor.updatePos(cursor.getPos() + 1);
+        if (delim != NAME_VALUE_DELIMITER) {
+            return new Parameter(name, null, null);
+        }
+
+        final String importance = TOKEN_PARSER.parseValue(buffer, cursor, VALUE_DELIMS);
+        if (!cursor.atEnd()) {
+            cursor.updatePos(cursor.getPos() + 1);
+        }
+
+        List<String> values = new ArrayList<String>();
+        while (!cursor.atEnd()) {
+            String value = TOKEN_PARSER.parseValue(buffer, cursor, VALUE_DELIMS);
+            values.add(value);
+            if (cursor.atEnd()) {
+                break;
+            }
+            final int delimiter = buffer.charAt(cursor.getPos());
+            if (!cursor.atEnd()) {
+                cursor.updatePos(cursor.getPos() + 1);
+            }
+            if (delimiter == ELEM_DELIMITER) {
+                break;
+            }
+        }
+
+        return new Parameter(name, importance, values.toArray(new String[values.size()]));
+    }
+
+    public static String getBoundaryParameterValue(Header[] headers, String headerName) {
+        Args.notNull(headers, "headers");
+        Args.notNull(headerName, "headerName");
+        for (Header header : headers) {
+            if (header.getName().equalsIgnoreCase(headerName)) {
+                for (HeaderElement headerElement : header.getElements()) {
+                    for (NameValuePair nameValuePair : headerElement.getParameters()) {
+                        if (nameValuePair.getName().equalsIgnoreCase("boundary")) {
+                            return nameValuePair.getValue();
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/ContentTypeUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/ContentTypeUtils.java
new file mode 100644
index 0000000..2a344db
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/ContentTypeUtils.java
@@ -0,0 +1,50 @@
+/**
+ * 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.util;
+
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.http.entity.ContentType;
+
+public final class ContentTypeUtils {
+
+    private ContentTypeUtils() {
+    }
+
+    public static boolean isEDIMessageContentType(ContentType ediMessageContentType) {
+        switch (ediMessageContentType.getMimeType().toLowerCase()) {
+        case AS2MediaType.APPLICATION_EDIFACT:
+            return true;
+        case AS2MediaType.APPLICATION_EDI_X12:
+            return true;
+        case AS2MediaType.APPLICATION_EDI_CONSENT:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    public static boolean isPkcs7SignatureType(ContentType pcks7SignatureType) {
+        switch (pcks7SignatureType.getMimeType().toLowerCase()) {
+        case AS2MimeType.APPLICATION_PKCS7_SIGNATURE:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java
new file mode 100644
index 0000000..3fcbace
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtils.java
@@ -0,0 +1,300 @@
+/**
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.component.as2.api.MDNField;
+import org.apache.camel.component.as2.api.entity.AS2DispositionModifier;
+import org.apache.camel.component.as2.api.entity.AS2DispositionType;
+import org.apache.camel.component.as2.api.entity.AS2MessageDispositionNotificationEntity;
+import org.apache.camel.component.as2.api.entity.DispositionMode;
+import org.apache.camel.component.as2.api.util.DispositionNotificationContentUtils.Field.Element;
+import org.apache.camel.component.as2.api.util.MicUtils.ReceivedContentMic;
+import org.apache.http.ParseException;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.message.TokenParser;
+import org.apache.http.util.Args;
+import org.apache.http.util.CharArrayBuffer;
+
+public final class DispositionNotificationContentUtils {
+    
+    private static final String REPORTING_UA = "reporting-ua";
+    private static final String MDN_GATEWAY = "mdn-gateway";
+    private static final String FINAL_RECIPIENT = "final-recipient";
+    private static final String ORIGINAL_MESSAGE_ID = "original-message-id";
+    private static final String DISPOSITION = "disposition";
+    private static final String FAILURE = "failure";
+    private static final String ERROR = "error";
+    private static final String WARNING = "warning";
+    private static final String RECEIVED_CONTENT_MIC = "received-content-mic";
+
+    public static class Field {
+
+        public static class Element {
+
+            private final String value;
+            private final String[] parameters;
+
+            public Element(String value, String[] parameters) {
+                this.value = value;
+                this.parameters = (parameters == null) ? new String[] {} : parameters;
+            }
+
+            public String getValue() {
+                return value;
+            }
+
+            public String[] getParameters() {
+                return parameters;
+            }
+
+            @Override
+            public String toString() {
+                return value + ((parameters.length > 0) ? ", " + String.join(",", parameters) : "");
+            }
+
+        }
+
+        private String name;
+        private Element[] elements;
+
+        public Field(String name, Element[] elements) {
+            this.name = Args.notNull(name, "name");
+            this.elements = (elements == null) ? new Element[] {} : elements;
+        }
+
+        public Field(String name, String value) {
+            this.name = Args.notNull(name, "name");
+            this.elements = new Element[] {new Element(value, null)};
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Element[] getElements() {
+            return elements;
+        }
+
+        public String getValue() {
+
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < elements.length; i++) {
+                Element element = elements[i];
+                if (i > 0) {
+                    builder.append("; " + element);
+                } else {
+                    builder.append(element);
+                }
+            }
+
+            return builder.toString();
+        }
+        
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(name + ": ");
+            for (int i = 0; i < elements.length; i++) {
+                Element element = elements[i];
+                if (i > 0) {
+                    sb.append("; " + element);
+                } else {
+                    sb.append(element);
+                }
+            }
+            return sb.toString();
+        }
+
+    }
+
+    private static final TokenParser TOKEN_PARSER = TokenParser.INSTANCE;
+
+    private static final char PARAM_DELIMITER = ',';
+    private static final char ELEM_DELIMITER = ';';
+
+    private static final BitSet TOKEN_DELIMS = TokenParser.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER);
+
+    private DispositionNotificationContentUtils() {
+    }
+
+    public static AS2MessageDispositionNotificationEntity parseDispositionNotification(List<CharArrayBuffer> dispositionNotificationFields)
+            throws ParseException {
+        String reportingUA = null;
+        String mtaName = null;
+        String finalRecipient = null;
+        String originalMessageId = null;
+        DispositionMode dispositionMode = null;
+        AS2DispositionType dispositionType = null;
+        AS2DispositionModifier dispositionModifier = null;
+        List<String> failures = new ArrayList<String>();
+        List<String> errors = new ArrayList<String>();
+        List<String> warnings = new ArrayList<String>();
+        Map<String, String> extensionFields = new HashMap<String, String>();
+        ReceivedContentMic receivedContentMic = null;
+
+        for (int i = 0; i < dispositionNotificationFields.size(); i++) {
+            final CharArrayBuffer fieldLine = dispositionNotificationFields.get(i);
+            final Field field = parseDispositionField(fieldLine);
+            switch(field.getName().toLowerCase()) {
+            case REPORTING_UA: {
+                if (field.getElements().length < 1) {
+                    throw new ParseException("Invalid '" + MDNField.REPORTING_UA + "' field: UA name is missing");
+                }
+                reportingUA = field.getValue();
+                break;
+            }
+            case MDN_GATEWAY: {
+                Element[] elements = field.getElements();
+                if (elements.length < 2) {
+                    throw new ParseException("Invalid '" + MDNField.MDN_GATEWAY + "' field: MTA name is missing");
+                }
+                mtaName = elements[1].getValue();
+                break;
+            }
+            case FINAL_RECIPIENT: {
+                Element[] elements = field.getElements();
+                if (elements.length < 2) {
+                    throw new ParseException("Invalid '" + MDNField.FINAL_RECIPIENT + "' field: recipient address is missing");
+                }
+                finalRecipient = elements[1].getValue();
+                break;
+            }
+            case ORIGINAL_MESSAGE_ID: {
+                originalMessageId = field.getValue();
+                break;
+            }
+            case DISPOSITION: {
+                Element[] elements = field.getElements();
+                if (elements.length < 2) {
+                    throw new ParseException("Invalid '" + MDNField.DISPOSITION + "' field: " + field.getValue());
+                }
+                dispositionMode = DispositionMode.parseDispositionMode(elements[0].getValue());
+                if (dispositionMode == null) {
+                    throw new ParseException("Invalid '" + MDNField.DISPOSITION + "' field: invalid disposition mode '" + elements[0].getValue() + "'");
+                }
+
+                String dispositionTypeString = elements[1].getValue();
+                int slash = dispositionTypeString.indexOf('/');
+                if (slash == -1) {
+                    dispositionType = AS2DispositionType.parseDispositionType(dispositionTypeString);
+                } else {
+                    dispositionType = AS2DispositionType.parseDispositionType(dispositionTypeString.substring(0, slash));
+                    dispositionModifier = AS2DispositionModifier.parseDispositionType(dispositionTypeString.substring(slash + 1));
+                }
+                break;
+            }
+            case FAILURE:
+                failures.add(field.getValue());
+                break;
+            case ERROR:
+                errors.add(field.getValue());
+                break;
+            case WARNING:
+                warnings.add(field.getValue());
+                break;
+            case RECEIVED_CONTENT_MIC: {
+                Element[] elements = field.getElements();
+                if (elements.length < 1) {
+                    throw new ParseException("Invalid '" + MDNField.RECEIVED_CONTENT_MIC + "' field: MIC is missing");
+                }
+                Element element = elements[0];
+                String[] parameters = element.getParameters();
+                if (parameters.length < 1) {
+                    throw new ParseException("Invalid '" + MDNField.RECEIVED_CONTENT_MIC + "' field: digest algorithm ID is missing");
+                }
+                String digestAlgorithmId = parameters[0];
+                String encodedMessageDigest = element.getValue();
+                receivedContentMic = new ReceivedContentMic(digestAlgorithmId, encodedMessageDigest);
+                break;
+            }
+            default: // Extension Field
+                extensionFields.put(field.getName(), field.getValue());
+            }
+        }
+        
+        return new AS2MessageDispositionNotificationEntity(reportingUA,
+                                                            mtaName,
+                                                            finalRecipient,
+                                                            originalMessageId,
+                                                            dispositionMode,
+                                                            dispositionType,
+                                                            dispositionModifier,
+                                                            failures.toArray(new String[failures.size()]),
+                                                            errors.toArray(new String[errors.size()]),
+                                                            warnings.toArray(new String[warnings.size()]),
+                                                            extensionFields,
+                                                            receivedContentMic);
+    }
+
+    public static Field parseDispositionField(CharArrayBuffer fieldLine) {
+        final int colon = fieldLine.indexOf(':');
+        if (colon == -1) {
+            throw new ParseException("Invalid field: " + fieldLine.toString());
+        }
+        final String fieldName = fieldLine.substringTrimmed(0, colon);
+
+        ParserCursor cursor = new ParserCursor(colon + 1, fieldLine.length());
+        
+        final List<Element> elements = new ArrayList<Element>();
+        while (!cursor.atEnd()) {
+            final Element element = parseDispositionFieldElement(fieldLine, cursor);
+            if (element.getValue() != null) {
+                elements.add(element);
+            }
+        }
+        
+        return new Field(fieldName, elements.toArray(new Element[elements.size()]));
+    }
+
+    public static Element parseDispositionFieldElement(CharArrayBuffer fieldLine, ParserCursor cursor) {
+        
+        final String value = TOKEN_PARSER.parseToken(fieldLine, cursor, TOKEN_DELIMS);
+        if (cursor.atEnd()) {
+            return new Element(value, null);
+        }
+        
+        final char delim = fieldLine.charAt(cursor.getPos());
+        cursor.updatePos(cursor.getPos() + 1);
+        if (delim == ELEM_DELIMITER) {
+            return new Element(value, null);
+        }
+        
+        final List<String> parameters = new ArrayList<String>();
+        while (!cursor.atEnd()) {
+            final String parameter = TOKEN_PARSER.parseToken(fieldLine, cursor, TOKEN_DELIMS);
+            parameters.add(parameter);
+            if (cursor.atEnd()) {
+                break;
+            }
+            final char ch = fieldLine.charAt(cursor.getPos());
+            if (!cursor.atEnd()) {
+                cursor.updatePos(cursor.getPos() + 1);
+            }
+            if (ch == ELEM_DELIMITER) {
+                break;
+            }
+        }
+        
+        return new Element(value, parameters.toArray(new String[parameters.size()]));
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java
new file mode 100644
index 0000000..ab8c108
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java
@@ -0,0 +1,247 @@
+/**
+ * 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.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIConsentEntity;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIEntity;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIFACTEntity;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIX12Entity;
+import org.apache.commons.codec.binary.Base64InputStream;
+import org.apache.commons.codec.binary.Base64OutputStream;
+import org.apache.commons.codec.net.QuotedPrintableCodec;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.Args;
+import org.bouncycastle.util.encoders.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class EntityUtils {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(EntityUtils.class);
+
+    private static AtomicLong partNumber = new AtomicLong();
+    
+    private EntityUtils() {
+    }
+
+    /**
+     * Generated a unique value for a Multipart boundary string.
+     * <p>
+     * The boundary string is composed of the components:
+     * "----=_Part_&lt;global_part_number&gt;_&lt;newly_created_object's_hashcode&gt;.&lt;current_time&gt;"
+     * <p>
+     * The generated string contains only US-ASCII characters and hence is safe
+     * for use in RFC822 headers.
+     * 
+     * @return The generated boundary string.
+     */
+    public static String createBoundaryValue() {
+        // TODO: ensure boundary string is limited to 70 characters or less.
+        StringBuffer s = new StringBuffer();
+        s.append("----=_Part_").append(partNumber.incrementAndGet()).append("_").append(s.hashCode()).append(".")
+                .append(System.currentTimeMillis());
+        return s.toString();
+    }
+
+    public static boolean validateBoundaryValue(String boundaryValue) {
+        return true; // TODO: add validation logic.
+    }
+
+    public static String appendParameter(String headerString, String parameterName, String parameterValue) {
+        return headerString + "; " + parameterName + "=" + parameterValue;
+    }
+    
+    public static byte[] encode(byte[] data, String encoding) throws Exception {
+        Args.notNull(data, "Data");
+        
+        if (encoding == null) {
+            // Identity encoding
+            return data;
+        }
+        
+        switch(encoding.toLowerCase()) {
+        case "base64":
+            return Base64.encode(data);
+        case "quoted-printable":
+            // TODO: implement QuotedPrintableOutputStream
+            return QuotedPrintableCodec.encodeQuotedPrintable(null, data);
+        case "binary":
+        case "7bit":
+        case "8bit":
+            // Identity encoding
+            return data;
+        default:
+            throw new Exception("Unknown encoding: " + encoding);
+        }
+    }
+    
+    public static OutputStream encode(OutputStream os, String encoding) throws Exception {
+        Args.notNull(os, "Output Stream");
+        
+        if (encoding == null) {
+            // Identity encoding
+            return os;
+        }
+        switch (encoding.toLowerCase()) {
+        case "base64":
+            return new Base64OutputStream(os, true);
+        case "quoted-printable":
+            // TODO: implement QuotedPrintableOutputStream
+            return new Base64OutputStream(os, true);
+        case "binary":
+        case "7bit":
+        case "8bit":
+            // Identity encoding
+            return os;
+        default:
+            throw new Exception("Unknown encoding: " + encoding);
+        }
+    }
+    
+    public static byte[] decode(byte[] data, String encoding) throws Exception {
+        Args.notNull(data, "Input Stream");
+        
+        if (encoding == null) {
+            // Identity encoding
+            return data;
+        }
+        switch (encoding.toLowerCase()) {
+        case "base64":
+            return Base64.decode(data);
+        case "quoted-printable":
+            return QuotedPrintableCodec.decodeQuotedPrintable(data);
+        case "binary":
+        case "7bit":
+        case "8bit":
+            // Identity encoding
+            return data;
+        default:
+            throw new Exception("Unknown encoding: " + encoding);
+        }
+    }
+    
+    public static InputStream decode(InputStream is, String encoding) throws Exception {
+        Args.notNull(is, "Input Stream");
+        
+        if (encoding == null) {
+            // Identity encoding
+            return is;
+        }
+        switch (encoding.toLowerCase()) {
+        case "base64":
+            return new Base64InputStream(is, false);
+        case "quoted-printable":
+            // TODO: implement QuotedPrintableInputStream
+            return new Base64InputStream(is, false);
+        case "binary":
+        case "7bit":
+        case "8bit":
+            // Identity encoding
+            return is;
+        default:
+            throw new Exception("Unknown encoding: " + encoding);
+        }
+    }
+    
+    public static ApplicationEDIEntity createEDIEntity(String ediMessage, ContentType ediMessageContentType, String contentTransferEncoding, boolean isMainBody) throws Exception {
+        Args.notNull(ediMessage, "EDI Message");
+        Args.notNull(ediMessageContentType, "EDI Message Content Type");
+        String charset = ediMessageContentType.getCharset() == null ? AS2Charset.US_ASCII : ediMessageContentType.getCharset().toString();
+        switch(ediMessageContentType.getMimeType().toLowerCase()) {
+        case AS2MediaType.APPLICATION_EDIFACT:
+            return new ApplicationEDIFACTEntity(ediMessage, charset, contentTransferEncoding, isMainBody);            
+        case AS2MediaType.APPLICATION_EDI_X12:
+            return new ApplicationEDIX12Entity(ediMessage, charset, contentTransferEncoding, isMainBody);            
+        case AS2MediaType.APPLICATION_EDI_CONSENT:
+            return new ApplicationEDIConsentEntity(ediMessage, charset, contentTransferEncoding, isMainBody);            
+        default:
+            throw new Exception("Invalid EDI entity mime type: " + ediMessageContentType.getMimeType());
+        }
+        
+    }
+    
+    public static byte[] getContent(HttpEntity entity) {
+        try {
+            final ByteArrayOutputStream outstream = new ByteArrayOutputStream();
+            entity.writeTo(outstream);
+            outstream.flush();
+            return outstream.toByteArray();
+        } catch (Exception e) {
+            LOG.debug("failed to get content", e);
+            return null;
+        }
+    }
+
+    public static boolean hasEntity(HttpMessage message) {
+        boolean hasEntity = false;
+        if (message instanceof HttpEntityEnclosingRequest) {
+            hasEntity = ((HttpEntityEnclosingRequest) message).getEntity() != null;
+        } else if (message instanceof HttpResponse) {
+            hasEntity = ((HttpResponse) message).getEntity() != null;
+        }
+        return hasEntity;
+    }
+
+    public static HttpEntity getMessageEntity(HttpMessage message) {
+        if (message instanceof HttpEntityEnclosingRequest) {
+            return ((HttpEntityEnclosingRequest) message).getEntity();
+        } else if (message instanceof HttpResponse) {
+            return ((HttpResponse) message).getEntity();
+        }
+        return null;
+    }
+
+    public static void setMessageEntity(HttpMessage message, HttpEntity entity) {
+        if (message instanceof HttpEntityEnclosingRequest) {
+            ((HttpEntityEnclosingRequest) message).setEntity(entity);
+        } else if (message instanceof HttpResponse) {
+            ((HttpResponse) message).setEntity(entity);
+        }
+        long contentLength = entity.getContentLength();
+        message.setHeader(AS2Header.CONTENT_LENGTH, Long.toString(contentLength));
+    }
+
+    public static byte[] decodeTransferEncodingOfBodyPartContent(String bodyPartContent,
+                                                                 ContentType contentType,
+                                                                 String bodyPartTransferEncoding)
+            throws Exception {
+        Args.notNull(bodyPartContent, "bodyPartContent");
+        Charset contentCharset = contentType.getCharset();
+        if (contentCharset == null) {
+            contentCharset = StandardCharsets.US_ASCII;
+        }
+        return decode(bodyPartContent.getBytes(contentCharset), bodyPartTransferEncoding);
+    
+    }
+
+
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java
new file mode 100644
index 0000000..0b757d7
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/HttpMessageUtils.java
@@ -0,0 +1,108 @@
+/**
+ * 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.util;
+
+import org.apache.camel.component.as2.api.entity.EntityParser;
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpMessage;
+import org.apache.http.NameValuePair;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.util.Args;
+import org.apache.http.util.CharArrayBuffer;
+
+public final class HttpMessageUtils {
+    
+    private HttpMessageUtils() {
+    }
+    
+    public static String getHeaderValue(HttpMessage message, String headerName) {
+        Header header = message.getFirstHeader(headerName);
+        return header == null ? null : header.getValue();
+    }
+    
+    public static void setHeaderValue(HttpMessage message, String headerName, String headerValue) {
+        Args.notNull(message, "message");
+        Args.notNull(headerName, "headerName");
+        if (headerValue == null) {
+            message.removeHeaders(headerName);
+        } else {
+            message.setHeader(headerName, headerValue);
+        }
+    }
+    
+    public static <T> T getEntity(HttpMessage request, Class<T> type) {
+        if (request instanceof HttpEntityEnclosingRequest) {
+            HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
+            if (entity != null && type.isInstance(entity)) {
+                return type.cast(entity);
+            }
+        }
+        return null;
+    }
+
+    public static String parseBodyPartContent(SessionInputBuffer inBuffer, String boundary) throws HttpException {
+        try {
+            CharArrayBuffer bodyPartContentBuffer = new CharArrayBuffer(1024);
+            CharArrayBuffer lineBuffer = new CharArrayBuffer(1024);
+            boolean foundMultipartEndBoundary = false;
+            while (inBuffer.readLine(lineBuffer) != -1) {
+                if (EntityParser.isBoundaryDelimiter(lineBuffer, null, boundary)) {
+                    foundMultipartEndBoundary = true;
+                    // Remove previous line ending: this is associated with
+                    // boundary
+                    bodyPartContentBuffer.setLength(bodyPartContentBuffer.length() - 2);
+                    lineBuffer.clear();
+                    break;
+                }
+                lineBuffer.append("\r\n"); // add line delimiter
+                bodyPartContentBuffer.append(lineBuffer);
+                lineBuffer.clear();
+            }
+            if (!foundMultipartEndBoundary) {
+                throw new HttpException("Failed to find end boundary delimiter for body part");
+            }
+    
+            return bodyPartContentBuffer.toString();
+        } catch (HttpException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new HttpException("Failed to parse body part content", e);
+        }
+    }
+
+    public static String getBoundaryParameterValue(HttpMessage message, String headerName) {
+        Args.notNull(message, "message");
+        Args.notNull(headerName, "headerName");
+        Header header = message.getFirstHeader(headerName);
+        if (header == null) {
+            return null;
+        }
+        for (HeaderElement headerElement : header.getElements()) {
+            for (NameValuePair nameValuePair : headerElement.getParameters()) {
+                if (nameValuePair.getName().equalsIgnoreCase("boundary")) {
+                    return nameValuePair.getValue();
+                }
+            }
+        }
+        return null;
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/MicUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/MicUtils.java
new file mode 100644
index 0000000..2189951
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/MicUtils.java
@@ -0,0 +1,151 @@
+/**
+ * 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.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MicAlgorithm;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIEntity;
+import org.apache.camel.component.as2.api.entity.DispositionNotificationOptions;
+import org.apache.camel.component.as2.api.entity.DispositionNotificationOptionsParser;
+import org.apache.camel.component.as2.api.entity.EntityParser;
+import org.apache.camel.component.as2.api.entity.MultipartSignedEntity;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.entity.ContentType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class MicUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(MicUtils.class);
+    
+    private MicUtils() {
+    }
+
+    public static class ReceivedContentMic {
+        private final String digestAlgorithmId;
+        private final String encodedMessageDigest;
+        
+        public ReceivedContentMic(String digestAlgorithmId, byte[] messageDigest) throws Exception {
+            this.digestAlgorithmId = digestAlgorithmId;
+            messageDigest = EntityUtils.encode(messageDigest, "base64");
+            this.encodedMessageDigest = new String(messageDigest, AS2Charset.US_ASCII);
+        }
+        
+        // Used when parsing received content MIC from received string
+        protected ReceivedContentMic(String digestAlgorithmId, String encodedMessageDigest) {
+            this.digestAlgorithmId = digestAlgorithmId;
+            this.encodedMessageDigest = encodedMessageDigest;
+        }
+
+        public String getDigestAlgorithmId() {
+            return digestAlgorithmId;
+        }
+
+        public String getEncodedMessageDigest() {
+            return encodedMessageDigest;
+        }
+        
+        @Override
+        public String toString() {
+            return encodedMessageDigest + "," + digestAlgorithmId;
+        }
+    }
+    
+    public static byte[] createMic(byte[] content, String algorithmId) {
+        try {
+            MessageDigest messageDigest = MessageDigest.getInstance(algorithmId, "BC");
+            return messageDigest.digest(content);
+        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+            LOG.debug("failed to get message digets '" + algorithmId + "'");
+            return null;
+        }
+    }
+    
+    public static ReceivedContentMic createReceivedContentMic(HttpEntityEnclosingRequest request) throws HttpException {
+        
+        String dispositionNotificationOptionsString =  HttpMessageUtils.getHeaderValue(request, AS2Header.DISPOSITION_NOTIFICATION_OPTIONS);
+        if (dispositionNotificationOptionsString == null) {
+            LOG.debug("do not create MIC: no disposition notification options in request");
+            return null;
+        }
+        DispositionNotificationOptions dispositionNotificationOptions = DispositionNotificationOptionsParser.parseDispositionNotificationOptions(dispositionNotificationOptionsString, null);
+        String micJdkAlgorithmName = getMicJdkAlgorithmName(dispositionNotificationOptions.getSignedReceiptMicalg().getValues());
+        if (micJdkAlgorithmName == null) {
+            LOG.debug("do not create MIC: no matching MIC algorithms found");
+            return null;
+        }
+
+        String contentTypeString = HttpMessageUtils.getHeaderValue(request, AS2Header.CONTENT_TYPE);
+        if (contentTypeString == null) {
+            LOG.debug("can not create MIC: content type missing from request");
+            return null;
+        }
+        ContentType contentType = ContentType.parse(contentTypeString);
+        
+        HttpEntity entity = null;
+        switch (contentType.getMimeType().toLowerCase()) {
+        case AS2MimeType.APPLICATION_EDIFACT:
+        case AS2MimeType.APPLICATION_EDI_X12:
+        case AS2MimeType.APPLICATION_EDI_CONSENT: {
+            EntityParser.parseAS2MessageEntity(request);
+            entity = HttpMessageUtils.getEntity(request, ApplicationEDIEntity.class);
+            break;
+        }
+        case AS2MimeType.MULTIPART_SIGNED: {
+            EntityParser.parseAS2MessageEntity(request);
+            MultipartSignedEntity multipartSignedEntity = HttpMessageUtils.getEntity(request,
+                    MultipartSignedEntity.class);
+            entity = multipartSignedEntity.getSignedDataEntity();
+            break;
+        }
+        default:
+            LOG.debug("can not create MIC: invalid content type '" + contentType.getMimeType()
+                    + "' for message integrity check");
+            return null;
+        }
+        
+        byte[] content = EntityUtils.getContent(entity);
+        
+        String micAS2AlgorithmName = AS2MicAlgorithm.getAS2AlgorithmName(micJdkAlgorithmName);
+        byte[] mic = createMic(content, micJdkAlgorithmName);
+        try {
+            return new ReceivedContentMic(micAS2AlgorithmName, mic);
+        } catch (Exception e) {
+            throw new HttpException("failed to encode MIC", e);
+        }
+    }
+    
+    public static String getMicJdkAlgorithmName(String[] micAs2AlgorithmNames) {
+        if (micAs2AlgorithmNames == null) {
+            return AS2MicAlgorithm.SHA_1.getJdkAlgorithmName();
+        }
+        for (String micAs2AlgorithmName : micAs2AlgorithmNames) {
+            String micJdkAlgorithmName = AS2MicAlgorithm.getJdkAlgorithmName(micAs2AlgorithmName);
+            if (micJdkAlgorithmName != null) {
+                return micJdkAlgorithmName;
+            }
+        }    
+        return AS2MicAlgorithm.SHA_1.getJdkAlgorithmName();
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/SigningUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/SigningUtils.java
new file mode 100644
index 0000000..7ee3bde
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/SigningUtils.java
@@ -0,0 +1,92 @@
+/**
+ * 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.util;
+
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import org.apache.camel.component.as2.api.AS2SignedDataGenerator;
+import org.apache.http.HttpException;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
+import org.bouncycastle.asn1.smime.SMIMECapability;
+import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
+import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+
+public final class SigningUtils {
+
+    private SigningUtils() {
+    }
+
+    public static AS2SignedDataGenerator createSigningGenerator(Certificate[] certificateChain, PrivateKey privateKey) throws HttpException {
+        
+        AS2SignedDataGenerator gen = new AS2SignedDataGenerator();
+
+        // Get first certificate in chain for signing
+        X509Certificate signingCert = (X509Certificate) certificateChain[0];
+
+        // Create capabilities vector
+        SMIMECapabilityVector capabilities = new SMIMECapabilityVector();
+        capabilities.addCapability(SMIMECapability.dES_EDE3_CBC);
+        capabilities.addCapability(SMIMECapability.rC2_CBC, 128);
+        capabilities.addCapability(SMIMECapability.dES_CBC);
+
+        // Create signing attributes
+        ASN1EncodableVector attributes = new ASN1EncodableVector();
+        attributes.add(new SMIMEEncryptionKeyPreferenceAttribute(new IssuerAndSerialNumber(
+                new X500Name(signingCert.getIssuerDN().getName()), signingCert.getSerialNumber())));
+        attributes.add(new SMIMECapabilitiesAttribute(capabilities));
+
+        SignerInfoGenerator signerInfoGenerator = null;
+        for (String signingAlgorithmName : AS2SignedDataGenerator.getSupportedSignatureAlgorithmNamesForKey(privateKey)) {
+            try {
+                signerInfoGenerator = new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC")
+                .setSignedAttributeGenerator(new AttributeTable(attributes))
+                .build(signingAlgorithmName, privateKey, signingCert);
+                break;
+            } catch (Exception e) {
+                signerInfoGenerator = null;
+                continue;
+            }
+        }
+        if (signerInfoGenerator == null) {
+            throw new HttpException("Failed to create signer info");
+        }
+        gen.addSignerInfoGenerator(signerInfoGenerator);
+        
+        // Create and populate certificate store.
+        try {
+            JcaCertStore certs = new JcaCertStore(Arrays.asList(certificateChain));
+            gen.addCertificates(certs);
+        } catch (CertificateEncodingException | CMSException e) {
+            throw new HttpException("Failed to add certificate chain to signature", e);
+        }
+
+        return gen;
+        
+    }
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2MessageTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2MessageTest.java
new file mode 100644
index 0000000..34307db
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2MessageTest.java
@@ -0,0 +1,367 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.component.as2.api.entity.ApplicationEDIEntity;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIFACTEntity;
+import org.apache.camel.component.as2.api.entity.ApplicationPkcs7SignatureEntity;
+import org.apache.camel.component.as2.api.entity.MultipartSignedEntity;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
+import org.bouncycastle.asn1.smime.SMIMECapability;
+import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
+import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class AS2MessageTest {
+    
+    public static final String EDI_MESSAGE = "UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'\n"
+            + "UNH+00000000000117+INVOIC:D:97B:UN'\n"
+            + "BGM+380+342459+9'\n"
+            + "DTM+3:20060515:102'\n"
+            + "RFF+ON:521052'\n"
+            + "NAD+BY+792820524::16++CUMMINS MID-RANGE ENGINE PLANT'\n"
+            + "NAD+SE+005435656::16++GENERAL WIDGET COMPANY'\n"
+            + "CUX+1:USD'\n"
+            + "LIN+1++157870:IN'\n"
+            + "IMD+F++:::WIDGET'\n"
+            + "QTY+47:1020:EA'\n"
+            + "ALI+US'\n"
+            + "MOA+203:1202.58'\n"
+            + "PRI+INV:1.179'\n"
+            + "LIN+2++157871:IN'\n"
+            + "IMD+F++:::DIFFERENT WIDGET'\n"
+            + "QTY+47:20:EA'\n"
+            + "ALI+JP'\n"
+            + "MOA+203:410'\n"
+            + "PRI+INV:20.5'\n"
+            + "UNS+S'\n"
+            + "MOA+39:2137.58'\n"
+            + "ALC+C+ABG'\n"
+            + "MOA+8:525'\n"
+            + "UNT+23+00000000000117'\n"
+            + "UNZ+1+00000000000778'";
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(AS2MessageTest.class);
+
+    private static final String METHOD = "POST";
+    private static final String TARGET_HOST = "localhost";
+    private static final int TARGET_PORT = 8080;
+    private static final String AS2_VERSION = "1.1";
+    private static final String USER_AGENT = "Camel AS2 Endpoint";
+    private static final String REQUEST_URI = "/";
+    private static final String AS2_NAME = "878051556";
+    private static final String SUBJECT = "Test Case";
+    private static final String FROM = "mrAS@example.org";
+    private static final String CLIENT_FQDN = "client.example.org";
+    private static final String SERVER_FQDN = "server.example.org";
+    private static final String DISPOSITION_NOTIFICATION_TO = "mrAS@example.org";
+    private static final String[] SIGNED_RECEIPT_MIC_ALGORITHMS = new String[] {"sha1", "md5"};
+    
+
+    private static AS2ServerConnection testServer;
+
+    private AS2SignedDataGenerator gen;
+    
+    private KeyPair issueKP;
+    private X509Certificate issueCert;
+
+    private KeyPair signingKP;
+    private X509Certificate signingCert;
+    private List<X509Certificate> certList;
+
+    private void setupKeysAndCertificates() throws Exception {
+        //
+        // set up our certificates
+        //
+        KeyPairGenerator    kpg  = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpg.initialize(1024, new SecureRandom());
+
+        String issueDN = "O=Punkhorn Software, C=US";
+        issueKP = kpg.generateKeyPair();
+        issueCert = Utils.makeCertificate(
+                                        issueKP, issueDN, issueKP, issueDN);
+        
+        //
+        // certificate we sign against
+        //
+        String signingDN = "CN=William J. Collins, E=punkhornsw@gmail.com, O=Punkhorn Software, C=US";
+        signingKP = kpg.generateKeyPair();
+        signingCert = Utils.makeCertificate(
+                                        signingKP, signingDN, issueKP, issueDN);
+        
+        certList = new ArrayList<X509Certificate>();
+
+        certList.add(signingCert);
+        certList.add(issueCert);
+
+    }
+    
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        Security.addProvider(new BouncyCastleProvider());
+        
+        //
+        // set up our certificates
+        //
+        KeyPairGenerator    kpg  = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpg.initialize(1024, new SecureRandom());
+
+        String issueDN = "O=Punkhorn Software, C=US";
+        KeyPair issueKP = kpg.generateKeyPair();
+        X509Certificate issueCert = Utils.makeCertificate(
+                                        issueKP, issueDN, issueKP, issueDN);
+        
+        //
+        // certificate we sign against
+        //
+        String signingDN = "CN=William J. Collins, E=punkhornsw@gmail.com, O=Punkhorn Software, C=US";
+        KeyPair signingKP = kpg.generateKeyPair();
+        X509Certificate signingCert = Utils.makeCertificate(
+                                        signingKP, signingDN, issueKP, issueDN);
+        
+        List<X509Certificate> certList = new ArrayList<X509Certificate>();
+
+        certList.add(signingCert);
+        certList.add(issueCert);
+
+        
+        testServer = new AS2ServerConnection(AS2_VERSION, "MyServer-HTTP/1.1", SERVER_FQDN, 8080, certList.toArray(new Certificate[0]), signingKP.getPrivate());
+        testServer.listen("*", new HttpRequestHandler() {
+            @Override
+            public void handle(HttpRequest request, HttpResponse response, HttpContext context)
+                    throws HttpException, IOException {
+                try {
+                    org.apache.camel.component.as2.api.entity.EntityParser.parseAS2MessageEntity(request);
+                    context.setAttribute(SUBJECT, SUBJECT);
+                    context.setAttribute(FROM, AS2_NAME);
+                } catch (Exception e) {
+                    throw new HttpException("Failed to parse AS2 Message Entity", e);
+                }
+            }
+        });
+    }
+    
+
+    @AfterClass
+    public static void tearDownOnce() throws Exception {
+        testServer.close();
+    }
+    
+    @Before
+    public void setUp() throws Exception {
+        Security.addProvider(new BouncyCastleProvider());
+        
+        setupKeysAndCertificates();
+        
+        // Create and populate certificate store.
+        JcaCertStore certs = new JcaCertStore(certList);
+
+        // Create capabilities vector
+        SMIMECapabilityVector capabilities = new SMIMECapabilityVector();
+        capabilities.addCapability(SMIMECapability.dES_EDE3_CBC);
+        capabilities.addCapability(SMIMECapability.rC2_CBC, 128);
+        capabilities.addCapability(SMIMECapability.dES_CBC);
+
+        // Create signing attributes
+        ASN1EncodableVector attributes = new ASN1EncodableVector();
+        attributes.add(new SMIMEEncryptionKeyPreferenceAttribute(new IssuerAndSerialNumber(new X500Name(signingCert.getIssuerDN().getName()), signingCert.getSerialNumber())));
+        attributes.add(new SMIMECapabilitiesAttribute(capabilities));
+        
+        for (String signingAlgorithmName : AS2SignedDataGenerator
+                .getSupportedSignatureAlgorithmNamesForKey(signingKP.getPrivate())) {
+            try {
+                this.gen = new AS2SignedDataGenerator();
+                this.gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC")
+                        .setSignedAttributeGenerator(new AttributeTable(attributes))
+                        .build(signingAlgorithmName, signingKP.getPrivate(), signingCert));
+                this.gen.addCertificates(certs);
+                break;
+            } catch (Exception e) {
+                this.gen = null;
+                continue;
+            }
+        }
+        
+        if (this.gen == null) {
+            throw new Exception("failed to create signing generator");
+        }
+    }
+    
+    @Test
+    public void plainEDIMessageTest() throws Exception {
+        AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);
+        AS2ClientManager clientManager = new AS2ClientManager(clientConnection);
+        
+        HttpCoreContext httpContext = clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME,
+                AS2MessageStructure.PLAIN, ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII),
+                null, null, null, DISPOSITION_NOTIFICATION_TO, SIGNED_RECEIPT_MIC_ALGORITHMS);
+        
+        HttpRequest request = httpContext.getRequest();
+        assertEquals("Unexpected method value", METHOD, request.getRequestLine().getMethod());
+        assertEquals("Unexpected request URI value", REQUEST_URI, request.getRequestLine().getUri());
+        assertEquals("Unexpected HTTP version value", HttpVersion.HTTP_1_1, request.getRequestLine().getProtocolVersion());
+
+        assertEquals("Unexpected subject value", SUBJECT, request.getFirstHeader(AS2Header.SUBJECT).getValue());
+        assertEquals("Unexpected from value", FROM, request.getFirstHeader(AS2Header.FROM).getValue());
+        assertEquals("Unexpected AS2 version value", AS2_VERSION, request.getFirstHeader(AS2Header.AS2_VERSION).getValue());
+        assertEquals("Unexpected AS2 from value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_FROM).getValue());
+        assertEquals("Unexpected AS2 to value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_TO).getValue());
+        assertTrue("Unexpected message id value", request.getFirstHeader(AS2Header.MESSAGE_ID).getValue().endsWith(CLIENT_FQDN + ">"));
+        assertEquals("Unexpected target host value", TARGET_HOST + ":" + TARGET_PORT, request.getFirstHeader(AS2Header.TARGET_HOST).getValue());
+        assertEquals("Unexpected user agent value", USER_AGENT, request.getFirstHeader(AS2Header.USER_AGENT).getValue());
+        assertNotNull("Date value missing", request.getFirstHeader(AS2Header.DATE));
+        assertNotNull("Content length value missing", request.getFirstHeader(AS2Header.CONTENT_LENGTH));
+        assertTrue("Unexpected content type for message", request.getFirstHeader(AS2Header.CONTENT_TYPE).getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+
+        assertTrue("Request does not contain entity", request instanceof BasicHttpEntityEnclosingRequest);
+        HttpEntity entity = ((BasicHttpEntityEnclosingRequest)request).getEntity();
+        assertNotNull("Request does not contain entity", entity);
+        assertTrue("Unexpected request entity type", entity instanceof ApplicationEDIFACTEntity);
+        ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) entity;
+        assertTrue("Unexpected content type for entity", ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+        assertTrue("Entity not set as main body of request", ediEntity.isMainBody());
+    }
+
+    @Test
+    public void multipartSignedMessageTest() throws Exception {
+        AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);
+        AS2ClientManager clientManager = new AS2ClientManager(clientConnection);
+        
+        HttpCoreContext httpContext = clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME,
+                AS2MessageStructure.SIGNED, ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII),
+                null, certList.toArray(new Certificate[0]), signingKP.getPrivate(), DISPOSITION_NOTIFICATION_TO,
+                SIGNED_RECEIPT_MIC_ALGORITHMS);
+        
+        HttpRequest request = httpContext.getRequest();
+        assertEquals("Unexpected method value", METHOD, request.getRequestLine().getMethod());
+        assertEquals("Unexpected request URI value", REQUEST_URI, request.getRequestLine().getUri());
+        assertEquals("Unexpected HTTP version value", HttpVersion.HTTP_1_1, request.getRequestLine().getProtocolVersion());
+        
+        assertEquals("Unexpected subject value", SUBJECT, request.getFirstHeader(AS2Header.SUBJECT).getValue());
+        assertEquals("Unexpected from value", FROM, request.getFirstHeader(AS2Header.FROM).getValue());
+        assertEquals("Unexpected AS2 version value", AS2_VERSION, request.getFirstHeader(AS2Header.AS2_VERSION).getValue());
+        assertEquals("Unexpected AS2 from value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_FROM).getValue());
+        assertEquals("Unexpected AS2 to value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_TO).getValue());
+        assertTrue("Unexpected message id value", request.getFirstHeader(AS2Header.MESSAGE_ID).getValue().endsWith(CLIENT_FQDN + ">"));
+        assertEquals("Unexpected target host value", TARGET_HOST + ":" + TARGET_PORT, request.getFirstHeader(AS2Header.TARGET_HOST).getValue());
+        assertEquals("Unexpected user agent value", USER_AGENT, request.getFirstHeader(AS2Header.USER_AGENT).getValue());
+        assertNotNull("Date value missing", request.getFirstHeader(AS2Header.DATE));
+        assertNotNull("Content length value missing", request.getFirstHeader(AS2Header.CONTENT_LENGTH));
+        assertTrue("Unexpected content type for message", request.getFirstHeader(AS2Header.CONTENT_TYPE).getValue().startsWith(AS2MediaType.MULTIPART_SIGNED));
+        
+        assertTrue("Request does not contain entity", request instanceof BasicHttpEntityEnclosingRequest);
+        HttpEntity entity = ((BasicHttpEntityEnclosingRequest)request).getEntity();
+        assertNotNull("Request does not contain entity", entity);
+        assertTrue("Unexpected request entity type", entity instanceof MultipartSignedEntity);
+        MultipartSignedEntity signedEntity = (MultipartSignedEntity)entity;
+        assertTrue("Entity not set as main body of request", signedEntity.isMainBody());
+        assertTrue("Request contains invalid number of mime parts", signedEntity.getPartCount() == 2);
+        
+        // Validated first mime part.
+        assertTrue("First mime part incorrect type ", signedEntity.getPart(0) instanceof ApplicationEDIFACTEntity);
+        ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) signedEntity.getPart(0);
+        assertTrue("Unexpected content type for first mime part", ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+        assertFalse("First mime type set as main body of request", ediEntity.isMainBody());
+        
+        // Validate second mime part.
+        assertTrue("Second mime part incorrect type ", signedEntity.getPart(1) instanceof ApplicationPkcs7SignatureEntity);
+        ApplicationPkcs7SignatureEntity signatureEntity = (ApplicationPkcs7SignatureEntity) signedEntity.getPart(1);
+        assertTrue("Unexpected content type for second mime part", signatureEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_PKCS7_SIGNATURE));
+        assertFalse("First mime type set as main body of request", signatureEntity.isMainBody());
+        
+    }
+    
+    @Test
+    public void signatureVerificationTest() throws Exception {
+        AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);
+        AS2ClientManager clientManager = new AS2ClientManager(clientConnection);
+        
+        HttpCoreContext httpContext = clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME,
+                AS2MessageStructure.SIGNED, ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII),
+                null, certList.toArray(new Certificate[0]), signingKP.getPrivate(), DISPOSITION_NOTIFICATION_TO,
+                SIGNED_RECEIPT_MIC_ALGORITHMS);
+        
+        HttpRequest request = httpContext.getRequest();
+        assertTrue("Request does not contain entity", request instanceof BasicHttpEntityEnclosingRequest);
+        HttpEntity entity = ((BasicHttpEntityEnclosingRequest)request).getEntity();
+        assertNotNull("Request does not contain entity", entity);
+        assertTrue("Unexpected request entity type", entity instanceof MultipartSignedEntity);
+        MultipartSignedEntity signedEntity = (MultipartSignedEntity)entity;
+        ApplicationEDIEntity ediMessageEntity = signedEntity.getSignedDataEntity();
+        assertNotNull("Multipart signed entity does not contain EDI message entity", ediMessageEntity);
+        ApplicationPkcs7SignatureEntity signatureEntity = signedEntity.getSignatureEntity();
+        assertNotNull("Multipart signed entity does not contain signature entity", signatureEntity);
+        
+        // Validate Signature
+        assertTrue("Signature is invalid", signedEntity.isValid());
+
+    }
+    
+    @Test
+    public void mdnMessageTest() throws Exception {
+        AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);
+        AS2ClientManager clientManager = new AS2ClientManager(clientConnection);
+        
+        HttpCoreContext httpContext = clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME,
+                AS2MessageStructure.PLAIN, ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII),
+                null, null, null, DISPOSITION_NOTIFICATION_TO, null);
+        
+        @SuppressWarnings("unused")
+        HttpResponse response = httpContext.getResponse();
+    }
+        
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/Utils.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/Utils.java
new file mode 100644
index 0000000..9ba2082
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/Utils.java
@@ -0,0 +1,83 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+public final class Utils {
+    //
+    // certificate serial number seed.
+    //
+    static int  serialNo = 1;
+
+    private Utils() {
+    }
+    
+    public static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey pub) throws IOException {
+        SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(pub.getEncoded());
+
+        BcX509ExtensionUtils utils = new BcX509ExtensionUtils();
+        return utils.createAuthorityKeyIdentifier(info);
+    }
+
+    static SubjectKeyIdentifier createSubjectKeyId(PublicKey pub) throws IOException {
+        SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(pub.getEncoded());
+
+        return new BcX509ExtensionUtils().createSubjectKeyIdentifier(info);
+    }
+
+    /**
+     * create a basic X509 certificate from the given keys
+     */
+    public static X509Certificate makeCertificate(KeyPair subKP, String subDN, KeyPair issKP, String issDN)
+            throws GeneralSecurityException, IOException, OperatorCreationException {
+        PublicKey subPub = subKP.getPublic();
+        PrivateKey issPriv = issKP.getPrivate();
+        PublicKey issPub = issKP.getPublic();
+
+        X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN),
+                BigInteger.valueOf(serialNo++), new Date(System.currentTimeMillis()),
+                new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub);
+
+        v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyId(subPub));
+
+        v3CertGen.addExtension(Extension.authorityKeyIdentifier, false, createAuthorityKeyId(issPub));
+
+        return new JcaX509CertificateConverter().setProvider("BC").getCertificate(
+                v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv)));
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptionsParserTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptionsParserTest.java
new file mode 100644
index 0000000..d9198ec
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/DispositionNotificationOptionsParserTest.java
@@ -0,0 +1,53 @@
+/**
+ * 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.entity;
+
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils.Parameter;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class DispositionNotificationOptionsParserTest {
+
+    private static final String TEST_NAME_VALUES = " signed-receipt-protocol   =   optional  , pkcs7-signature  ;    signed-receipt-micalg   =    required  ,  sha1  ";
+    private static final String SIGNED_RECEIPT_PROTOCOL_ATTRIBUTE = "signed-receipt-protocol";
+    private static final String SIGNED_RECEIPT_PROTOCOL_IMPORTANCE = "optional";
+    private static final String[] SIGNED_RECEIPT_PROTOCOL_VALUES = {"pkcs7-signature"};
+    private static final String SIGNED_RECEIPT_MICALG_ATTRIBUTE = "signed-receipt-micalg";
+    private static final String SIGNED_RECEIPT_MICALG_IMPORTANCE = "required";
+    private static final String[] SIGNED_RECEIPT_MICALG_VALUES = {"sha1"};
+
+    @Test
+    public void parseDispositionNotificationOptionsTest() {
+
+        DispositionNotificationOptions dispositionNotificationOptions =  DispositionNotificationOptionsParser.parseDispositionNotificationOptions(TEST_NAME_VALUES, null);
+        Parameter signedReceiptProtocol = dispositionNotificationOptions.getSignedReceiptProtocol();
+        assertNotNull("signed receipt protocol not parsed", signedReceiptProtocol);
+        assertEquals("Unexpected value for signed receipt protocol attribute", SIGNED_RECEIPT_PROTOCOL_ATTRIBUTE, signedReceiptProtocol.getAttribute());
+        assertEquals("Unexpected value for signed receipt protocol importance", SIGNED_RECEIPT_PROTOCOL_IMPORTANCE, signedReceiptProtocol.getImportance().getImportance());
+        assertArrayEquals("Unexpected value for parameter importance", SIGNED_RECEIPT_PROTOCOL_VALUES, signedReceiptProtocol.getValues());
+
+        Parameter signedReceiptMicalg = dispositionNotificationOptions.getSignedReceiptMicalg();
+        assertNotNull("signed receipt micalg not parsed", signedReceiptProtocol);
+        assertEquals("Unexpected value for signed receipt micalg attribute", SIGNED_RECEIPT_MICALG_ATTRIBUTE, signedReceiptMicalg.getAttribute());
+        assertEquals("Unexpected value for signed receipt micalg importance", SIGNED_RECEIPT_MICALG_IMPORTANCE, signedReceiptMicalg.getImportance().getImportance());
+        assertArrayEquals("Unexpected value for micalg importance", SIGNED_RECEIPT_MICALG_VALUES, signedReceiptMicalg.getValues());
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java
new file mode 100644
index 0000000..6180e0e
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/entity/EntityParserTest.java
@@ -0,0 +1,239 @@
+/**
+ * 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.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.io.AS2SessionInputBuffer;
+import org.apache.camel.component.as2.api.util.EntityUtils;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.impl.EnglishReasonPhraseCatalog;
+import org.apache.http.impl.io.HttpTransportMetricsImpl;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class EntityParserTest {
+    
+    public static final String REPORT_TYPE_HEADER_VALUE = 
+            "disposition-notification; boundary=\"----=_Part_56_1672293592.1028122454656\"\r\n";
+    
+    public static final String DISPOSITION_NOTIFICATION_REPORT_CONTENT = 
+            "\r\n"
+            + "------=_Part_56_1672293592.1028122454656\r\n" 
+            + "Content-Type: text/plain\r\n"
+            + "Content-Transfer-Encoding: 7bit\r\n" + "\r\n" 
+            + "MDN for -\r\n"
+            + " Message ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n" 
+            + "  From: \"\\\"  as2Name  \\\"\"\r\n"
+            + "  To: \"0123456780000\"" 
+            + "  Received on: 2002-07-31 at 09:34:14 (EDT)\r\n" 
+            + " Status: processed\r\n"
+            + " Comment: This is not a guarantee that the message has\r\n"
+            + "  been completely processed or &understood by the receiving\r\n" 
+            + "  translator\r\n" + "\r\n"
+            + "------=_Part_56_1672293592.1028122454656\r\n" 
+            + "Content-Type: message/disposition-notification\r\n"
+            + "Content-Transfer-Encoding: 7bit\r\n" + "\r\n" 
+            + "Reporting-UA: AS2 Server\r\n"
+            + "MDN-Gateway: dns; example.com\r\n" 
+            + "Original-Recipient: rfc822; 0123456780000\r\n"
+            + "Final-Recipient: rfc822; 0123456780000\r\n"
+            + "Original-Message-ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n"
+            + "Disposition: automatic-action/MDN-sent-automatically;\r\n" 
+            + "  processed/warning: you're awesome\r\n"
+            + "Failure: oops-a-failure\r\n" + "Error: oops-an-error\r\n" 
+            + "Warning: oops-a-warning\r\n"
+            + "Received-content-MIC: 7v7F++fQaNB1sVLFtMRp+dF+eG4=, sha1\r\n" 
+            + "\r\n"
+            + "------=_Part_56_1672293592.1028122454656--\r\n";
+
+    public static final String DISPOSITION_NOTIFICATION_REPORT_CONTENT_BOUNDARY = "----=_Part_56_1672293592.1028122454656";
+    
+    public static final String DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME = "US-ASCII";
+    
+    public static final String DISPOSITION_NOTIFICATION_REPORT_CONTENT_TRANSFER_ENCODING = "7bit";
+
+    public static final String TEXT_PLAIN_CONTENT = 
+            "MDN for -\r\n"
+            + " Message ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n" 
+            + "  From: \"\\\"  as2Name  \\\"\"\r\n"
+            + "  To: \"0123456780000\"" 
+            + "  Received on: 2002-07-31 at 09:34:14 (EDT)\r\n" 
+            + " Status: processed\r\n"
+            + " Comment: This is not a guarantee that the message has\r\n"
+            + "  been completely processed or &understood by the receiving\r\n" 
+            + "  translator\r\n" 
+            + "\r\n"
+            + "------=_Part_56_1672293592.1028122454656--\r\n";
+    
+    public static final String TEXT_PLAIN_CONTENT_BOUNDARY = "----=_Part_56_1672293592.1028122454656";
+    
+    public static final String TEXT_PLAIN_CONTENT_CHARSET_NAME = "US-ASCII";
+    
+    public static final String TEXT_PLAIN_CONTENT_TRANSFER_ENCODING = "7bit";
+
+    public static final String EXPECTED_TEXT_PLAIN_CONTENT = 
+            "MDN for -\r\n"
+            + " Message ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n" 
+            + "  From: \"\\\"  as2Name  \\\"\"\r\n"
+            + "  To: \"0123456780000\"" 
+            + "  Received on: 2002-07-31 at 09:34:14 (EDT)\r\n" 
+            + " Status: processed\r\n"
+            + " Comment: This is not a guarantee that the message has\r\n"
+            + "  been completely processed or &understood by the receiving\r\n" 
+            + "  translator\r\n";            
+    
+    public static final String DISPOSITION_NOTIFICATION_CONTENT = 
+            "Reporting-UA: AS2 Server\r\n"
+            + "MDN-Gateway: dns; example.com\r\n" 
+            + "Original-Recipient: rfc822; 0123456780000\r\n"
+            + "Final-Recipient: rfc822; 0123456780000\r\n"
+            + "Original-Message-ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n"
+            + "Disposition: automatic-action/MDN-sent-automatically;\r\n" 
+            + "  processed/warning: you're awesome\r\n"
+            + "Failure: oops-a-failure\r\n" + "Error: oops-an-error\r\n" 
+            + "Warning: oops-a-warning\r\n"
+            + "Received-content-MIC: 7v7F++fQaNB1sVLFtMRp+dF+eG4=, sha1\r\n" 
+            + "\r\n"
+            + "------=_Part_56_1672293592.1028122454656--\r\n";
+    
+    public static final String DISPOSITION_NOTIFICATION_CONTENT_BOUNDARY = "----=_Part_56_1672293592.1028122454656";
+    
+    public static final String DISPOSITION_NOTIFICATION_CONTENT_CHARSET_NAME = "US-ASCII";
+    
+    public static final String DISPOSITION_NOTIFICATION_CONTENT_TRANSFER_ENCODING = "7bit";
+
+    public static final String EXPECTED_REPORTING_UA = "AS2 Server";
+    public static final String EXPECTED_MTN_NAME = "example.com";
+    public static final String EXPECTED_ORIGINAL_RECIPIENT = "rfc822; 0123456780000";
+    public static final String EXPECTED_FINAL_RECIPIENT = "0123456780000";
+    public static final String EXPECTED_ORIGINAL_MESSAGE_ID = "<200207310834482A70BF63@\\\"~~foo~~\\\">";
+    public static final DispositionMode EXPECTED_DISPOSITION_MODE = DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY;
+    public static final String EXPECTED_DISPOSITION_MODIFIER = "warning: you're awesome";
+    public static final AS2DispositionType EXPECTED_DISPOSITION_TYPE = AS2DispositionType.PROCESSED;
+    public static final String[] EXPECTED_FAILURE = {"oops-a-failure"};
+    public static final String[] EXPECTED_ERROR = {"oops-an-error"};
+    public static final String[] EXPECTED_WARNING = {"oops-a-warning"};
+    public static final String EXPECTED_ENCODED_MESSAGE_DIGEST = "7v7F++fQaNB1sVLFtMRp+dF+eG4=";
+    public static final String EXPECTED_DIGEST_ALGORITHM_ID = "sha1";
+    
+    private static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+    
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+    
+    @Test
+    public void parseMessageDispositionNotificationReportMessageTest() throws Exception {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, EnglishReasonPhraseCatalog.INSTANCE.getReason(HttpStatus.SC_OK, null));
+        HttpMessageUtils.setHeaderValue(response, AS2Header.CONTENT_TRANSFER_ENCODING, DISPOSITION_NOTIFICATION_CONTENT_TRANSFER_ENCODING);
+        HttpMessageUtils.setHeaderValue(response, AS2Header.REPORT_TYPE, REPORT_TYPE_HEADER_VALUE);
+        
+        BasicHttpEntity entity = new BasicHttpEntity();
+        entity.setContentType(AS2MimeType.MULTIPART_REPORT);
+        InputStream is = new ByteArrayInputStream(DISPOSITION_NOTIFICATION_REPORT_CONTENT.getBytes(DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME));
+        entity.setContent(is);
+        EntityUtils.setMessageEntity(response, entity);
+        
+        EntityParser.parseMessageDispositionNotificationReportEntity(response);
+        HttpEntity parsedEntity = EntityUtils.getMessageEntity(response);
+        assertNotNull("Unexpected Null message disposition notification report entity", parsedEntity);
+        assertTrue("Unexpected type for message disposition notification report entity", parsedEntity instanceof DispositionNotificationMultipartReportEntity);
+    }
+    
+    @Test
+    public void parseMessageDispositionNotificationReportBodyTest() throws Exception {
+        
+        InputStream is = new ByteArrayInputStream(DISPOSITION_NOTIFICATION_REPORT_CONTENT.getBytes(DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME));
+        AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, null);
+        inbuffer.bind(is);
+
+        DispositionNotificationMultipartReportEntity dispositionNotificationMultipartReportEntity = EntityParser
+                .parseMultipartReportEntityBody(inbuffer, DISPOSITION_NOTIFICATION_REPORT_CONTENT_BOUNDARY,
+                        DISPOSITION_NOTIFICATION_REPORT_CONTENT_CHARSET_NAME,
+                        DISPOSITION_NOTIFICATION_REPORT_CONTENT_TRANSFER_ENCODING);
+        
+        assertNotNull("Unexpected Null disposition notification multipart entity", dispositionNotificationMultipartReportEntity);
+        assertEquals("Unexpected number of body parts", 2, dispositionNotificationMultipartReportEntity.getPartCount());
+        
+        assertTrue("Unexpected type for first body part", dispositionNotificationMultipartReportEntity.getPart(0) instanceof TextPlainEntity);
+        assertTrue("Unexpected type for second body part", dispositionNotificationMultipartReportEntity.getPart(1) instanceof AS2MessageDispositionNotificationEntity);
+    }
+
+    @Test
+    public void parseTextPlainBodyTest() throws Exception {
+        
+        InputStream is = new ByteArrayInputStream(TEXT_PLAIN_CONTENT.getBytes(TEXT_PLAIN_CONTENT_CHARSET_NAME));
+        AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, null);
+        inbuffer.bind(is);
+
+        TextPlainEntity textPlainEntity = EntityParser.parseTextPlainEntityBody(inbuffer, TEXT_PLAIN_CONTENT_BOUNDARY, TEXT_PLAIN_CONTENT_CHARSET_NAME, TEXT_PLAIN_CONTENT_TRANSFER_ENCODING);
+        
+        String text = textPlainEntity.getText();
+        
+        assertEquals("Unexpected text", EXPECTED_TEXT_PLAIN_CONTENT, text);
+    }
+
+    @Test
+    public void parseMessageDispositionNotificationBodyTest() throws Exception {
+
+        InputStream is = new ByteArrayInputStream(DISPOSITION_NOTIFICATION_CONTENT.getBytes(DISPOSITION_NOTIFICATION_CONTENT_CHARSET_NAME));
+        AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, null);
+        inbuffer.bind(is);
+        
+        AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity = EntityParser
+                .parseMessageDispositionNotificationEntityBody(inbuffer, DISPOSITION_NOTIFICATION_CONTENT_BOUNDARY,
+                        DISPOSITION_NOTIFICATION_CONTENT_CHARSET_NAME,
+                        DISPOSITION_NOTIFICATION_CONTENT_TRANSFER_ENCODING);
+
+        assertEquals("Unexpected Reporting UA value", EXPECTED_REPORTING_UA, messageDispositionNotificationEntity.getReportingUA());
+        assertEquals("Unexpected MTN Name", EXPECTED_MTN_NAME, messageDispositionNotificationEntity.getMtnName());
+        assertEquals("Unexpected Original Recipient", EXPECTED_ORIGINAL_RECIPIENT, messageDispositionNotificationEntity.getExtensionFields().get("Original-Recipient"));
+        assertEquals("Unexpected Final Reciptient", EXPECTED_FINAL_RECIPIENT, messageDispositionNotificationEntity.getFinalRecipient());
+        assertEquals("Unexpected Original Message ID", EXPECTED_ORIGINAL_MESSAGE_ID, messageDispositionNotificationEntity.getOriginalMessageId());
+        assertEquals("Unexpected Disposition Mode", EXPECTED_DISPOSITION_MODE, messageDispositionNotificationEntity.getDispositionMode());
+        assertNotNull("Unexpected Null Disposition Modifier", messageDispositionNotificationEntity.getDispositionModifier());
+        assertEquals("Unexpected Disposition Modifier", EXPECTED_DISPOSITION_MODIFIER, messageDispositionNotificationEntity.getDispositionModifier().getModifier());
+        assertEquals("Unexpected Disposition Type", EXPECTED_DISPOSITION_TYPE, messageDispositionNotificationEntity.getDispositionType());
+        assertArrayEquals("Unexpected Failure Array value", EXPECTED_FAILURE, messageDispositionNotificationEntity.getFailureFields());
+        assertArrayEquals("Unexpected Error Array value", EXPECTED_ERROR, messageDispositionNotificationEntity.getErrorFields());
+        assertArrayEquals("Unexpected Warning Array value", EXPECTED_WARNING, messageDispositionNotificationEntity.getWarningFields());
+        assertNotNull("Unexpected Null Received Content MIC", messageDispositionNotificationEntity.getReceivedContentMic());
+        assertEquals("Unexpected Encoded Message Digest", EXPECTED_ENCODED_MESSAGE_DIGEST, messageDispositionNotificationEntity.getReceivedContentMic().getEncodedMessageDigest());
+        assertEquals("Unexpected Digest Algorithm ID", EXPECTED_DIGEST_ALGORITHM_ID, messageDispositionNotificationEntity.getReceivedContentMic().getDigestAlgorithmId());
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java
new file mode 100644
index 0000000..7804125
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/AS2HeaderUtilsTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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.util;
+
+import org.apache.camel.component.as2.api.util.AS2HeaderUtils.Parameter;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.util.CharArrayBuffer;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class AS2HeaderUtilsTest {
+
+    private static final String TEST_NAME_VALUES = " signed-receipt-protocol   =   optional  , pkcs7-signature  ;    signed-receipt-micalg   =    required  ,  sha1  ";
+    private static final String SIGNED_RECEIPT_PROTOCOL_ATTRIBUTE = "signed-receipt-protocol";
+    private static final String SIGNED_RECEIPT_PROTOCOL_IMPORTANCE = "optional";
+    private static final String[] SIGNED_RECEIPT_PROTOCOL_VALUES = {"pkcs7-signature"};
+    private static final String SIGNED_RECEIPT_MICALG_ATTRIBUTE = "signed-receipt-micalg";
+    private static final String SIGNED_RECEIPT_MICALG_IMPORTANCE = "required";
+    private static final String[] SIGNED_RECEIPT_MICALG_VALUES = {"sha1"};
+    
+    @Test
+    public void parseNameValuePairTest() {
+
+        final CharArrayBuffer buffer = new CharArrayBuffer(TEST_NAME_VALUES.length());
+        buffer.append(TEST_NAME_VALUES);
+        final ParserCursor cursor = new ParserCursor(0, TEST_NAME_VALUES.length());
+        
+        Parameter parameter = AS2HeaderUtils.parseParameter(buffer, cursor);
+        assertEquals("Unexpected value for parameter attribute", SIGNED_RECEIPT_PROTOCOL_ATTRIBUTE, parameter.getAttribute());
+        assertEquals("Unexpected value for parameter importance", SIGNED_RECEIPT_PROTOCOL_IMPORTANCE, parameter.getImportance().getImportance());
+        assertArrayEquals("Unexpected value for parameter values", SIGNED_RECEIPT_PROTOCOL_VALUES, parameter.getValues());
+        
+        parameter = AS2HeaderUtils.parseParameter(buffer, cursor);
+        assertEquals("Unexpected value for parameter attribute", SIGNED_RECEIPT_MICALG_ATTRIBUTE, parameter.getAttribute());
+        assertEquals("Unexpected value for parameter importance", SIGNED_RECEIPT_MICALG_IMPORTANCE, parameter.getImportance().getImportance());
+        assertArrayEquals("Unexpected value for parameter values", SIGNED_RECEIPT_MICALG_VALUES, parameter.getValues());
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtilsTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtilsTest.java
new file mode 100644
index 0000000..2b189d5
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/DispositionNotificationContentUtilsTest.java
@@ -0,0 +1,106 @@
+/**
+ * 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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.component.as2.api.entity.AS2DispositionType;
+import org.apache.camel.component.as2.api.entity.AS2MessageDispositionNotificationEntity;
+import org.apache.camel.component.as2.api.entity.DispositionMode;
+import org.apache.camel.component.as2.api.entity.EntityParser;
+import org.apache.camel.component.as2.api.io.AS2SessionInputBuffer;
+import org.apache.http.impl.io.HttpTransportMetricsImpl;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.util.CharArrayBuffer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class DispositionNotificationContentUtilsTest {
+    
+    public static final String DISPOSITION_NOTIFICATION_CONTENT = 
+            "Reporting-UA: AS2 Server\r\n"
+            + "MDN-Gateway: dns; example.com\r\n" 
+            + "Original-Recipient: rfc822; 0123456780000\r\n"
+            + "Final-Recipient: rfc822; 0123456780000\r\n"
+            + "Original-Message-ID: <200207310834482A70BF63@\\\"~~foo~~\\\">\r\n"
+            + "Disposition: automatic-action/MDN-sent-automatically;\r\n" 
+            + "  processed/warning: you're awesome\r\n"
+            + "Failure: oops-a-failure\r\n" 
+            + "Error: oops-an-error\r\n" + "Warning: oops-a-warning\r\n"
+            + "Received-content-MIC: 7v7F++fQaNB1sVLFtMRp+dF+eG4=, sha1\r\n" 
+            + "\r\n";
+
+    public static final String EXPECTED_REPORTING_UA = "AS2 Server";
+    public static final String EXPECTED_MTN_NAME = "example.com";
+    public static final String EXPECTED_ORIGINAL_RECIPIENT = "rfc822; 0123456780000";
+    public static final String EXPECTED_FINAL_RECIPIENT = "0123456780000";
+    public static final String EXPECTED_ORIGINAL_MESSAGE_ID = "<200207310834482A70BF63@\\\"~~foo~~\\\">";
+    public static final DispositionMode EXPECTED_DISPOSITION_MODE = DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY;
+    public static final String EXPECTED_DISPOSITION_MODIFIER = "warning: you're awesome";
+    public static final AS2DispositionType EXPECTED_DISPOSITION_TYPE = AS2DispositionType.PROCESSED;
+    public static final String[] EXPECTED_FAILURE = {"oops-a-failure"};
+    public static final String[] EXPECTED_ERROR = {"oops-an-error"};
+    public static final String[] EXPECTED_WARNING = {"oops-a-warning"};
+    public static final String EXPECTED_ENCODED_MESSAGE_DIGEST = "7v7F++fQaNB1sVLFtMRp+dF+eG4=";
+    public static final String EXPECTED_DIGEST_ALGORITHM_ID = "sha1";
+        
+
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void test() throws Exception {
+        
+        InputStream is = new ByteArrayInputStream(DISPOSITION_NOTIFICATION_CONTENT.getBytes());
+        
+        AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new HttpTransportMetricsImpl(), 8 * 1024);
+        inbuffer.bind(is);
+        
+        List<CharArrayBuffer> dispositionNotificationFields = EntityParser.parseBodyPartFields(inbuffer, null, BasicLineParser.INSTANCE, new ArrayList<CharArrayBuffer>());
+        AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity =  DispositionNotificationContentUtils.parseDispositionNotification(dispositionNotificationFields);
+        
+        assertEquals("Unexpected Reporting UA value", EXPECTED_REPORTING_UA, messageDispositionNotificationEntity.getReportingUA());
+        assertEquals("Unexpected MTN Name", EXPECTED_MTN_NAME, messageDispositionNotificationEntity.getMtnName());
+        assertEquals("Unexpected Original Recipient", EXPECTED_ORIGINAL_RECIPIENT, messageDispositionNotificationEntity.getExtensionFields().get("Original-Recipient"));
+        assertEquals("Unexpected Final Reciptient", EXPECTED_FINAL_RECIPIENT, messageDispositionNotificationEntity.getFinalRecipient());
+        assertEquals("Unexpected Original Message ID", EXPECTED_ORIGINAL_MESSAGE_ID, messageDispositionNotificationEntity.getOriginalMessageId());
+        assertEquals("Unexpected Disposition Mode", EXPECTED_DISPOSITION_MODE, messageDispositionNotificationEntity.getDispositionMode());
+        assertNotNull("Unexpected Null Disposition Modifier", messageDispositionNotificationEntity.getDispositionModifier());
+        assertEquals("Unexpected Disposition Modifier", EXPECTED_DISPOSITION_MODIFIER, messageDispositionNotificationEntity.getDispositionModifier().getModifier());
+        assertEquals("Unexpected Disposition Type", EXPECTED_DISPOSITION_TYPE, messageDispositionNotificationEntity.getDispositionType());
+        assertArrayEquals("Unexpected Failure Array value", EXPECTED_FAILURE, messageDispositionNotificationEntity.getFailureFields());
+        assertArrayEquals("Unexpected Error Array value", EXPECTED_ERROR, messageDispositionNotificationEntity.getErrorFields());
+        assertArrayEquals("Unexpected Warning Array value", EXPECTED_WARNING, messageDispositionNotificationEntity.getWarningFields());
+        assertNotNull("Unexpected Null Received Content MIC", messageDispositionNotificationEntity.getReceivedContentMic());
+        assertEquals("Unexpected Encoded Message Digest", EXPECTED_ENCODED_MESSAGE_DIGEST, messageDispositionNotificationEntity.getReceivedContentMic().getEncodedMessageDigest());
+        assertEquals("Unexpected Digest Algorithm ID", EXPECTED_DIGEST_ALGORITHM_ID, messageDispositionNotificationEntity.getReceivedContentMic().getDigestAlgorithmId());
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/MicUtilsTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/MicUtilsTest.java
new file mode 100644
index 0000000..6674df8
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/MicUtilsTest.java
@@ -0,0 +1,111 @@
+/**
+ * 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.util;
+
+import java.io.InputStream;
+import java.security.Security;
+
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.AS2TransferEncoding;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIFACTEntity;
+import org.apache.camel.component.as2.api.util.MicUtils.ReceivedContentMic;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class MicUtilsTest {
+    
+    public static final Logger LOG = LoggerFactory.getLogger(MicUtilsTest.class);
+
+    private static final String DISPOSITION_NOTIFICATION_OPTIONS_VALUE = " signed-receipt-protocol   =   optional  , pkcs7-signature  ;    signed-receipt-micalg   =    required  ,  sha1  ";
+    private static final String CONTENT_TYPE_VALUE = AS2MimeType.APPLICATION_EDIFACT;
+    private static final String EDI_MESSAGE = 
+            "UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'\n"
+            + "UNH+00000000000117+INVOIC:D:97B:UN'\n" 
+            + "BGM+380+342459+9'\n" 
+            + "DTM+3:20060515:102'\n"
+            + "RFF+ON:521052'\n" 
+            + "NAD+BY+792820524::16++CUMMINS MID-RANGE ENGINE PLANT'\n"
+            + "NAD+SE+005435656::16++GENERAL WIDGET COMPANY'\n" 
+            + "CUX+1:USD'\n" 
+            + "LIN+1++157870:IN'\n"
+            + "IMD+F++:::WIDGET'\n" 
+            + "QTY+47:1020:EA'\n" 
+            + "ALI+US'\n"
+            + "MOA+203:1202.58'\n" 
+            + "PRI+INV:1.179'\n"
+            + "LIN+2++157871:IN'\n" 
+            + "IMD+F++:::DIFFERENT WIDGET'\n" 
+            + "QTY+47:20:EA'\n" 
+            + "ALI+JP'\n"
+            + "MOA+203:410'\n" 
+            + "PRI+INV:20.5'\n" 
+            + "UNS+S'\n" 
+            + "MOA+39:2137.58'\n" 
+            + "ALC+C+ABG'\n" 
+            + "MOA+8:525'\n"
+            + "UNT+23+00000000000117'\n" 
+            + "UNZ+1+00000000000778'";
+
+    private static final String EXPECTED_MESSAGE_DIGEST_ALGORITHM = "sha1";
+    private static final String EXPECTED_ENCODED_MESSAGE_DIGEST = "PaQyByOuBL7XiYyts4Sdmvl1WME=";
+
+    @Before
+    public void setUp() throws Exception {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void createReceivedContentMicTest() throws Exception {
+        
+        HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/", HttpVersion.HTTP_1_1);
+        request.addHeader(AS2Header.DISPOSITION_NOTIFICATION_OPTIONS, DISPOSITION_NOTIFICATION_OPTIONS_VALUE);
+        request.addHeader(AS2Header.CONTENT_TYPE, CONTENT_TYPE_VALUE);
+        
+        ApplicationEDIFACTEntity edifactEntity = new ApplicationEDIFACTEntity(EDI_MESSAGE, AS2Charset.US_ASCII, AS2TransferEncoding.NONE, true);
+        InputStream is = edifactEntity.getContent();
+        BasicHttpEntity basicEntity = new BasicHttpEntity();
+        basicEntity.setContent(is);
+        basicEntity.setContentType(CONTENT_TYPE_VALUE);
+        request.setEntity(basicEntity);
+
+        ReceivedContentMic receivedContentMic = MicUtils.createReceivedContentMic(request);
+        assertNotNull("Failed to create Received Content MIC");
+        LOG.debug("Digest Algorithm: " + receivedContentMic.getDigestAlgorithmId());
+        assertEquals("Unexpected digest algorithm value", EXPECTED_MESSAGE_DIGEST_ALGORITHM, receivedContentMic.getDigestAlgorithmId());
+        LOG.debug("Encoded Message Digest: " + receivedContentMic.getEncodedMessageDigest());
+        assertEquals("Unexpected encoded message digest value", EXPECTED_ENCODED_MESSAGE_DIGEST, receivedContentMic.getEncodedMessageDigest());
+        
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-api/src/test/resources/log4j2.properties b/components/camel-as2/camel-as2-api/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..d9f0508
--- /dev/null
+++ b/components/camel-as2/camel-as2-api/src/test/resources/log4j2.properties
@@ -0,0 +1,23 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.out.ref = out
diff --git a/components/camel-as2/camel-as2-component/pom.xml b/components/camel-as2/camel-as2-component/pom.xml
new file mode 100644
index 0000000..5d6fec0
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/pom.xml
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-as2-parent</artifactId>
+    <version>2.22.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-as2</artifactId>
+  <packaging>jar</packaging>
+  <name>Camel :: AS2 :: Component</name>
+  <description>Camel AS2 component</description>
+
+  <properties>
+    <schemeName>as2</schemeName>
+    <componentName>AS2</componentName>
+    <componentPackage>org.apache.camel.component.as2</componentPackage>
+    <outPackage>org.apache.camel.component.as2.internal</outPackage>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-as2-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <!-- Camel annotations in provided scope to avoid compile errors in IDEs -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>spi-annotations</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- Component API javadoc in provided scope to read API signatures -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-as2-api</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+      <scope>provided</scope>
+    </dependency>
+    
+    <!-- logging -->
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <defaultGoal>install</defaultGoal>
+
+    <plugins>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+      </plugin>
+
+      <!-- to generate the MANIFEST-FILE of the bundle -->
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>3.2.0</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>Camel Component for ${componentName}</Bundle-Name>
+            <Bundle-SymbolicName>org.apache.camel.camel-as2</Bundle-SymbolicName>
+            <Export-Service>org.apache.camel.spi.ComponentResolver;component=${schemeName}</Export-Service>
+            <Export-Package>${componentPackage};version=${project.version}</Export-Package>
+            <Import-Package>
+              ${componentPackage}.api;version=${project.version},
+              ${componentPackage};version=${project.version},
+              org.apache.camel.*;version=2.18.3
+            </Import-Package>
+            <Private-Package>${outPackage}</Private-Package>
+            <Implementation-Title>Apache Camel</Implementation-Title>
+            <Implementation-Version>${project.version}</Implementation-Version>
+            <Karaf-Info>Camel;${project.artifactId}=${project.version}</Karaf-Info>
+            <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
+            <_failok>false</_failok>
+          </instructions>
+        </configuration>
+      </plugin>
+
+      <!-- generate Component source and test source -->
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-api-component-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-test-component-classes</id>
+            <goals>
+              <goal>fromApis</goal>
+            </goals>
+            <configuration>
+              <apis>
+                <api>
+                    <apiName>send</apiName>
+                    <proxyClass>org.apache.camel.component.as2.api.AS2ClientManager</proxyClass>
+                    <fromJavadoc>
+                      <excludeMethods>createSigningGenerator</excludeMethods>
+                    </fromJavadoc>
+                </api>
+                <api>
+                    <apiName>listen</apiName>
+                    <proxyClass>org.apache.camel.component.as2.api.AS2ServerManager</proxyClass>
+	                <fromJavadoc>
+	                  <excludeMethods>stopListening|handleMDNResponse</excludeMethods>
+	                </fromJavadoc>
+                    <excludeConfigNames>handler</excludeConfigNames>
+                </api>
+              </apis>
+              <!-- Specify global values for all APIs here, these are overridden at API level
+              <substitutions/>
+              <excludeConfigNames/>
+              <excludeConfigTypes/>
+              <extraOptions/>
+              <fromJavadoc/>
+              <aliases/>
+              <nullableOptions/>
+              -->
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- generate components meta-data and validate component includes documentation etc -->
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-package-maven-plugin</artifactId>
+        <version>${project.version}</version>
+        <executions>
+          <execution>
+            <id>prepare</id>
+            <goals>
+              <goal>prepare-components</goal>
+            </goals>
+            <phase>generate-resources</phase>
+          </execution>
+          <execution>
+            <id>validate</id>
+            <goals>
+              <goal>validate-components</goal>
+            </goals>
+            <phase>prepare-package</phase>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- add generated source and test source to build -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>add-generated-sources</id>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>${project.build.directory}/generated-sources/camel-component</source>
+              </sources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>add-generated-test-sources</id>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>${project.build.directory}/generated-test-sources/camel-component</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.camel</groupId>
+          <artifactId>camel-api-component-maven-plugin</artifactId>
+          <version>${project.version}</version>
+          <configuration>
+            <scheme>${schemeName}</scheme>
+            <componentName>${componentName}</componentName>
+            <componentPackage>${componentPackage}</componentPackage>
+            <outPackage>${outPackage}</outPackage>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-api-component-maven-plugin</artifactId>
+        <version>${project.version}</version>
+        <configuration>
+          <scheme>${schemeName}</scheme>
+          <componentName>${componentName}</componentName>
+          <componentPackage>${componentPackage}</componentPackage>
+          <outPackage>${outPackage}</outPackage>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/components/camel-as2/camel-as2-component/src/main/java/META-INF/MANIFEST.MF b/components/camel-as2/camel-as2-component/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path: 
+
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Component.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Component.java
new file mode 100644
index 0000000..2bfd0de
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Component.java
@@ -0,0 +1,59 @@
+/**
+ * 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;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.component.as2.internal.AS2ApiCollection;
+import org.apache.camel.component.as2.internal.AS2ApiName;
+import org.apache.camel.util.component.AbstractApiComponent;
+
+/**
+ * Represents the component that manages {@link AS2Endpoint}.
+ */
+public class AS2Component extends AbstractApiComponent<AS2ApiName, AS2Configuration, AS2ApiCollection> {
+
+    public AS2Component() {
+        super(AS2Endpoint.class, AS2ApiName.class, AS2ApiCollection.getCollection());
+    }
+
+    public AS2Component(CamelContext context) {
+        super(context, AS2Endpoint.class, AS2ApiName.class, AS2ApiCollection.getCollection());
+    }
+
+    @Override
+    protected AS2ApiName getApiName(String apiNameStr) throws IllegalArgumentException {
+        return AS2ApiName.fromValue(apiNameStr);
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String methodName, AS2ApiName apiName,
+                                      AS2Configuration endpointConfiguration) {
+        AS2Endpoint endpoint = new AS2Endpoint(uri, this, apiName, methodName, endpointConfiguration);
+        endpoint.setName(methodName);
+        return endpoint;
+    }
+
+    /**
+     * To use the shared configuration
+     */
+    @Override
+    public void setConfiguration(AS2Configuration configuration) {
+        super.setConfiguration(configuration);
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
new file mode 100644
index 0000000..6ad35c1
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
@@ -0,0 +1,432 @@
+/**
+ * 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;
+
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.as2.api.AS2MessageStructure;
+import org.apache.camel.component.as2.internal.AS2ApiName;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.apache.camel.spi.UriPath;
+import org.apache.http.entity.ContentType;
+
+/**
+ * Component configuration for AS2 component.
+ */
+@UriParams
+public class AS2Configuration {
+
+    @UriPath
+    @Metadata(required = "true")
+    private AS2ApiName apiName;
+
+    @UriPath
+    @Metadata(required = "true")
+    private String methodName;
+    
+    @UriPath
+    private String as2Version = "1.1";
+
+    @UriParam
+    private String userAgent = "Camel AS2 Client Endpoint";
+    
+    @UriParam
+    private String server = "Camel AS2 Server Endpoint";
+    
+    @UriParam
+    private String serverFqdn = "camel.apache.org";
+    
+    @UriParam
+    private String targetHostname;
+    
+    @UriParam
+    private Integer targetPortNumber;
+    
+    @UriParam
+    private String clientFqdn = "camel.apache.org";
+    
+    @UriParam
+    private Integer serverPortNumber;
+    
+    @UriParam
+    private String requestUri = "/";
+    
+    @UriParam
+    private ContentType ediMessageType;
+    
+    @UriParam
+    private String ediMessageTransferEncoding;
+    
+    @UriParam
+    private AS2MessageStructure as2MessageStructure;
+    
+    @UriParam
+    private String subject;
+    
+    @UriParam
+    private String from;
+    
+    @UriParam
+    private String as2From;
+    
+    @UriParam
+    private String as2To;
+    
+    @UriParam
+    private String signingAlgorithmName;
+    
+    @UriParam
+    private Certificate[] signingCertificateChain;
+    
+    @UriParam
+    private PrivateKey signingPrivateKey;
+    
+    @UriParam
+    private String dispositionNotificationTo;
+    
+    @UriParam
+    private String[] signedReceiptMicAlgorithms;
+    
+    /**
+     * What kind of operation to perform
+     * 
+     * @return the API Name
+     */
+    public AS2ApiName getApiName() {
+        return apiName;
+    }
+
+    /**
+     * What kind of operation to perform
+     * 
+     * @param apiName -
+     *            the API Name to set
+     */
+    public void setApiName(AS2ApiName apiName) {
+        this.apiName = apiName;
+    }
+
+    /**
+     * What sub operation to use for the selected operation
+     * 
+     * @return The methodName
+     */
+    public String getMethodName() {
+        return methodName;
+    }
+
+    /**
+     * What sub operation to use for the selected operation
+     * 
+     * @param methodName -
+     *            the methodName to set
+     */
+    public void setMethodName(String methodName) {
+        this.methodName = methodName;
+    }
+
+    /**
+     * The version of the AS2 protocol.
+     * 
+     * @return The version of the AS2 protocol.
+     */
+    public String getAs2Version() {
+        return as2Version;
+    }
+
+    /**
+     * The version of the AS2 protocol.
+     * 
+     * @param as2Version - the version of the AS2 protocol.
+     */
+    public void setAs2Version(String as2Version) {
+        if (!as2Version.equals("1.0") && !as2Version.equals("1.1")) {
+            throw new IllegalArgumentException(String.format("Value '%s' of configuration parameter 'as2Version' must be either '1.0' or '1.1'", as2Version));
+        }
+        this.as2Version = as2Version;
+    }
+
+    /**
+     * The value included in the <code>User-Agent</code>
+     * message header identifying the AS2 user agent.
+     * 
+     * @return AS2 user agent identification string.
+     */
+    public String getUserAgent() {
+        return userAgent;
+    }
+
+    /**
+     * The value included in the <code>User-Agent</code>
+     * message header identifying the AS2 user agent.
+     * 
+     * @param userAgent - AS2 user agent identification string.
+     */
+    public void setUserAgent(String userAgent) {
+        this.userAgent = userAgent;
+    }
+
+    /**
+     * The value included in the <code>Server</code> 
+     * message header identifying the AS2 Server.
+     * 
+     * @return AS2 server identification string.
+     */
+    public String getServer() {
+        return server;
+    }
+
+    /**
+     * The value included in the <code>Server</code> 
+     * message header identifying the AS2 Server.
+     * 
+     * @param server - AS2 server identification string.
+     */
+    public void setServer(String server) {
+        this.server = server;
+    }
+
+    /**
+     * The Server Fully Qualified Domain Name (FQDN). 
+     * 
+     * <p> Used in message ids sent by endpoint.
+     * 
+     * @return The FQDN of client.
+     */
+    public String getServerFqdn() {
+        return serverFqdn;
+    }
+
+    /**
+     * The Server Fully Qualified Domain Name (FQDN). 
+     * 
+     * <p> Used in message ids sent by endpoint.
+     * 
+     * @param clientFqdn - the FQDN of client.
+     */
+    public void setServerFqdn(String serverFqdn) {
+        if (clientFqdn == null) {
+            throw new RuntimeCamelException("Parameter 'serverFqdn' can not be null");
+        }
+        this.serverFqdn = serverFqdn;
+    }
+
+    /**
+     * The host name (IP or DNS) of target host.
+     * 
+     * @return The target host name (IP or DNS name).
+     */
+    public String getTargetHostname() {
+        return targetHostname;
+    }
+
+    /**
+     * The host name (IP or DNS name) of target host.
+     * 
+     * @param targetHostname - the target host name (IP or DNS name).
+     */
+    public void setTargetHostname(String targetHostname) {
+        this.targetHostname = targetHostname;
+    }
+
+    /**
+     * The port number of target host.
+     * 
+     * @return The target port number. -1 indicates the scheme default port.
+     */
+    public int getTargetPortNumber() {
+        return targetPortNumber;
+    }
+
+    /**
+     * The port number of target host.
+     * 
+     * @param targetPortNumber - the target port number. -1 indicates the scheme default port.
+     */
+    public void setTargetPortNumber(String targetPortNumber) {
+        try {
+            this.targetPortNumber = Integer.valueOf(targetPortNumber);
+        } catch (NumberFormatException e) {
+            throw new RuntimeCamelException(String.format("Invalid target port number: %s", targetPortNumber));
+        }
+    }
+
+    /**
+     * The Client Fully Qualified Domain Name (FQDN). 
+     * 
+     * <p> Used in message ids sent by endpoint.
+     * 
+     * @return The FQDN of client.
+     */
+    public String getClientFqdn() {
+        return clientFqdn;
+    }
+
+    /**
+     * The Client Fully Qualified Domain Name (FQDN). 
+     * 
+     * <p> Used in message ids sent by endpoint.
+     * 
+     * @param clientFqdn - the FQDN of client.
+     */
+    public void setClientFqdn(String clientFqdn) {
+        if (clientFqdn == null) {
+            throw new RuntimeCamelException("Parameter 'clientFqdn' can not be null");
+        }
+        this.clientFqdn = clientFqdn;
+    }
+
+    /**
+     * The port number of server.
+     * 
+     * @return The server port number.
+     */
+    public Integer getServerPortNumber() {
+        return serverPortNumber;
+    }
+
+    /**
+     * The port number of server.
+     * 
+     * @param serverPortNumber - the server port number.
+     */
+    public void setServerPortNumber(String serverPortNumber) {
+        try {
+            this.serverPortNumber = Integer.valueOf(serverPortNumber);
+        } catch (NumberFormatException e) {
+            throw new RuntimeCamelException(String.format("Invalid target port number: %s", targetPortNumber));
+        }
+    }
+
+    public String getRequestUri() {
+        return requestUri;
+    }
+
+    public void setRequestUri(String requestUri) {
+        this.requestUri = requestUri;
+    }
+
+    public ContentType getEdiMessageType() {
+        return ediMessageType;
+    }
+
+    public void setEdiMessageType(ContentType ediMessageType) {
+        this.ediMessageType = ediMessageType;
+    }
+
+    public String getEdiMessageTransferEncoding() {
+        return ediMessageTransferEncoding;
+    }
+
+    public void setEdiMessageTransferEncoding(String ediMessageTransferEncoding) {
+        this.ediMessageTransferEncoding = ediMessageTransferEncoding;
+    }
+
+    public AS2MessageStructure getAs2MessageStructure() {
+        return as2MessageStructure;
+    }
+
+    public void setAs2MessageStructure(AS2MessageStructure as2MessageStructure) {
+        this.as2MessageStructure = as2MessageStructure;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public String getFrom() {
+        return from;
+    }
+
+    public void setFrom(String from) {
+        this.from = from;
+    }
+
+    public String getAs2From() {
+        return as2From;
+    }
+
+    public void setAs2From(String as2From) {
+        this.as2From = as2From;
+    }
+
+    public String getAs2To() {
+        return as2To;
+    }
+
+    public void setAs2To(String as2To) {
+        this.as2To = as2To;
+    }
+
+    public String getSigningAlgorithmName() {
+        return signingAlgorithmName;
+    }
+
+    public void setSigningAlgorithmName(String signingAlgorithmName) {
+        this.signingAlgorithmName = signingAlgorithmName;
+    }
+
+    public Certificate[] getSigningCertificateChain() {
+        return signingCertificateChain;
+    }
+
+    public void setSigningCertificateChain(Certificate[] signingCertificateChain) {
+        this.signingCertificateChain = signingCertificateChain;
+    }
+
+    public PrivateKey getSigningPrivateKey() {
+        return signingPrivateKey;
+    }
+
+    public void setSigningPrivateKey(PrivateKey signingPrivateKey) {
+        this.signingPrivateKey = signingPrivateKey;
+    }
+
+    public void setTargetPortNumber(Integer targetPortNumber) {
+        this.targetPortNumber = targetPortNumber;
+    }
+
+    public void setServerPortNumber(Integer serverPortNumber) {
+        this.serverPortNumber = serverPortNumber;
+    }
+
+    public String getDispositionNotificationTo() {
+        return dispositionNotificationTo;
+    }
+
+    public void setDispositionNotificationTo(String dispositionNotificationTo) {
+        this.dispositionNotificationTo = dispositionNotificationTo;
+    }
+
+    public String[] getSignedReceiptMicAlgorithms() {
+        return signedReceiptMicAlgorithms;
+    }
+
+    public void setSignedReceiptMicAlgorithms(String[] signedReceiptMicAlgorithms) {
+        this.signedReceiptMicAlgorithms = signedReceiptMicAlgorithms;
+    }
+    
+    
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java
new file mode 100644
index 0000000..2616b59
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java
@@ -0,0 +1,116 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.Processor;
+import org.apache.camel.component.as2.api.AS2ServerConnection;
+import org.apache.camel.component.as2.api.AS2ServerManager;
+import org.apache.camel.component.as2.api.entity.EntityParser;
+import org.apache.camel.component.as2.internal.AS2ApiName;
+import org.apache.camel.util.component.AbstractApiConsumer;
+import org.apache.camel.util.component.ApiConsumerHelper;
+import org.apache.camel.util.component.ApiMethod;
+import org.apache.camel.util.component.ApiMethodHelper;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+
+/**
+ * The AS2 consumer.
+ */
+public class AS2Consumer extends AbstractApiConsumer<AS2ApiName, AS2Configuration> implements HttpRequestHandler {
+    
+    private static final String HANDLER_PROPERTY = "handler";
+    private static final String REQUEST_URI_PROPERTY = "requestUri";
+
+    private AS2ServerConnection as2ServerConnection;
+    
+    private AS2ServerManager apiProxy;
+    
+    private final ApiMethod apiMethod;
+
+    private final Map<String, Object> properties;
+
+    public AS2Consumer(AS2Endpoint endpoint, Processor processor) {
+        super(endpoint, processor);
+
+        apiMethod = ApiConsumerHelper.findMethod(endpoint, this);
+
+        // Add listener property to register this consumer as listener for
+        // events.
+        properties = new HashMap<String, Object>();
+        properties.putAll(endpoint.getEndpointProperties());
+        properties.put(HANDLER_PROPERTY, this);
+
+        as2ServerConnection = endpoint.getAS2ServerConnection();
+        
+        apiProxy = new AS2ServerManager(as2ServerConnection);
+    }
+
+    @Override
+    public void interceptPropertyNames(Set<String> propertyNames) {
+        propertyNames.add(HANDLER_PROPERTY);
+    }
+    
+    @Override
+    protected int poll() throws Exception {
+        return 0;
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+        
+        // invoke the API method to start listening
+        ApiMethodHelper.invokeMethod(apiProxy, apiMethod, properties);
+    }
+    
+    @Override
+    protected void doStop() throws Exception {
+        String requestUri = (String) properties.get(REQUEST_URI_PROPERTY);
+        apiProxy.stopListening(requestUri);
+
+        super.doStop();
+    }
+
+    @Override
+    public void handle(HttpRequest request, HttpResponse response, HttpContext context)
+            throws HttpException, IOException {
+        try {
+            if (request instanceof HttpEntityEnclosingRequest) {
+                EntityParser.parseAS2MessageEntity(request);
+                // TODO derive last to parameters from configuration.
+                apiProxy.handleMDNResponse((HttpEntityEnclosingRequest)request, response, context, "MDN Response", "Camel AS2 Server Endpoint");
+            }
+            // Convert HTTP context to exchange and process
+            log.debug("Processed {} event for {}", ApiConsumerHelper.getResultsProcessed(this, context, false),
+                    as2ServerConnection);
+        } catch (Exception e) {
+            log.info("Received exception consuming AS2 message: ", e);
+        }
+        
+    }
+    
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java
new file mode 100644
index 0000000..6002fd4
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java
@@ -0,0 +1,162 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.Map;
+
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.as2.api.AS2ClientConnection;
+import org.apache.camel.component.as2.api.AS2ClientManager;
+import org.apache.camel.component.as2.api.AS2ServerConnection;
+import org.apache.camel.component.as2.api.AS2ServerManager;
+import org.apache.camel.component.as2.internal.AS2ApiCollection;
+import org.apache.camel.component.as2.internal.AS2ApiName;
+import org.apache.camel.component.as2.internal.AS2ConnectionHelper;
+import org.apache.camel.component.as2.internal.AS2Constants;
+import org.apache.camel.component.as2.internal.AS2PropertiesHelper;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.util.component.AbstractApiEndpoint;
+import org.apache.camel.util.component.ApiMethod;
+import org.apache.camel.util.component.ApiMethodPropertiesHelper;
+
+/**
+ * Represents a AS2 endpoint.
+ */
+@UriEndpoint(scheme = "as2", title = "AS2", syntax = "as2:name", consumerClass = AS2Consumer.class, label = "AS2")
+public class AS2Endpoint extends AbstractApiEndpoint<AS2ApiName, AS2Configuration> {
+
+    @UriPath @Metadata(required = "true")
+    private String name;
+
+    private Object apiProxy;
+    
+    private AS2ClientConnection as2ClientConnection;
+
+    private AS2ServerConnection as2ServerConnection;
+
+    public AS2Endpoint(String uri, AS2Component component,
+                         AS2ApiName apiName, String methodName, AS2Configuration endpointConfiguration) {
+        super(uri, component, apiName, methodName, AS2ApiCollection.getCollection().getHelper(apiName), endpointConfiguration);
+
+    }
+    
+    public AS2ClientConnection getAS2ClientConnection() {
+        return as2ClientConnection;
+    }
+
+    public AS2ServerConnection getAS2ServerConnection() {
+        return as2ServerConnection;
+    }
+
+    public Producer createProducer() throws Exception {
+        return new AS2Producer(this);
+    }
+
+    public Consumer createConsumer(Processor processor) throws Exception {
+        // make sure inBody is not set for consumers
+        if (inBody != null) {
+            throw new IllegalArgumentException("Option inBody is not supported for consumer endpoint");
+        }
+        final AS2Consumer consumer = new AS2Consumer(this, processor);
+        // also set consumer.* properties
+        configureConsumer(consumer);
+        return consumer;
+    }
+
+    @Override
+    protected ApiMethodPropertiesHelper<AS2Configuration> getPropertiesHelper() {
+        return AS2PropertiesHelper.getHelper();
+    }
+
+    protected String getThreadProfileName() {
+        return AS2Constants.THREAD_PROFILE_NAME;
+    }
+
+    @Override
+    protected void afterConfigureProperties() {
+        // create HTTP connection eagerly, a good way to validate configuration
+        switch (apiName) {
+        case SEND:
+            createAS2ClientConnection();
+            break;
+        case LISTEN:
+            createAS2ServerConnection();
+            break;
+        default:
+            break;
+        }
+    }
+    
+    @Override
+    public Object getApiProxy(ApiMethod method, Map<String, Object> args) {
+        if (apiProxy == null) {
+            createApiProxy(method, args);
+        }
+        return apiProxy;
+    }
+    
+    /**
+     * Some description of this option, and what it does
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    private void createApiProxy(ApiMethod method, Map<String, Object> args) {
+        switch (apiName) {
+        case SEND:
+            apiProxy = new AS2ClientManager(getAS2ClientConnection());
+            break;
+        case LISTEN:
+            apiProxy = new AS2ServerManager(getAS2ServerConnection());
+            break;
+        default:
+            throw new IllegalArgumentException("Invalid API name " + apiName);
+        }
+    }
+    
+    private void createAS2ClientConnection() {
+        try {
+            as2ClientConnection = AS2ConnectionHelper.createAS2ClientConnection(configuration);
+        } catch (UnknownHostException e) {
+            throw new RuntimeCamelException(String.format("Client HTTP connection failed: Unknown target host '%s'",
+                    configuration.getTargetHostname()));
+        } catch (IOException e) {
+            throw new RuntimeCamelException("Client HTTP connection failed", e);
+        }
+    }
+    
+    private void createAS2ServerConnection() {
+        try {
+            as2ServerConnection = AS2ConnectionHelper.createAS2ServerConnection(configuration);
+        } catch (IOException e) {
+            throw new RuntimeCamelException("Server HTTP connection failed", e);
+        }
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java
new file mode 100644
index 0000000..5fc4a0f
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java
@@ -0,0 +1,31 @@
+/**
+ * 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;
+
+import org.apache.camel.component.as2.internal.AS2ApiName;
+import org.apache.camel.component.as2.internal.AS2PropertiesHelper;
+import org.apache.camel.util.component.AbstractApiProducer;
+
+/**
+ * The AS2 producer.
+ */
+public class AS2Producer extends AbstractApiProducer<AS2ApiName, AS2Configuration> {
+
+    public AS2Producer(AS2Endpoint endpoint) {
+        super(endpoint, AS2PropertiesHelper.getHelper());
+    }
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java
new file mode 100644
index 0000000..23e769c
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java
@@ -0,0 +1,73 @@
+/**
+ * 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.internal;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.component.as2.AS2Configuration;
+import org.apache.camel.component.as2.api.AS2ClientConnection;
+import org.apache.camel.component.as2.api.AS2ServerConnection;
+
+/**
+ * Utility class for creating AS2 connections.
+ */
+public final class AS2ConnectionHelper {
+    
+    private static Map<Integer, AS2ServerConnection> serverConnections = new HashMap<Integer, AS2ServerConnection>(); 
+    
+    /**
+     * Prevent instantiation
+     */
+    private AS2ConnectionHelper() {
+    }
+        
+    /**
+     * Create an AS2 client connection.
+     * 
+     * @param configuration - configuration used to configure connection.
+     * @return The AS2 client connection.
+     * @throws UnknownHostException Failed to establish connection due to unknown host.
+     * @throws IOException - Failed to establish connection.
+     */
+    public static AS2ClientConnection createAS2ClientConnection(AS2Configuration configuration) throws UnknownHostException, IOException {
+        return new AS2ClientConnection(configuration.getAs2Version(), configuration.getUserAgent(), configuration.getClientFqdn(),
+                configuration.getTargetHostname(), configuration.getTargetPortNumber());
+    }
+    
+    /**
+     * Create an AS2 server connection.
+     * 
+     * @param configuration - configuration used to configure connection.
+     * @return The AS2 server connection.
+     * @throws IOException 
+     */
+    public static AS2ServerConnection createAS2ServerConnection(AS2Configuration configuration) throws IOException {
+        synchronized (serverConnections) {
+            AS2ServerConnection serverConnection = serverConnections.get(configuration.getServerPortNumber());
+            if (serverConnection == null) {
+                serverConnection = new AS2ServerConnection(configuration.getAs2Version(), configuration.getServer(),
+                        configuration.getServerFqdn(), configuration.getServerPortNumber(),
+                        configuration.getSigningCertificateChain(), configuration.getSigningPrivateKey());
+                serverConnections.put(configuration.getServerPortNumber(), serverConnection);
+            }
+            return serverConnection;
+        }
+    }
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2Constants.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2Constants.java
new file mode 100644
index 0000000..6edda82
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2Constants.java
@@ -0,0 +1,29 @@
+/**
+ * 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.internal;
+
+/**
+ * Constants for AS2 component.
+ */
+public interface AS2Constants {
+
+    // suffix for parameters when passed as exchange header properties
+    String PROPERTY_PREFIX = "CamelAS2.";
+
+    // thread profile name for this component
+    String THREAD_PROFILE_NAME = "CamelAS2";
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2PropertiesHelper.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2PropertiesHelper.java
new file mode 100644
index 0000000..18cb741
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2PropertiesHelper.java
@@ -0,0 +1,39 @@
+/**
+ * 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.internal;
+
+import org.apache.camel.component.as2.AS2Configuration;
+import org.apache.camel.util.component.ApiMethodPropertiesHelper;
+
+/**
+ * Singleton {@link ApiMethodPropertiesHelper} for AS2 component.
+ */
+public final class AS2PropertiesHelper extends ApiMethodPropertiesHelper<AS2Configuration> {
+
+    private static AS2PropertiesHelper helper;
+
+    private AS2PropertiesHelper() {
+        super(AS2Configuration.class, AS2Constants.PROPERTY_PREFIX);
+    }
+
+    public static synchronized AS2PropertiesHelper getHelper() {
+        if (helper == null) {
+            helper = new AS2PropertiesHelper();
+        }
+        return helper;
+    }
+}
diff --git a/components/camel-as2/camel-as2-component/src/main/resources/META-INF/services/org/apache/camel/component/as2 b/components/camel-as2/camel-as2-component/src/main/resources/META-INF/services/org/apache/camel/component/as2
new file mode 100644
index 0000000..93dff32
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/main/resources/META-INF/services/org/apache/camel/component/as2
@@ -0,0 +1 @@
+class=org.apache.camel.component.as2.AS2Component
diff --git a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientManagerIntegrationTest.java b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientManagerIntegrationTest.java
new file mode 100644
index 0000000..a685145
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientManagerIntegrationTest.java
@@ -0,0 +1,210 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2Constants;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.AS2MessageStructure;
+import org.apache.camel.component.as2.api.AS2MimeType;
+import org.apache.camel.component.as2.api.AS2ServerConnection;
+import org.apache.camel.component.as2.api.AS2ServerManager;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIEntity;
+import org.apache.camel.component.as2.api.entity.DispositionNotificationMultipartReportEntity;
+import org.apache.camel.component.as2.api.entity.MimeEntity;
+import org.apache.camel.component.as2.api.util.HttpMessageUtils;
+import org.apache.camel.component.as2.internal.AS2ApiCollection;
+import org.apache.camel.component.as2.internal.AS2ClientManagerApiMethod;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test class for {@link org.apache.camel.component.as2.api.AS2ClientManager} APIs.
+ */
+public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AS2ClientManagerIntegrationTest.class);
+    private static final String PATH_PREFIX = AS2ApiCollection.getCollection().getApiName(AS2ClientManagerApiMethod.class).getName();
+
+    private static final String REQUEST_URI = "/";
+    private static final String SUBJECT = "Test Case";
+    private static final String AS2_NAME = "878051556";
+    private static final String FROM = "mrAS@example.org";
+    
+    private static final String MDN_FROM = "as2Test@server.example.com";
+    private static final String MDN_SUBJECT_PREFIX = "MDN Response:";
+    
+    private static final String EDI_MESSAGE = 
+            "UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'\n"
+            + "UNH+00000000000117+INVOIC:D:97B:UN'\n"
+            + "BGM+380+342459+9'\n"
+            + "DTM+3:20060515:102'\n"
+            + "RFF+ON:521052'\n"
+            + "NAD+BY+792820524::16++CUMMINS MID-RANGE ENGINE PLANT'\n"
+            + "NAD+SE+005435656::16++GENERAL WIDGET COMPANY'\n"
+            + "CUX+1:USD'\n"
+            + "LIN+1++157870:IN'\n"
+            + "IMD+F++:::WIDGET'\n"
+            + "QTY+47:1020:EA'\n"
+            + "ALI+US'\n"
+            + "MOA+203:1202.58'\n"
+            + "PRI+INV:1.179'\n"
+            + "LIN+2++157871:IN'\n"
+            + "IMD+F++:::DIFFERENT WIDGET'\n"
+            + "QTY+47:20:EA'\n"
+            + "ALI+JP'\n"
+            + "MOA+203:410'\n"
+            + "PRI+INV:20.5'\n"
+            + "UNS+S'\n"
+            + "MOA+39:2137.58'\n"
+            + "ALC+C+ABG'\n"
+            + "MOA+8:525'\n"
+            + "UNT+23+00000000000117'\n"
+            + "UNZ+1+00000000000778'\n";
+    
+    private static final String EXPECTED_AS2_VERSION = "1.1";
+    private static final String EXPECTED_MDN_SUBJECT = MDN_SUBJECT_PREFIX + SUBJECT;
+    
+    private static AS2ServerConnection serverConnection;
+
+    @Test
+    public void plainMessageSendTest() throws Exception {
+        final Map<String, Object> headers = new HashMap<String, Object>();
+        // parameter type is String
+        headers.put("CamelAS2.ediMessage", EDI_MESSAGE);
+        // parameter type is String
+        headers.put("CamelAS2.requestUri", REQUEST_URI);
+        // parameter type is String
+        headers.put("CamelAS2.subject", SUBJECT);
+        // parameter type is String
+        headers.put("CamelAS2.from", FROM);
+        // parameter type is String
+        headers.put("CamelAS2.as2From", AS2_NAME);
+        // parameter type is String
+        headers.put("CamelAS2.as2To", AS2_NAME);
+        // parameter type is org.apache.camel.component.as2.api.AS2MessageStructure
+        headers.put("CamelAS2.as2MessageStructure", AS2MessageStructure.PLAIN);
+        // parameter type is org.apache.http.entity.ContentType
+        headers.put("CamelAS2.ediMessageContentType", ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII));
+        // parameter type is String
+        headers.put("CamelAS2.ediMessageTransferEncoding", null);
+        // parameter type is String
+        headers.put("CamelAS2.signingAlgorithmName", null);
+        // parameter type is java.security.cert.Certificate[]
+        headers.put("CamelAS2.signingCertificateChain", null);
+        // parameter type is java.security.PrivateKey
+        headers.put("CamelAS2.signingPrivateKey", null);
+        // parameter type is String
+        headers.put("CamelAS2.dispositionNotificationTo", "mrAS2@example.com");
+        // parameter type is String[]
+        headers.put("CamelAS2.signedReceiptMicAlgorithms", null);
+
+        final org.apache.http.protocol.HttpCoreContext result = requestBodyAndHeaders("direct://SEND", null, headers);
+
+        assertNotNull("send result", result);
+        LOG.debug("send: " + result);
+        HttpRequest request = result.getRequest();
+        assertNotNull("Request", request);
+        assertTrue("Request does not contain body", request instanceof HttpEntityEnclosingRequest);
+        HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
+        assertNotNull("Request body", entity);
+        assertTrue("Request body does not contain EDI entity", entity instanceof ApplicationEDIEntity);
+        String ediMessage = ((ApplicationEDIEntity)entity).getEdiMessage();
+        assertEquals("EDI message is different", EDI_MESSAGE, ediMessage);
+        
+        HttpResponse response = result.getResponse();
+        assertNotNull("Response", response);
+        assertEquals("Unexpected response type", AS2MimeType.MULTIPART_REPORT, HttpMessageUtils.getHeaderValue(response, AS2Header.CONTENT_TYPE));
+        assertEquals("Unexpected mime version", AS2Constants.MIME_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.MIME_VERSION));
+        assertEquals("Unexpected AS2 version", EXPECTED_AS2_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_VERSION));
+        assertEquals("Unexpected MDN subject", EXPECTED_MDN_SUBJECT, HttpMessageUtils.getHeaderValue(response, AS2Header.SUBJECT));
+        assertEquals("Unexpected MDN from", MDN_FROM, HttpMessageUtils.getHeaderValue(response, AS2Header.FROM));
+        assertEquals("Unexpected AS2 from", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_FROM));
+        assertEquals("Unexpected AS2 to", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_TO));
+        assertNotNull("Missing message id", HttpMessageUtils.getHeaderValue(response, AS2Header.MESSAGE_ID));
+        
+        HttpEntity responseEntity = response.getEntity();
+        assertNotNull("Response entity", responseEntity);
+        assertTrue("Unexpected response entity type", responseEntity instanceof DispositionNotificationMultipartReportEntity);
+        DispositionNotificationMultipartReportEntity reportEntity = (DispositionNotificationMultipartReportEntity)responseEntity;
+        assertEquals("Unexpected number of body parts in report", 2, reportEntity.getPartCount());
+        MimeEntity firstPart = reportEntity.getPart(0);
+        assertEquals("Unexpected content type in first body part of report", ContentType.create(AS2MimeType.TEXT_PLAIN, AS2Charset.US_ASCII).toString(), firstPart.getContentTypeValue());
+        MimeEntity secondPart = reportEntity.getPart(1);
+        assertEquals("Unexpected content type in second body part of report",
+                ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, AS2Charset.US_ASCII).toString(),
+                secondPart.getContentTypeValue());
+    }
+
+    @BeforeClass
+    public static void setupTest() throws Exception {
+        receiveTestMessages();
+    }
+    
+    @AfterClass
+    public static void teardownTest() throws Exception {
+        if (serverConnection != null) {
+            serverConnection.stopListening("/");
+        }
+    }
+
+    public static class RequestHandler implements HttpRequestHandler {
+
+        @Override
+        public void handle(HttpRequest request, HttpResponse response, HttpContext context)
+                throws HttpException, IOException {
+            LOG.info("Received test message: " + request);
+            context.setAttribute(AS2ServerManager.FROM, MDN_FROM);
+            context.setAttribute(AS2ServerManager.SUBJECT, MDN_SUBJECT_PREFIX);
+        }
+
+    }
+    
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() {
+                // test route for send
+                from("direct://SEND").to("as2://" + PATH_PREFIX + "/send");
+
+            }
+        };
+    }
+   
+    private static void receiveTestMessages() throws IOException {
+        serverConnection = new AS2ServerConnection("1.1", "AS2ClientManagerIntegrationTest Server",
+                "server.example.com", 8888, null, null);
+        serverConnection.listen("/", new RequestHandler());
+    }
+}
diff --git a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerManagerIntegrationTest.java b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerManagerIntegrationTest.java
new file mode 100644
index 0000000..272adbe
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerManagerIntegrationTest.java
@@ -0,0 +1,310 @@
+/**
+ * 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;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.as2.api.AS2Charset;
+import org.apache.camel.component.as2.api.AS2ClientConnection;
+import org.apache.camel.component.as2.api.AS2ClientManager;
+import org.apache.camel.component.as2.api.AS2Header;
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.AS2MessageStructure;
+import org.apache.camel.component.as2.api.AS2SignedDataGenerator;
+import org.apache.camel.component.as2.api.entity.ApplicationEDIFACTEntity;
+import org.apache.camel.component.as2.api.entity.ApplicationPkcs7SignatureEntity;
+import org.apache.camel.component.as2.api.entity.MultipartSignedEntity;
+import org.apache.camel.component.as2.api.util.SigningUtils;
+import org.apache.camel.component.as2.internal.AS2ApiCollection;
+import org.apache.camel.component.as2.internal.AS2ServerManagerApiMethod;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
+import org.bouncycastle.asn1.smime.SMIMECapability;
+import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
+import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test class for {@link org.apache.camel.component.as2.api.AS2ServerManager} APIs.
+ */
+public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AS2ServerManagerIntegrationTest.class);
+    private static final String PATH_PREFIX = AS2ApiCollection.getCollection().getApiName(AS2ServerManagerApiMethod.class).getName();
+
+    private static final String METHOD = "POST";
+    private static final String TARGET_HOST = "localhost";
+    private static final int TARGET_PORT = 8888;
+    private static final String AS2_VERSION = "1.1";
+    private static final String USER_AGENT = "Camel AS2 Endpoint";
+    private static final String REQUEST_URI = "/";
+    private static final String AS2_NAME = "878051556";
+    private static final String SUBJECT = "Test Case";
+    private static final String FROM = "mrAS@example.org";
+    private static final String CLIENT_FQDN = "example.org";
+    private static final String DISPOSITION_NOTIFICATION_TO = "mrAS@example.org";
+    private static final String[] SIGNED_RECEIPT_MIC_ALGORITHMS = new String[] {"sha1", "md5"};
+
+    private static final String EDI_MESSAGE = "UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'\n"
+            + "UNH+00000000000117+INVOIC:D:97B:UN'\n"
+            + "BGM+380+342459+9'\n"
+            + "DTM+3:20060515:102'\n"
+            + "RFF+ON:521052'\n"
+            + "NAD+BY+792820524::16++CUMMINS MID-RANGE ENGINE PLANT'\n"
+            + "NAD+SE+005435656::16++GENERAL WIDGET COMPANY'\n"
+            + "CUX+1:USD'\n"
+            + "LIN+1++157870:IN'\n"
+            + "IMD+F++:::WIDGET'\n"
+            + "QTY+47:1020:EA'\n"
+            + "ALI+US'\n"
+            + "MOA+203:1202.58'\n"
+            + "PRI+INV:1.179'\n"
+            + "LIN+2++157871:IN'\n"
+            + "IMD+F++:::DIFFERENT WIDGET'\n"
+            + "QTY+47:20:EA'\n"
+            + "ALI+JP'\n"
+            + "MOA+203:410'\n"
+            + "PRI+INV:20.5'\n"
+            + "UNS+S'\n"
+            + "MOA+39:2137.58'\n"
+            + "ALC+C+ABG'\n"
+            + "MOA+8:525'\n"
+            + "UNT+23+00000000000117'\n"
+            + "UNZ+1+00000000000778'";
+    
+    private AS2SignedDataGenerator gen;
+    
+    private KeyPair issueKP;
+    private X509Certificate issueCert;
+
+    private KeyPair signingKP;
+    private X509Certificate signingCert;
+    private List<X509Certificate> certList;
+    
+    @Test
+    public void receivePlainEDIMessageTest() throws Exception {
+        AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);
+        AS2ClientManager clientManager = new AS2ClientManager(clientConnection);
+        
+        clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME, AS2MessageStructure.PLAIN,
+                ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), null, null, null,
+                DISPOSITION_NOTIFICATION_TO, SIGNED_RECEIPT_MIC_ALGORITHMS);
+
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:as2RcvMsgs");
+        mockEndpoint.expectedMinimumMessageCount(1);
+        mockEndpoint.setResultWaitTime(TimeUnit.MILLISECONDS.convert(30,  TimeUnit.SECONDS));
+        mockEndpoint.assertIsSatisfied();
+        
+        final List<Exchange> exchanges = mockEndpoint.getExchanges();
+        assertNotNull("listen result", exchanges);
+        assertFalse("listen result", exchanges.isEmpty());
+        LOG.debug("poll result: " + exchanges);
+        
+        Exchange exchange = exchanges.get(0);
+        Message message = exchange.getIn();
+        assertNotNull("exchange message", message);
+        BasicHttpContext context = message.getBody(BasicHttpContext.class);
+        assertNotNull("context", context);
+        HttpCoreContext coreContext = HttpCoreContext.adapt(context);
+        HttpRequest request = coreContext.getRequest();
+        assertNotNull("request", request);
+        assertEquals("Unexpected method value", METHOD, request.getRequestLine().getMethod());
+        assertEquals("Unexpected request URI value", REQUEST_URI, request.getRequestLine().getUri());
+        assertEquals("Unexpected HTTP version value", HttpVersion.HTTP_1_1, request.getRequestLine().getProtocolVersion());
+        assertEquals("Unexpected subject value", SUBJECT, request.getFirstHeader(AS2Header.SUBJECT).getValue());
+        assertEquals("Unexpected from value", FROM, request.getFirstHeader(AS2Header.FROM).getValue());
+        assertEquals("Unexpected AS2 version value", AS2_VERSION, request.getFirstHeader(AS2Header.AS2_VERSION).getValue());
+        assertEquals("Unexpected AS2 from value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_FROM).getValue());
+        assertEquals("Unexpected AS2 to value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_TO).getValue());
+        assertTrue("Unexpected message id value", request.getFirstHeader(AS2Header.MESSAGE_ID).getValue().endsWith(CLIENT_FQDN + ">"));
+        assertEquals("Unexpected target host value", TARGET_HOST + ":" + TARGET_PORT, request.getFirstHeader(AS2Header.TARGET_HOST).getValue());
+        assertEquals("Unexpected user agent value", USER_AGENT, request.getFirstHeader(AS2Header.USER_AGENT).getValue());
+        assertNotNull("Date value missing", request.getFirstHeader(AS2Header.DATE));
+        assertNotNull("Content length value missing", request.getFirstHeader(AS2Header.CONTENT_LENGTH));
+        assertTrue("Unexpected content type for message", request.getFirstHeader(AS2Header.CONTENT_TYPE).getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+        
+        
+        assertTrue("Request does not contain entity", request instanceof BasicHttpEntityEnclosingRequest);
+        HttpEntity entity = ((BasicHttpEntityEnclosingRequest)request).getEntity();
+        assertNotNull("Request does not contain entity", entity);
+        assertTrue("Unexpected request entity type", entity instanceof ApplicationEDIFACTEntity);
+        ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) entity;
+        assertTrue("Unexpected content type for entity", ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+        assertTrue("Entity not set as main body of request", ediEntity.isMainBody());
+        
+    }
+
+    @Test
+    public void receiveMultipartSignedMessageTest() throws Exception {
+        setupSigningGenerator();
+        
+        AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);
+        AS2ClientManager clientManager = new AS2ClientManager(clientConnection);
+        
+        clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME, AS2MessageStructure.SIGNED,
+                ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), null,
+                certList.toArray(new Certificate[0]), signingKP.getPrivate(), DISPOSITION_NOTIFICATION_TO,
+                SIGNED_RECEIPT_MIC_ALGORITHMS);
+        
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:as2RcvMsgs");
+        mockEndpoint.expectedMinimumMessageCount(1);
+        mockEndpoint.setResultWaitTime(TimeUnit.MILLISECONDS.convert(30,  TimeUnit.SECONDS));
+        mockEndpoint.assertIsSatisfied();
+        
+        final List<Exchange> exchanges = mockEndpoint.getExchanges();
+        assertNotNull("listen result", exchanges);
+        assertFalse("listen result", exchanges.isEmpty());
+        LOG.debug("poll result: " + exchanges);
+        
+        Exchange exchange = exchanges.get(0);
+        Message message = exchange.getIn();
+        assertNotNull("exchange message", message);
+        BasicHttpContext context = message.getBody(BasicHttpContext.class);
+        assertNotNull("context", context);
+        HttpCoreContext coreContext = HttpCoreContext.adapt(context);
+        HttpRequest request = coreContext.getRequest();
+        assertNotNull("request", request);
+        assertEquals("Unexpected method value", METHOD, request.getRequestLine().getMethod());
+        assertEquals("Unexpected request URI value", REQUEST_URI, request.getRequestLine().getUri());
+        assertEquals("Unexpected HTTP version value", HttpVersion.HTTP_1_1, request.getRequestLine().getProtocolVersion());
+        
+        assertEquals("Unexpected subject value", SUBJECT, request.getFirstHeader(AS2Header.SUBJECT).getValue());
+        assertEquals("Unexpected from value", FROM, request.getFirstHeader(AS2Header.FROM).getValue());
+        assertEquals("Unexpected AS2 version value", AS2_VERSION, request.getFirstHeader(AS2Header.AS2_VERSION).getValue());
+        assertEquals("Unexpected AS2 from value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_FROM).getValue());
+        assertEquals("Unexpected AS2 to value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_TO).getValue());
+        assertTrue("Unexpected message id value", request.getFirstHeader(AS2Header.MESSAGE_ID).getValue().endsWith(CLIENT_FQDN + ">"));
+        assertEquals("Unexpected target host value", TARGET_HOST + ":" + TARGET_PORT, request.getFirstHeader(AS2Header.TARGET_HOST).getValue());
+        assertEquals("Unexpected user agent value", USER_AGENT, request.getFirstHeader(AS2Header.USER_AGENT).getValue());
+        assertNotNull("Date value missing", request.getFirstHeader(AS2Header.DATE));
+        assertNotNull("Content length value missing", request.getFirstHeader(AS2Header.CONTENT_LENGTH));
+        assertTrue("Unexpected content type for message", request.getFirstHeader(AS2Header.CONTENT_TYPE).getValue().startsWith(AS2MediaType.MULTIPART_SIGNED));
+        
+        assertTrue("Request does not contain entity", request instanceof BasicHttpEntityEnclosingRequest);
+        HttpEntity entity = ((BasicHttpEntityEnclosingRequest)request).getEntity();
+        assertNotNull("Request does not contain entity", entity);
+        assertTrue("Unexpected request entity type", entity instanceof MultipartSignedEntity);
+        MultipartSignedEntity signedEntity = (MultipartSignedEntity)entity;
+        assertTrue("Entity not set as main body of request", signedEntity.isMainBody());
+        assertTrue("Request contains invalid number of mime parts", signedEntity.getPartCount() == 2);
+        
+        // Validated first mime part.
+        assertTrue("First mime part incorrect type ", signedEntity.getPart(0) instanceof ApplicationEDIFACTEntity);
+        ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) signedEntity.getPart(0);
+        assertTrue("Unexpected content type for first mime part", ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+        assertFalse("First mime type set as main body of request", ediEntity.isMainBody());
+        
+        // Validate second mime part.
+        assertTrue("Second mime part incorrect type ", signedEntity.getPart(1) instanceof ApplicationPkcs7SignatureEntity);
+        ApplicationPkcs7SignatureEntity signatureEntity = (ApplicationPkcs7SignatureEntity) signedEntity.getPart(1);
+        assertTrue("Unexpected content type for second mime part", signatureEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_PKCS7_SIGNATURE));
+        assertFalse("First mime type set as main body of request", signatureEntity.isMainBody());
+        
+        // Validate Signature
+        assertTrue("Signature is invalid", signedEntity.isValid());
+    }
+    
+    private void setupSigningGenerator() throws Exception {
+        Security.addProvider(new BouncyCastleProvider());
+        
+        setupKeysAndCertificates();
+        
+        // Create and populate certificate store.
+        JcaCertStore certs = new JcaCertStore(certList);
+
+        // Create capabilities vector
+        SMIMECapabilityVector capabilities = new SMIMECapabilityVector();
+        capabilities.addCapability(SMIMECapability.dES_EDE3_CBC);
+        capabilities.addCapability(SMIMECapability.rC2_CBC, 128);
+        capabilities.addCapability(SMIMECapability.dES_CBC);
+
+        // Create signing attributes
+        ASN1EncodableVector attributes = new ASN1EncodableVector();
+        attributes.add(new SMIMEEncryptionKeyPreferenceAttribute(new IssuerAndSerialNumber(new X500Name(signingCert.getIssuerDN().getName()), signingCert.getSerialNumber())));
+        attributes.add(new SMIMECapabilitiesAttribute(capabilities));
+        
+        gen = SigningUtils.createSigningGenerator(certList.toArray(new X509Certificate[0]), signingKP.getPrivate());
+        gen.addCertificates(certs);
+      
+    }
+
+    private void setupKeysAndCertificates() throws Exception {
+        //
+        // set up our certificates
+        //
+        KeyPairGenerator    kpg  = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpg.initialize(1024, new SecureRandom());
+
+        String issueDN = "O=Punkhorn Software, C=US";
+        issueKP = kpg.generateKeyPair();
+        issueCert = Utils.makeCertificate(
+                                        issueKP, issueDN, issueKP, issueDN);
+        
+        //
+        // certificate we sign against
+        //
+        String signingDN = "CN=William J. Collins, E=punkhornsw@gmail.com, O=Punkhorn Software, C=US";
+        signingKP = kpg.generateKeyPair();
+        signingCert = Utils.makeCertificate(
+                                        signingKP, signingDN, issueKP, issueDN);
+        
+        certList = new ArrayList<X509Certificate>();
+
+        certList.add(signingCert);
+        certList.add(issueCert);
+
+    }
+
+    
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() {
+                // test route for listen
+                from("as2://" + PATH_PREFIX + "/listen?requestUriPattern=/")
+                    .to("mock:as2RcvMsgs");
+
+            }
+        };
+    }
+}
diff --git a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AbstractAS2TestSupport.java b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AbstractAS2TestSupport.java
new file mode 100644
index 0000000..691bf0c
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AbstractAS2TestSupport.java
@@ -0,0 +1,82 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.util.IntrospectionSupport;
+
+/**
+ * Abstract base class for AS2 Integration tests generated by Camel API component maven plugin.
+ */
+public class AbstractAS2TestSupport extends CamelTestSupport {
+
+    private static final String TEST_OPTIONS_PROPERTIES = "/test-options.properties";
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+
+        final CamelContext context = super.createCamelContext();
+
+        // read AS2 component configuration from TEST_OPTIONS_PROPERTIES
+        final Properties properties = new Properties();
+        try {
+            properties.load(getClass().getResourceAsStream(TEST_OPTIONS_PROPERTIES));
+        } catch (Exception e) {
+            throw new IOException(String.format("%s could not be loaded: %s", TEST_OPTIONS_PROPERTIES, e.getMessage()),
+                e);
+        }
+
+        Map<String, Object> options = new HashMap<String, Object>();
+        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+            options.put(entry.getKey().toString(), entry.getValue());
+        }
+
+        final AS2Configuration configuration = new AS2Configuration();
+        IntrospectionSupport.setProperties(configuration, options);
+
+        // add AS2Component to Camel context
+        final AS2Component component = new AS2Component(context);
+        component.setConfiguration(configuration);
+        context.addComponent("as2", component);
+
+        return context;
+    }
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        // only create the context once for this class
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers)
+        throws CamelExecutionException {
+        return (T) template().requestBodyAndHeaders(endpointUri, body, headers);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T requestBody(String endpoint, Object body) throws CamelExecutionException {
+        return (T) template().requestBody(endpoint, body);
+    }
+}
diff --git a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/Utils.java b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/Utils.java
new file mode 100644
index 0000000..1dcc0ff
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/Utils.java
@@ -0,0 +1,83 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+public final class Utils {
+    //
+    // certificate serial number seed.
+    //
+    static int  serialNo = 1;
+    
+    private Utils() {
+    }
+
+    public static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey pub) throws IOException {
+        SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(pub.getEncoded());
+
+        BcX509ExtensionUtils utils = new BcX509ExtensionUtils();
+        return utils.createAuthorityKeyIdentifier(info);
+    }
+
+    public static SubjectKeyIdentifier createSubjectKeyId(PublicKey pub) throws IOException {
+        SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(pub.getEncoded());
+
+        return new BcX509ExtensionUtils().createSubjectKeyIdentifier(info);
+    }
+
+    /**
+     * create a basic X509 certificate from the given keys
+     */
+    public static X509Certificate makeCertificate(KeyPair subKP, String subDN, KeyPair issKP, String issDN)
+            throws GeneralSecurityException, IOException, OperatorCreationException {
+        PublicKey subPub = subKP.getPublic();
+        PrivateKey issPriv = issKP.getPrivate();
+        PublicKey issPub = issKP.getPublic();
+
+        X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN),
+                BigInteger.valueOf(serialNo++), new Date(System.currentTimeMillis()),
+                new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub);
+
+        v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyId(subPub));
+
+        v3CertGen.addExtension(Extension.authorityKeyIdentifier, false, createAuthorityKeyId(issPub));
+
+        return new JcaX509CertificateConverter().setProvider("BC").getCertificate(
+                v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv)));
+    }
+
+}
diff --git a/components/camel-as2/camel-as2-component/src/test/resources/log4j2.properties b/components/camel-as2/camel-as2-component/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..d9f0508
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/test/resources/log4j2.properties
@@ -0,0 +1,23 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.out.ref = out
diff --git a/components/camel-as2/camel-as2-component/src/test/resources/test-options.properties b/components/camel-as2/camel-as2-component/src/test/resources/test-options.properties
new file mode 100644
index 0000000..10b4e62
--- /dev/null
+++ b/components/camel-as2/camel-as2-component/src/test/resources/test-options.properties
@@ -0,0 +1,36 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+###############################################
+## AS2 Component Configuration Parameters
+###############################################
+
+## user agent identification string sent by endpoint
+## in User-Agent message header
+#userAgent=AS2 Client
+
+## host name of host messages sent to
+targetHostname=localhost
+
+## port number of host messages sent to
+targetPortNumber=8888
+
+## fully qualified domain name used in Message-Id message header.
+clientFqdn=example.com
+
+## port number listened on
+serverPortNumber=8888
\ No newline at end of file
diff --git a/components/camel-as2/pom.xml b/components/camel-as2/pom.xml
new file mode 100644
index 0000000..0cc73d8
--- /dev/null
+++ b/components/camel-as2/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>components</artifactId>
+    <groupId>org.apache.camel</groupId>
+    <version>2.22.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-as2-parent</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Camel :: AS2 :: Parent</name>
+  <description>Camel AS2 parent</description>
+
+  <modules>
+    <module>camel-as2-component</module>
+    <module>camel-as2-api</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/components/pom.xml b/components/pom.xml
index 9cb9add..d1857e7 100644
--- a/components/pom.xml
+++ b/components/pom.xml
... 20 lines suppressed ...

-- 
To stop receiving notification emails like this one, please contact
davsclaus@apache.org.