[ https://issues.apache.org/jira/browse/CAMEL-12605?focusedWorklogId=175939&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-175939 ]
ASF GitHub Bot logged work on CAMEL-12605: ------------------------------------------ Author: ASF GitHub Bot Created on: 17/Dec/18 09:19 Start Date: 17/Dec/18 09:19 Worklog Time Spent: 10m Work Description: oscerd closed pull request #2675: [CAMEL-12605] Refactored AS2 Server connection to accept decryption key. URL: https://github.com/apache/camel/pull/2675 This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): 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 12c11322a61..09b10611151 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 RequestListenerThread(String as2Version, 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 void run() { } 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 void run() { private AS2SignatureAlgorithm signingAlgorithm; private Certificate[] signingCertificateChain; private PrivateKey signingPrivateKey; + private PrivateKey decryptingPrivateKey; public AS2ServerConnection(String as2Version, String originServer, @@ -189,7 +192,8 @@ public AS2ServerConnection(String as2Version, 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 AS2ServerConnection(String as2Version, 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 @@ protected HttpProcessor initProtocolProcessor(String as2Version, 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 71e323c9856..f6eb364b509 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 @@ 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 AS2MessageDispositionNotificationEntity(HttpEntityEnclosingRequest reques 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 AS2MessageDispositionNotificationEntity(HttpEntityEnclosingRequest reques 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 4706ae61242..fd68a3e01fc 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 DispositionNotificationMultipartReportEntity(HttpEntityEnclosingRequest r 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 DispositionNotificationMultipartReportEntity(HttpEntityEnclosingRequest r 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 eb4563919dc..9652f5e9a99 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 @@ private static void parseMessageDispositionNotificationReportEntity(HttpMessage 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 @@ private static MimeEntity extractEdiPayloadFromEnvelopedEntity(ApplicationPkcs7M 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 14a4109ce77..eacb22c2ae6 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 @@ 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 void process(HttpResponse response, HttpContext context) throws HttpExcep 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 8eb66b13d86..c3da3037220 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 @@ 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 String toString() { } } - 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 static ReceivedContentMic createReceivedContentMic(HttpEntityEnclosingReq 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 b9d336ac030..b8369f5e01b 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 @@ 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 @@ private void setupKeysAndCertificates() throws Exception { 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 static void tearDownOnce() throws Exception { 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 void asynchronousMdnMessageTest() throws Exception { 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 void asynchronousMdnMessageTest() throws Exception { 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 5cbaba709ac..e14c5115741 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 void createReceivedContentMicTest() throws Exception { 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 d6ca3a7d6a6..2470df8290c 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 0c1180cd1c7..7ff15959af7 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 c92b50e2a9f..57ac5ba4cae 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 @@ private Certificate[] encryptingCertificateChain; @UriParam - private PrivateKey encryptingPrivateKey; + private PrivateKey decryptingPrivateKey; public AS2ApiName getApiName() { return apiName; @@ -452,14 +452,14 @@ public void setEncryptingCertificateChain(Certificate[] signingCertificateChain) 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 bc1fff57139..908eef766e0 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 static AS2ServerConnection createAS2ServerConnection(AS2Configuration con 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 9f83ea2747d..a52747fe8cc 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 @@ private X509Certificate issueCert; private KeyPair signingKP; + private KeyPair decryptingKP; private X509Certificate signingCert; private List<X509Certificate> certList; private AS2SignedDataGenerator gen; @@ -299,8 +300,6 @@ public void envelopedMessageSendTest() throws Exception { 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 void envelopedMessageSendTest() throws Exception { 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 void multipartSignedMessageTest() throws Exception { 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 void compressedMessageTest() throws Exception { 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 void asyncMDNTest() throws Exception { 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 @@ private static void setupServerKeysAndCertificates() throws Exception { 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 @@ private void setupKeysAndCertificates() throws Exception { 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 cb58af7716e..34b598b6d34 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.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.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 @@ + "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 void receivePlainEDIMessageTest() throws Exception { @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 void receiveMultipartSignedMessageTest() throws Exception { // 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 @@ private void setupSigningGenerator() throws Exception { } - private void setupKeysAndCertificates() throws Exception { + private static void setupKeysAndCertificates() throws Exception { // // set up our certificates // @@ -295,9 +369,19 @@ private void setupKeysAndCertificates() throws Exception { 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 eda444f3b43..6b66ad7cb89 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 void setResolvePropertyPlaceholders( /** * The key used to encrypt the EDI message. */ - private PrivateKey encryptingPrivateKey; + private PrivateKey decryptingPrivateKey; public AS2ApiName getApiName() { return apiName; @@ -412,12 +412,12 @@ public void setEncryptingCertificateChain( 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 ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org Issue Time Tracking ------------------- Worklog Id: (was: 175939) Time Spent: 0.5h (was: 20m) > Enhance the AS2 Component to send and receive encrypted AS2 messages > -------------------------------------------------------------------- > > Key: CAMEL-12605 > URL: https://issues.apache.org/jira/browse/CAMEL-12605 > Project: Camel > Issue Type: Improvement > Affects Versions: 2.23.0 > Reporter: William Collins > Assignee: William Collins > Priority: Major > Time Spent: 0.5h > Remaining Estimate: 0h > > Enhance the AS2 Component to support encrypted AS2 messages per RFC4130 -- This message was sent by Atlassian JIRA (v7.6.3#76005)