You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2018/12/17 07:54:07 UTC

[camel] branch master updated (67b3fa5 -> 0c03d4e)

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

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


    from 67b3fa5  CAMEL-11492: Use Antora for User manual and com...
     add 262a713  CAMEL-13009: DigestMethod and DigestValue elements created either in XAdES or DS namespace depending on XAdES version required
     new 0c03d4e  [CAMEL-12605] Refactored AS2 Server connection to accept decryption key.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../component/as2/api/AS2ServerConnection.java     |  18 ++--
 .../AS2MessageDispositionNotificationEntity.java   |   6 +-
 ...spositionNotificationMultipartReportEntity.java |   6 +-
 .../component/as2/api/entity/EntityParser.java     |   9 +-
 .../component/as2/api/protocol/ResponseMDN.java    |   6 +-
 .../camel/component/as2/api/util/MicUtils.java     |  10 +-
 .../camel/component/as2/api/AS2MessageTest.java    |  54 +++--------
 .../camel/component/as2/api/util/MicUtilsTest.java |   2 +-
 components/camel-as2/camel-as2-component/pom.xml   |  12 +--
 .../src/main/docs/as2-component.adoc               |   2 +-
 .../camel/component/as2/AS2Configuration.java      |  10 +-
 .../as2/internal/AS2ConnectionHelper.java          |   2 +-
 .../as2/AS2ClientManagerIntegrationTest.java       |  15 +--
 .../as2/AS2ServerManagerIntegrationTest.java       | 104 +++++++++++++++++++--
 .../xmlsecurity/api/XAdESSignatureProperties.java  |  18 +++-
 .../as2/springboot/AS2ComponentConfiguration.java  |  10 +-
 16 files changed, 182 insertions(+), 102 deletions(-)


[camel] 01/01: [CAMEL-12605] Refactored AS2 Server connection to accept decryption key.

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0c03d4e70f305c3900552d03290faacb4347f100
Author: William Collins <pu...@gmail.com>
AuthorDate: Fri Dec 14 15:55:06 2018 -0500

    [CAMEL-12605] Refactored AS2 Server connection to accept decryption key.
---
 .../component/as2/api/AS2ServerConnection.java     |  18 ++--
 .../AS2MessageDispositionNotificationEntity.java   |   6 +-
 ...spositionNotificationMultipartReportEntity.java |   6 +-
 .../component/as2/api/entity/EntityParser.java     |   9 +-
 .../component/as2/api/protocol/ResponseMDN.java    |   6 +-
 .../camel/component/as2/api/util/MicUtils.java     |  10 +-
 .../camel/component/as2/api/AS2MessageTest.java    |  54 +++--------
 .../camel/component/as2/api/util/MicUtilsTest.java |   2 +-
 components/camel-as2/camel-as2-component/pom.xml   |  12 +--
 .../src/main/docs/as2-component.adoc               |   2 +-
 .../camel/component/as2/AS2Configuration.java      |  10 +-
 .../as2/internal/AS2ConnectionHelper.java          |   2 +-
 .../as2/AS2ClientManagerIntegrationTest.java       |  15 +--
 .../as2/AS2ServerManagerIntegrationTest.java       | 104 +++++++++++++++++++--
 .../as2/springboot/AS2ComponentConfiguration.java  |  10 +-
 15 files changed, 168 insertions(+), 98 deletions(-)

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
index 12c1132..09b1061 100644
--- 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
@@ -66,14 +66,15 @@ public class AS2ServerConnection {
                                      int port,
                                      AS2SignatureAlgorithm signatureAlgorithm,
                                      Certificate[] signingCertificateChain,
-                                     PrivateKey signingPrivateKey)
+                                     PrivateKey signingPrivateKey,
+                                     PrivateKey decryptingPrivateKey)
                 throws IOException {
             setName(REQUEST_LISTENER_THREAD_NAME_PREFIX + port);
             serversocket = new ServerSocket(port);
 
             // Set up HTTP protocol processor for incoming connections
             final HttpProcessor inhttpproc = initProtocolProcessor(as2Version, originServer, serverFqdn, port,
-                    signatureAlgorithm, signingCertificateChain, signingPrivateKey);
+                    signatureAlgorithm, signingCertificateChain, signingPrivateKey, decryptingPrivateKey);
 
             reqistry = new UriHttpRequestHandlerMapper();
 
