This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch sandbox/camel-3.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9ca79898a006f9f8b24f1ba5bf7d7c5f7a10b5b7 Author: William Collins <punkhor...@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=punkhor...@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 86c2c5e..37af503 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