@@ -163,6 +164,7 @@ public class AS2ServerConnection {
             } catch (final IOException ex) {
                 LOG.error("I/O error: {}", ex.getMessage());
             } catch (final HttpException ex) {
+                ex.printStackTrace();
                 LOG.error("Unrecoverable HTTP protocol violation: {}", ex.getMessage());
             } finally {
                 try {
@@ -182,6 +184,7 @@ public class AS2ServerConnection {
     private AS2SignatureAlgorithm signingAlgorithm;
     private Certificate[] signingCertificateChain;
     private PrivateKey signingPrivateKey;
+    private PrivateKey decryptingPrivateKey;
 
     public AS2ServerConnection(String as2Version,
                                String originServer,
@@ -189,7 +192,8 @@ public class AS2ServerConnection {
                                Integer serverPortNumber,
                                AS2SignatureAlgorithm signingAlgorithm,
                                Certificate[] signingCertificateChain,
-                               PrivateKey signingPrivateKey)
+                               PrivateKey signingPrivateKey,
+                               PrivateKey decryptingPrivateKey)
             throws IOException {
         this.as2Version = Args.notNull(as2Version, "as2Version");
         this.originServer = Args.notNull(originServer, "userAgent");
@@ -198,9 +202,10 @@ public class AS2ServerConnection {
         this.signingAlgorithm = signingAlgorithm;
         this.signingCertificateChain = signingCertificateChain;
         this.signingPrivateKey = signingPrivateKey;
+        this.decryptingPrivateKey = decryptingPrivateKey;
 
         listenerThread = new RequestListenerThread(this.as2Version, this.originServer, this.serverFqdn,
-                this.serverPortNumber, this.signingAlgorithm, this.signingCertificateChain, this.signingPrivateKey);
+                this.serverPortNumber, this.signingAlgorithm, this.signingCertificateChain, this.signingPrivateKey, this.decryptingPrivateKey);
         listenerThread.setDaemon(true);
         listenerThread.start();
     }
@@ -240,10 +245,11 @@ public class AS2ServerConnection {
                                                   int port,
                                                   AS2SignatureAlgorithm signatureAlgorithm,
                                                   Certificate[] signingCertificateChain,
-                                                  PrivateKey signingPrivateKey) {
+                                                  PrivateKey signingPrivateKey,
+                                                  PrivateKey decryptingPrivateKey) {
         return HttpProcessorBuilder.create().add(new ResponseContent(true)).add(new ResponseServer(originServer))
                 .add(new ResponseDate()).add(new ResponseConnControl()).add(new ResponseMDN(as2Version, serverFqdn,
-                        signatureAlgorithm, signingCertificateChain, signingPrivateKey))
+                        signatureAlgorithm, signingCertificateChain, signingPrivateKey, decryptingPrivateKey))
                 .build();
     }
 
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
index 71e323c..f6eb364 100644
--- 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
@@ -18,6 +18,7 @@ package org.apache.camel.component.as2.api.entity;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.security.PrivateKey;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -76,7 +77,8 @@ public class AS2MessageDispositionNotificationEntity extends MimeEntity {
                                                    String[] warningFields,
                                                    Map<String, String> extensionFields,
                                                    String charset,
-                                                   boolean isMainBody) throws HttpException {
+                                                   boolean isMainBody,
+                                                   PrivateKey decryptingPrivateKey) throws HttpException {
         setMainBody(isMainBody);
         setContentType(ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, charset));
 
@@ -87,7 +89,7 @@ public class AS2MessageDispositionNotificationEntity extends MimeEntity {
 
         this.originalMessageId  = HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID);
 
-        this.receivedContentMic = MicUtils.createReceivedContentMic(request);
+        this.receivedContentMic = MicUtils.createReceivedContentMic(request, decryptingPrivateKey);
 
         this.reportingUA = HttpMessageUtils.getHeaderValue(response, AS2Header.SERVER);
 
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
index 4706ae6..fd68a3e 100644
--- 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
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.component.as2.api.entity;
 
+import java.security.PrivateKey;
 import java.util.Map;
 
 import org.apache.camel.component.as2.api.AS2Charset;
@@ -49,7 +50,8 @@ public class DispositionNotificationMultipartReportEntity extends MultipartRepor
                                                         Map<String, String> extensionFields,
                                                         String charset,
                                                         String boundary,
-                                                        boolean isMainBody)
+                                                        boolean isMainBody,
+                                                        PrivateKey decryptingPrivateKey)
             throws HttpException {
         super(charset, isMainBody, boundary);
         removeHeaders(AS2Header.CONTENT_TYPE);
@@ -58,7 +60,7 @@ public class DispositionNotificationMultipartReportEntity extends MultipartRepor
         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));
+                dispositionModifier, failureFields, errorFields, warningFields, extensionFields, charset, false, decryptingPrivateKey));
     }
 
     public String getMainMessageContentType() {
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
index eb45639..9652f5e9 100644
--- 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
@@ -422,6 +422,11 @@ public final class EntityParser {
     public static void parseAS2MessageEntity(HttpMessage message) throws HttpException {
         if (EntityUtils.hasEntity(message)) {
             HttpEntity entity = Args.notNull(EntityUtils.getMessageEntity(message), "message entity");
+            
+            if (entity instanceof MimeEntity) {
+                // already parsed
+                return;
+            }
 
             try {
                 // Determine Content Type of Message
@@ -1138,8 +1143,8 @@ public final class EntityParser {
             break;
         }
         case AS2MimeType.APPLICATION_PKCS7_MIME: {
-            if (contentType.getParameter("mime-type").equals("compressed-data")) {
-                throw new HttpException("Failed to extract EDI payload: invalid mime type '" + contentType.getParameter("mime-type") + "' for AS2 enveloped entity");
+            if (!"compressed-data".equals(contentType.getParameter("smime-type"))) {
+                throw new HttpException("Failed to extract EDI payload: invalid mime type '" + contentType.getParameter("smime-type") + "' for AS2 enveloped entity");
             }
             ApplicationPkcs7MimeCompressedDataEntity compressedDataEntity = (ApplicationPkcs7MimeCompressedDataEntity) entity;
             ediEntity = extractEdiPayloadFromCompressedEntity(compressedDataEntity);
diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
index 14a4109..eacb22c 100644
--- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
+++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/protocol/ResponseMDN.java
@@ -59,13 +59,15 @@ public class ResponseMDN implements HttpResponseInterceptor {
     private AS2SignatureAlgorithm signingAlgorithm;
     private Certificate[] signingCertificateChain;
     private PrivateKey signingPrivateKey;
+    private PrivateKey decryptingPrivateKey;
 
-    public ResponseMDN(String as2Version, String serverFQDN, AS2SignatureAlgorithm signingAlgorithm, Certificate[] signingCertificateChain, PrivateKey signingPrivateKey) {
+    public ResponseMDN(String as2Version, String serverFQDN, AS2SignatureAlgorithm signingAlgorithm, Certificate[] signingCertificateChain, PrivateKey signingPrivateKey, PrivateKey decryptingPrivateKey) {
         this.as2Version = as2Version;
         this.serverFQDN = serverFQDN;
         this.signingAlgorithm = signingAlgorithm;
         this.signingCertificateChain = signingCertificateChain;
         this.signingPrivateKey = signingPrivateKey;
+        this.decryptingPrivateKey = decryptingPrivateKey;
     }
 
     @Override
@@ -100,7 +102,7 @@ public class ResponseMDN implements HttpResponseInterceptor {
         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);
+                AS2DispositionType.PROCESSED, null, null, null, null, null, AS2Charset.US_ASCII, boundary, true, decryptingPrivateKey);
 
         DispositionNotificationOptions dispositionNotificationOptions = DispositionNotificationOptionsParser
                 .parseDispositionNotificationOptions(
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
index 8eb66b1..c3da303 100644
--- 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
@@ -19,21 +19,17 @@ package org.apache.camel.component.as2.api.util;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
 
 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.ApplicationPkcs7MimeCompressedDataEntity;
 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;
 
@@ -83,7 +79,7 @@ public final class MicUtils {
         }
     }
 
-    public static ReceivedContentMic createReceivedContentMic(HttpEntityEnclosingRequest request) throws HttpException {
+    public static ReceivedContentMic createReceivedContentMic(HttpEntityEnclosingRequest request, PrivateKey decryptingPrivateKey) throws HttpException {
 
         String dispositionNotificationOptionsString =  HttpMessageUtils.getHeaderValue(request, AS2Header.DISPOSITION_NOTIFICATION_OPTIONS);
         if (dispositionNotificationOptionsString == null) {
@@ -97,7 +93,7 @@ public final class MicUtils {
             return null;
         }
 
-        HttpEntity entity = EntityParser.extractEdiPayload(request, null);
+        HttpEntity entity = EntityParser.extractEdiPayload(request, decryptingPrivateKey);
 
         byte[] content = EntityUtils.getContent(entity);
 
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
index b9d336a..b8369f5 100644
--- 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
@@ -140,14 +140,17 @@ public class AS2MessageTest {
 
     private AS2SignedDataGenerator gen;
 
-    private KeyPair issueKP;
-    private X509Certificate issueCert;
+    private static KeyPair issueKP;
+    private static X509Certificate issueCert;
 
-    private KeyPair signingKP;
-    private X509Certificate signingCert;
-    private List<X509Certificate> certList;
+    private static KeyPair signingKP;
+    private static X509Certificate signingCert;
+    private static List<X509Certificate> certList;
+    
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        Security.addProvider(new BouncyCastleProvider());
 
-    private void setupKeysAndCertificates() throws Exception {
         //
         // set up our certificates
         //
@@ -170,38 +173,11 @@ public class AS2MessageTest {
 
         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<>();
-
-        certList.add(signingCert);
-        certList.add(issueCert);
+        
+        KeyPair decryptingKP = signingKP;
 
         testServer = new AS2ServerConnection(AS2_VERSION, "MyServer-HTTP/1.1", SERVER_FQDN, TARGET_PORT, AS2SignatureAlgorithm.SHA256WITHRSA,
-                certList.toArray(new Certificate[0]), signingKP.getPrivate());
+                certList.toArray(new Certificate[0]), signingKP.getPrivate(), decryptingKP.getPrivate());
         testServer.listen("*", new HttpRequestHandler() {
             @Override
             public void handle(HttpRequest request, HttpResponse response, HttpContext context)
@@ -227,8 +203,6 @@ public class AS2MessageTest {
     public void setUp() throws Exception {
         Security.addProvider(new BouncyCastleProvider());
 
-        setupKeysAndCertificates();
-
         // Create and populate certificate store.
         JcaCertStore certs = new JcaCertStore(certList);
 
@@ -703,7 +677,7 @@ public class AS2MessageTest {
         DispositionNotificationMultipartReportEntity mdn = new DispositionNotificationMultipartReportEntity(request,
                 response, DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, AS2DispositionType.PROCESSED,
                 dispositionModifier, failureFields, errorFields, warningFields, extensionFields, null, "boundary",
-                true);
+                true, null);
 
         // Send MDN
         HttpCoreContext httpContext = mdnManager.send(mdn, RECIPIENT_DELIVERY_ADDRESS);
@@ -730,7 +704,7 @@ public class AS2MessageTest {
         assertArrayEquals("Unexpected value for Error Fields", errorFields, mdnEntity.getErrorFields());
         assertArrayEquals("Unexpected value for Warning Fields", warningFields, mdnEntity.getWarningFields());
         assertEquals("Unexpected value for Extension Fields", extensionFields, mdnEntity.getExtensionFields());
-        ReceivedContentMic expectedMic = MicUtils.createReceivedContentMic(request);
+        ReceivedContentMic expectedMic = MicUtils.createReceivedContentMic(request, null);
         ReceivedContentMic mdnMic = mdnEntity.getReceivedContentMic();
         assertEquals("Unexpected value for Recieved Content Mic", expectedMic.getEncodedMessageDigest(),
                 mdnMic.getEncodedMessageDigest());
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
index 5cbaba7..e14c511 100644
--- 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
@@ -99,7 +99,7 @@ public class MicUtilsTest {
         basicEntity.setContentType(CONTENT_TYPE_VALUE);
         request.setEntity(basicEntity);
 
-        ReceivedContentMic receivedContentMic = MicUtils.createReceivedContentMic(request);
+        ReceivedContentMic receivedContentMic = MicUtils.createReceivedContentMic(request, null);
         assertNotNull("Failed to create Received Content MIC");
         LOG.debug("Digest Algorithm: " + receivedContentMic.getDigestAlgorithmId());
         assertEquals("Unexpected digest algorithm value", EXPECTED_MESSAGE_DIGEST_ALGORITHM, receivedContentMic.getDigestAlgorithmId());
diff --git a/components/camel-as2/camel-as2-component/pom.xml b/components/camel-as2/camel-as2-component/pom.xml
index d6ca3a7..2470df8 100644
--- a/components/camel-as2/camel-as2-component/pom.xml
+++ b/components/camel-as2/camel-as2-component/pom.xml
@@ -155,11 +155,7 @@
                     <apiName>client</apiName>
                     <proxyClass>org.apache.camel.component.as2.api.AS2ClientManager</proxyClass>
                     <fromJavadoc>
-                      <excludeMethods>createSigningGenerator</excludeMethods>
-                      <excludeMethods>createEncryptingGenerator</excludeMethods>
-                      <excludeMethods>createCompressorGenerator</excludeMethods>
-                      <excludeMethods>createEncryptor</excludeMethods>
-                      <excludeMethods>createCompressor</excludeMethods>
+                      <excludeMethods>createSigningGenerator|createEncryptingGenerator|createCompressorGenerator|createEncryptor|createCompressor</excludeMethods>
                     </fromJavadoc>
                     <nullableOptions>
                         <nullableOption>ediMessageTransferEncoding</nullableOption>
@@ -171,7 +167,6 @@
                         <nullableOption>signedReceiptMicAlgorithms</nullableOption>
                         <nullableOption>encryptingAlgorithm</nullableOption>
                         <nullableOption>encryptingCertificateChain</nullableOption>
-                        <nullableOption>encryptingPrivateKey</nullableOption>
                     </nullableOptions>
                 </api>
                 <api>
@@ -181,7 +176,10 @@
                       <excludeMethods>stopListening|handleMDNResponse</excludeMethods>
 	                </fromJavadoc>
                     <excludeConfigNames>handler</excludeConfigNames>
-                </api>
+<!--                     <nullableOptions>
+                        <nullableOption>decryptingPrivateKey</nullableOption>
+                    </nullableOptions>
+ -->                </api>
               </apis>
               <!-- Specify global values for all APIs here, these are overridden at API level
               <substitutions/>
diff --git a/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc b/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
index 0c1180c..7ff1595 100644
--- a/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
+++ b/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
@@ -82,12 +82,12 @@ with the following path and query parameters:
 | *as2Version* (common) | The version of the AS2 protocol. | 1.1 | String
 | *clientFqdn* (common) | The Client Fully Qualified Domain Name (FQDN). Used in message ids sent by endpoint. | camel.apache.org | String
 | *compressionAlgorithm* (common) | The algorithm used to compress EDI message. |  | AS2Compression Algorithm
+| *decryptingPrivateKey* (common) | The key used to encrypt the EDI message. |  | PrivateKey
 | *dispositionNotificationTo* (common) | The value of the Disposition-Notification-To header. Assigning a value to this parameter requests a message disposition notification (MDN) for the AS2 message. |  | String
 | *ediMessageTransferEncoding* (common) | The transfer encoding of EDI message. |  | String
 | *ediMessageType* (common) | The content type of EDI message. One of application/edifact, application/edi-x12, application/edi-consent |  | ContentType
 | *encryptingAlgorithm* (common) | The algorithm used to encrypt EDI message. |  | AS2EncryptionAlgorithm
 | *encryptingCertificateChain* (common) | The chain of certificates used to encrypt EDI message. |  | Certificate[]
-| *encryptingPrivateKey* (common) | The key used to encrypt the EDI message. |  | PrivateKey
 | *from* (common) | The value of the From header of AS2 message. |  | String
 | *inBody* (common) | Sets the name of a parameter to be passed in the exchange In Body |  | String
 | *methodName* (common) | *Required* What sub operation to use for the selected operation |  | String
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
index c92b50e..57ac5ba 100644
--- 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
@@ -118,7 +118,7 @@ public class AS2Configuration {
     private Certificate[] encryptingCertificateChain;
 
     @UriParam
-    private PrivateKey encryptingPrivateKey;
+    private PrivateKey decryptingPrivateKey;
 
     public AS2ApiName getApiName() {
         return apiName;
@@ -452,14 +452,14 @@ public class AS2Configuration {
         this.encryptingCertificateChain = signingCertificateChain;
     }
 
-    public PrivateKey getEncryptingPrivateKey() {
-        return encryptingPrivateKey;
+    public PrivateKey getDecryptingPrivateKey() {
+        return decryptingPrivateKey;
     }
 
     /**
      * The key used to encrypt the EDI message.
      */
-    public void setEncryptingPrivateKey(PrivateKey signingPrivateKey) {
-        this.encryptingPrivateKey = signingPrivateKey;
+    public void setDecryptingPrivateKey(PrivateKey signingPrivateKey) {
+        this.decryptingPrivateKey = signingPrivateKey;
     }
 }
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
index bc1fff5..908eef7 100644
--- 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
@@ -64,7 +64,7 @@ public final class AS2ConnectionHelper {
             if (serverConnection == null) {
                 serverConnection = new AS2ServerConnection(configuration.getAs2Version(), configuration.getServer(),
                         configuration.getServerFqdn(), configuration.getServerPortNumber(), configuration.getSigningAlgorithm(),
-                        configuration.getSigningCertificateChain(), configuration.getSigningPrivateKey());
+                        configuration.getSigningCertificateChain(), configuration.getSigningPrivateKey(), configuration.getDecryptingPrivateKey());
                 serverConnections.put(configuration.getServerPortNumber(), serverConnection);
             }
             return serverConnection;
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
index 9f83ea2..a52747f 100644
--- 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
@@ -161,6 +161,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
     private X509Certificate issueCert;
 
     private KeyPair signingKP;
+    private KeyPair decryptingKP;
     private X509Certificate signingCert;
     private List<X509Certificate> certList;
     private AS2SignedDataGenerator gen;
@@ -299,8 +300,6 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
         headers.put("CamelAS2.encryptingAlgorithm", AS2EncryptionAlgorithm.AES128_CBC);
         // parameter type is java.security.cert.Certificate[]
         headers.put("CamelAS2.encryptingCertificateChain", certList);
-        // parameter type is java.security.PrivateKey
-        headers.put("CamelAS2.encryptingPrivateKey", signingKP.getPrivate());
 
         final org.apache.http.protocol.HttpCoreContext result = requestBodyAndHeaders("direct://SEND", EDI_MESSAGE, headers);
 
@@ -312,7 +311,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
         HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
         assertNotNull("Request body", entity);
         assertTrue("Request body does not contain ApplicationPkcs7Mime entity", entity instanceof ApplicationPkcs7MimeEnvelopedDataEntity);
-        MimeEntity envelopeEntity = ((ApplicationPkcs7MimeEnvelopedDataEntity)entity).getEncryptedEntity(signingKP.getPrivate());
+        MimeEntity envelopeEntity = ((ApplicationPkcs7MimeEnvelopedDataEntity)entity).getEncryptedEntity(decryptingKP.getPrivate());
         assertTrue("Enveloped entity is not an EDI entity", envelopeEntity instanceof ApplicationEDIEntity);
         String ediMessage = ((ApplicationEDIEntity)envelopeEntity).getEdiMessage();
         assertEquals("EDI message is different", EDI_MESSAGE.replaceAll("[\n\r]", ""), ediMessage.replaceAll("[\n\r]", ""));
@@ -437,7 +436,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
         assertEquals("Unexpected value for disposition type", AS2DispositionType.PROCESSED, messageDispositionNotificationEntity.getDispositionType());
         
         ReceivedContentMic receivedContentMic = messageDispositionNotificationEntity.getReceivedContentMic();
-        ReceivedContentMic computedContentMic = MicUtils.createReceivedContentMic((HttpEntityEnclosingRequest)request);
+        ReceivedContentMic computedContentMic = MicUtils.createReceivedContentMic((HttpEntityEnclosingRequest)request, decryptingKP.getPrivate());
         assertEquals("Received content MIC does not match computed", computedContentMic.getEncodedMessageDigest(), receivedContentMic.getEncodedMessageDigest());
     }
 
@@ -524,7 +523,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
         assertEquals("Unexpected value for disposition type", AS2DispositionType.PROCESSED, messageDispositionNotificationEntity.getDispositionType());
         
         ReceivedContentMic receivedContentMic = messageDispositionNotificationEntity.getReceivedContentMic();
-        ReceivedContentMic computedContentMic = MicUtils.createReceivedContentMic((HttpEntityEnclosingRequest)request);
+        ReceivedContentMic computedContentMic = MicUtils.createReceivedContentMic((HttpEntityEnclosingRequest)request, decryptingKP.getPrivate());
         assertEquals("Received content MIC does not match computed", computedContentMic.getEncodedMessageDigest(), receivedContentMic.getEncodedMessageDigest());
     }
 
@@ -564,7 +563,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
         DispositionNotificationMultipartReportEntity mdn = new DispositionNotificationMultipartReportEntity(request,
                 response, DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, AS2DispositionType.PROCESSED,
                 dispositionModifier, failureFields, errorFields, warningFields, extensionFields, null, "boundary",
-                true);
+                true, serverSigningKP.getPrivate());
 
         // Send MDN
         @SuppressWarnings("unused")
@@ -648,7 +647,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
 
     private static void receiveTestMessages() throws IOException {
         serverConnection = new AS2ServerConnection(AS2_VERSION, ORIGIN_SERVER_NAME,
-                SERVER_FQDN, PARTNER_TARGET_PORT, AS2SignatureAlgorithm.SHA256WITHRSA, serverCertList.toArray(new Certificate[0]), serverSigningKP.getPrivate());
+                SERVER_FQDN, PARTNER_TARGET_PORT, AS2SignatureAlgorithm.SHA256WITHRSA, serverCertList.toArray(new Certificate[0]), serverSigningKP.getPrivate(), serverSigningKP.getPrivate());
         serverConnection.listen("/", new RequestHandler());
     }
 
@@ -678,5 +677,7 @@ public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
         certList.add(signingCert);
         certList.add(issueCert);
 
+        // keys used to encrypt/decrypt
+        decryptingKP = signingKP;
     }
 }
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
index cb58af7..34b598b 100644
--- 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
@@ -26,19 +26,24 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.camel.CamelContext;
 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.AS2EncryptionAlgorithm;
 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.AS2SignatureAlgorithm;
 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.ApplicationPkcs7MimeEnvelopedDataEntity;
 import org.apache.camel.component.as2.api.entity.ApplicationPkcs7SignatureEntity;
+import org.apache.camel.component.as2.api.entity.MimeEntity;
 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;
@@ -60,6 +65,7 @@ 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.BeforeClass;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -112,14 +118,21 @@ public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
             + "UNT+23+00000000000117'\n"
             + "UNZ+1+00000000000778'";
 
-    private AS2SignedDataGenerator gen;
+    private static AS2SignedDataGenerator gen;
 
-    private KeyPair issueKP;
-    private X509Certificate issueCert;
+    private static KeyPair issueKP;
+    private static X509Certificate issueCert;
 
-    private KeyPair signingKP;
-    private X509Certificate signingCert;
-    private List<X509Certificate> certList;
+    private static KeyPair signingKP;
+    private static X509Certificate signingCert;
+    private static List<X509Certificate> certList;
+    
+    private static KeyPair decryptingKP;
+    
+    @BeforeClass
+    public static void  setup() throws Exception {
+        setupSigningGenerator();
+    }
 
     @Test
     public void receivePlainEDIMessageTest() throws Exception {
@@ -178,7 +191,6 @@ public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
 
     @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);
@@ -245,8 +257,70 @@ public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
         // Validate Signature
         assertTrue("Signature is invalid", signedEntity.isValid());
     }
+    
+    @Test
+    public void receiveEnvelopedMessageTest() 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.ENCRYPTED,
+                ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), null, null, null, null,
+                null, DISPOSITION_NOTIFICATION_TO, SIGNED_RECEIPT_MIC_ALGORITHMS, AS2EncryptionAlgorithm.AES128_CBC, certList.toArray(new Certificate[0]));
 
-    private void setupSigningGenerator() throws Exception {
+        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(AS2MimeType.APPLICATION_PKCS7_MIME));
+
+
+        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 ApplicationPkcs7MimeEnvelopedDataEntity);
+        ApplicationPkcs7MimeEnvelopedDataEntity envelopedEntity = (ApplicationPkcs7MimeEnvelopedDataEntity) entity;
+        assertTrue("Entity not set as main body of request", envelopedEntity.isMainBody());
+
+        // Validated enveloped part.
+        MimeEntity encryptedEntity = envelopedEntity.getEncryptedEntity(signingKP.getPrivate());
+        assertTrue("Enveloped mime part incorrect type ", encryptedEntity instanceof ApplicationEDIFACTEntity);
+        ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) encryptedEntity;
+        assertTrue("Unexpected content type for enveloped mime part",
+                ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
+        assertFalse("Enveloped mime type set as main body of request", ediEntity.isMainBody());
+        assertEquals("Unexpected content for enveloped mime part", EDI_MESSAGE.replaceAll("[\n\r]", ""),
+                ediEntity.getEdiMessage().replaceAll("[\n\r]", ""));
+
+    }
+
+    private static void setupSigningGenerator() throws Exception {
         Security.addProvider(new BouncyCastleProvider());
 
         setupKeysAndCertificates();
@@ -270,7 +344,7 @@ public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
 
     }
 
-    private void setupKeysAndCertificates() throws Exception {
+    private static void setupKeysAndCertificates() throws Exception {
         //
         // set up our certificates
         //
@@ -295,9 +369,19 @@ public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
 
         certList.add(signingCert);
         certList.add(issueCert);
+        
+        decryptingKP = signingKP;
 
     }
-
+    
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        AS2Component as2Component = (AS2Component) context.getComponent("as2");
+        AS2Configuration configuration = as2Component.getConfiguration();
+        configuration.setDecryptingPrivateKey(decryptingKP.getPrivate());
+        return context;
+    }
 
     @Override
     protected RouteBuilder createRouteBuilder() throws Exception {
diff --git a/platforms/spring-boot/components-starter/camel-as2-starter/src/main/java/org/apache/camel/component/as2/springboot/AS2ComponentConfiguration.java b/platforms/spring-boot/components-starter/camel-as2-starter/src/main/java/org/apache/camel/component/as2/springboot/AS2ComponentConfiguration.java
index eda444f..6b66ad7 100644
--- a/platforms/spring-boot/components-starter/camel-as2-starter/src/main/java/org/apache/camel/component/as2/springboot/AS2ComponentConfiguration.java
+++ b/platforms/spring-boot/components-starter/camel-as2-starter/src/main/java/org/apache/camel/component/as2/springboot/AS2ComponentConfiguration.java
@@ -194,7 +194,7 @@ public class AS2ComponentConfiguration
         /**
          * The key used to encrypt the EDI message.
          */
-        private PrivateKey encryptingPrivateKey;
+        private PrivateKey decryptingPrivateKey;
 
         public AS2ApiName getApiName() {
             return apiName;
@@ -412,12 +412,12 @@ public class AS2ComponentConfiguration
             this.encryptingCertificateChain = encryptingCertificateChain;
         }
 
-        public PrivateKey getEncryptingPrivateKey() {
-            return encryptingPrivateKey;
+        public PrivateKey getDecryptingPrivateKey() {
+            return decryptingPrivateKey;
         }
 
-        public void setEncryptingPrivateKey(PrivateKey encryptingPrivateKey) {
-            this.encryptingPrivateKey = encryptingPrivateKey;
+        public void setDecryptingPrivateKey(PrivateKey decryptingPrivateKey) {
+            this.decryptingPrivateKey = decryptingPrivateKey;
         }
     }
 }
\ No newline at end of file