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
The following commit(s) were added to refs/heads/master by this push: new 2233a6a [CAMEL-12605] Added support for encrypted and signed AS2 message 2233a6a is described below commit 2233a6a98a7fc23de6e451390c11f40c5d9d59f5 Author: William Collins <punkhor...@gmail.com> AuthorDate: Wed Sep 26 14:27:25 2018 -0400 [CAMEL-12605] Added support for encrypted and signed AS2 message --- .../camel/component/as2/api/AS2ClientManager.java | 37 ++++--- .../component/as2/api/entity/EntityParser.java | 2 +- .../as2/api/entity/MultipartMimeEntity.java | 4 + .../as2/api/entity/MultipartSignedEntity.java | 9 +- .../camel/component/as2/api/AS2MessageTest.java | 116 +++++++++++++++++---- 5 files changed, 126 insertions(+), 42 deletions(-) diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java index c99f341..5865efa 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java @@ -29,8 +29,10 @@ import org.apache.camel.component.as2.api.util.EntityUtils; import org.apache.camel.component.as2.api.util.SigningUtils; import org.apache.http.HttpException; import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHttpEntityEnclosingRequest; +import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HttpCoreContext; import org.apache.http.util.Args; import org.bouncycastle.cms.CMSEnvelopedDataGenerator; @@ -252,31 +254,38 @@ public class AS2ClientManager { throw new HttpException("Failed to create EDI message entity", e); } switch (as2MessageStructure) { - case PLAIN: + case PLAIN: { applicationEDIEntity.setMainBody(true); EntityUtils.setMessageEntity(request, applicationEDIEntity); break; - case SIGNED: + } + case SIGNED: { AS2SignedDataGenerator gen = createSigningGenerator(httpContext); // Create Multipart Signed Entity - try { - MultipartSignedEntity multipartSignedEntity = new MultipartSignedEntity(applicationEDIEntity, gen, - AS2Charset.US_ASCII, AS2TransferEncoding.BASE64, true, null); - multipartSignedEntity.setMainBody(true); - EntityUtils.setMessageEntity(request, multipartSignedEntity); - } catch (Exception e) { - throw new HttpException("Failed to sign message", e); - } + MultipartSignedEntity multipartSignedEntity = new MultipartSignedEntity(applicationEDIEntity, gen, + AS2Charset.US_ASCII, AS2TransferEncoding.BASE64, true, null); + multipartSignedEntity.setMainBody(true); + EntityUtils.setMessageEntity(request, multipartSignedEntity); break; - case ENCRYPTED: + } + case ENCRYPTED: { CMSEnvelopedDataGenerator envelopedDataGenerator = createEncryptingGenerator(httpContext); OutputEncryptor encryptor = createEncryptor(httpContext); ApplicationPkcs7MimeEntity pkcs7MimeEntity = new ApplicationPkcs7MimeEntity(applicationEDIEntity, envelopedDataGenerator, encryptor, AS2TransferEncoding.BASE64, true); EntityUtils.setMessageEntity(request, pkcs7MimeEntity); break; - case ENCRYPTED_SIGNED: - // TODO : Add code here to add application/pkcs7-mime entity when encryption facility available. + } + case ENCRYPTED_SIGNED: { + AS2SignedDataGenerator signingGenrator = createSigningGenerator(httpContext); + MultipartSignedEntity multipartSignedEntity = new MultipartSignedEntity(applicationEDIEntity, + signingGenrator, AS2Charset.US_ASCII, AS2TransferEncoding.BASE64, false, null); + + CMSEnvelopedDataGenerator envelopedDataGenerator = createEncryptingGenerator(httpContext); + OutputEncryptor encryptor = createEncryptor(httpContext); + ApplicationPkcs7MimeEntity pkcs7MimeEntity = new ApplicationPkcs7MimeEntity(multipartSignedEntity, envelopedDataGenerator, encryptor, AS2TransferEncoding.BASE64, true); + EntityUtils.setMessageEntity(request, pkcs7MimeEntity); break; + } default: throw new HttpException("Unknown AS2 Message Structure"); } @@ -292,7 +301,7 @@ public class AS2ClientManager { httpContext.setAttribute(HTTP_RESPONSE, response); return httpContext; } - + public AS2SignedDataGenerator createSigningGenerator(HttpCoreContext httpContext) throws HttpException { Certificate[] certificateChain = httpContext.getAttribute(SIGNING_CERTIFICATE_CHAIN, Certificate[].class); 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 fcb2431..aaf12e8 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 @@ -171,7 +171,7 @@ public final class EntityParser { throw new HttpException("Failed to read start boundary for body part", e); } - if (!foundEndBoundary) { + if (!foundEndBoundary && boundary != null) { throw new HttpException("Failed to find start boundary for body part"); } diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java index 2b206f3..03f5835 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartMimeEntity.java @@ -55,6 +55,10 @@ public abstract class MultipartMimeEntity extends MimeEntity { protected MultipartMimeEntity() { } + + public String getBoundary() { + return boundary; + } public void addPart(MimeEntity part) { parts.add(part); diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java index 6869b5f..3b0a6d2 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/MultipartSignedEntity.java @@ -22,10 +22,8 @@ import java.io.InputStream; import java.security.cert.X509Certificate; import java.util.Collection; -import org.apache.camel.component.as2.api.AS2Header; import org.apache.camel.component.as2.api.AS2SignedDataGenerator; -import org.apache.http.entity.ContentType; -import org.apache.http.message.BasicHeader; +import org.apache.http.HttpException; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cms.CMSProcessable; @@ -38,10 +36,9 @@ import org.bouncycastle.util.Store; public class MultipartSignedEntity extends MultipartMimeEntity { - public MultipartSignedEntity(MimeEntity data, AS2SignedDataGenerator signer, String signatureCharSet, String signatureTransferEncoding, boolean isMainBody, String boundary) throws Exception { + public MultipartSignedEntity(MimeEntity data, AS2SignedDataGenerator signer, String signatureCharSet, String signatureTransferEncoding, boolean isMainBody, String boundary) throws HttpException { super(null, isMainBody, boundary); - ContentType contentType = signer.createMultipartSignedContentType(this.boundary); - this.contentType = new BasicHeader(AS2Header.CONTENT_TYPE, contentType.toString()); + setContentType(signer.createMultipartSignedContentType(this.boundary)); addPart(data); ApplicationPkcs7SignatureEntity signature = new ApplicationPkcs7SignatureEntity(data, signer, signatureCharSet, signatureTransferEncoding, false); addPart(signature); 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 d9d3338..7d181aa 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 @@ -366,105 +366,105 @@ public class AS2MessageTest { @Test public void aes128CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES128_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.AES128_CBC); } @Test public void aes192CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES192_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.AES192_CBC); } @Test public void aes256CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES256_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.AES256_CBC); } @Test public void aes128CcmEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES128_CCM); + envelopedMessageTest(AS2EncryptionAlgorithm.AES128_CCM); } @Test public void aes192CcmEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES192_CCM); + envelopedMessageTest(AS2EncryptionAlgorithm.AES192_CCM); } @Test public void aes256CcmEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES256_CCM); + envelopedMessageTest(AS2EncryptionAlgorithm.AES256_CCM); } @Test public void aes128GcmEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES128_GCM); + envelopedMessageTest(AS2EncryptionAlgorithm.AES128_GCM); } @Test public void aes192GcmEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES192_GCM); + envelopedMessageTest(AS2EncryptionAlgorithm.AES192_GCM); } @Test public void aes256GcmEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.AES256_GCM); + envelopedMessageTest(AS2EncryptionAlgorithm.AES256_GCM); } @Test public void camellia128CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.CAMELLIA128_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.CAMELLIA128_CBC); } @Test public void camellia192CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.CAMELLIA192_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.CAMELLIA192_CBC); } @Test public void camellia256CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.CAMELLIA256_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.CAMELLIA256_CBC); } @Test public void cast5CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.CAST5_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.CAST5_CBC); } @Test public void desCbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.DES_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.DES_CBC); } @Test public void desEde3CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.DES_EDE3_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.DES_EDE3_CBC); } @Test public void cost28147GcfbEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.GOST28147_GCFB); + envelopedMessageTest(AS2EncryptionAlgorithm.GOST28147_GCFB); } @Test public void ideaCbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.IDEA_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.IDEA_CBC); } @Test public void rc2CbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.RC2_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.RC2_CBC); } @Test public void rc4EnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.RC4); + envelopedMessageTest(AS2EncryptionAlgorithm.RC4); } @Test public void seedCbcEnvelopedMessageTest() throws Exception { - envelopeddMessageTest(AS2EncryptionAlgorithm.SEED_CBC); + envelopedMessageTest(AS2EncryptionAlgorithm.SEED_CBC); } - public void envelopeddMessageTest(AS2EncryptionAlgorithm encryptionAlgorithm) throws Exception { + public void envelopedMessageTest(AS2EncryptionAlgorithm encryptionAlgorithm) throws Exception { AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT); AS2ClientManager clientManager = new AS2ClientManager(clientConnection); @@ -520,6 +520,80 @@ public class AS2MessageTest { } @Test + public void aes128CbcEnvelopedAndSignedMessageTest() throws Exception { + envelopedAndSignedMessageTest(AS2EncryptionAlgorithm.AES128_CBC); + } + + public void envelopedAndSignedMessageTest(AS2EncryptionAlgorithm encryptionAlgorithm) throws Exception { + AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, + TARGET_HOST, TARGET_PORT); + AS2ClientManager clientManager = new AS2ClientManager(clientConnection); + + LOG.info("Key Algoritm: " + signingKP.getPrivate().getAlgorithm()); + + HttpCoreContext httpContext = clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME, + AS2MessageStructure.ENCRYPTED_SIGNED, ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), + null, certList.toArray(new Certificate[0]), signingKP.getPrivate(), DISPOSITION_NOTIFICATION_TO, + SIGNED_RECEIPT_MIC_ALGORITHMS, encryptionAlgorithm, certList.toArray(new Certificate[0]), + signingKP.getPrivate()); + + HttpRequest request = httpContext.getRequest(); + assertEquals("Unexpected method value", METHOD, request.getRequestLine().getMethod()); + assertEquals("Unexpected request URI value", REQUEST_URI, request.getRequestLine().getUri()); + assertEquals("Unexpected HTTP version value", HttpVersion.HTTP_1_1, + request.getRequestLine().getProtocolVersion()); + + assertEquals("Unexpected subject value", SUBJECT, request.getFirstHeader(AS2Header.SUBJECT).getValue()); + assertEquals("Unexpected from value", FROM, request.getFirstHeader(AS2Header.FROM).getValue()); + assertEquals("Unexpected AS2 version value", AS2_VERSION, + request.getFirstHeader(AS2Header.AS2_VERSION).getValue()); + assertEquals("Unexpected AS2 from value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_FROM).getValue()); + assertEquals("Unexpected AS2 to value", AS2_NAME, request.getFirstHeader(AS2Header.AS2_TO).getValue()); + assertTrue("Unexpected message id value", + request.getFirstHeader(AS2Header.MESSAGE_ID).getValue().endsWith(CLIENT_FQDN + ">")); + assertEquals("Unexpected target host value", TARGET_HOST + ":" + TARGET_PORT, + request.getFirstHeader(AS2Header.TARGET_HOST).getValue()); + assertEquals("Unexpected user agent value", USER_AGENT, + request.getFirstHeader(AS2Header.USER_AGENT).getValue()); + assertNotNull("Date value missing", request.getFirstHeader(AS2Header.DATE)); + assertNotNull("Content length value missing", request.getFirstHeader(AS2Header.CONTENT_LENGTH)); + assertTrue("Unexpected content type for message", + request.getFirstHeader(AS2Header.CONTENT_TYPE).getValue().startsWith(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 ApplicationPkcs7MimeEntity); + ApplicationPkcs7MimeEntity envelopedEntity = (ApplicationPkcs7MimeEntity) 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 MultipartSignedEntity); + MultipartSignedEntity multipartSignedEntity = (MultipartSignedEntity) encryptedEntity; + assertTrue("Unexpected content type for enveloped mime part", + multipartSignedEntity.getContentType().getValue().startsWith(AS2MediaType.MULTIPART_SIGNED)); + assertFalse("Enveloped mime type set as main body of request", multipartSignedEntity.isMainBody()); + assertTrue("Request contains invalid number of mime parts", multipartSignedEntity.getPartCount() == 2); + + // Validated first mime part. + assertTrue("First mime part incorrect type ", multipartSignedEntity.getPart(0) instanceof ApplicationEDIFACTEntity); + ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) multipartSignedEntity.getPart(0); + assertTrue("Unexpected content type for first mime part", + ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT)); + assertFalse("First mime type set as main body of request", ediEntity.isMainBody()); + + // Validate second mime part. + assertTrue("Second mime part incorrect type ", + multipartSignedEntity.getPart(1) instanceof ApplicationPkcs7SignatureEntity); + ApplicationPkcs7SignatureEntity signatureEntity = (ApplicationPkcs7SignatureEntity) multipartSignedEntity.getPart(1); + assertTrue("Unexpected content type for second mime part", + signatureEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_PKCS7_SIGNATURE)); + assertFalse("First mime type set as main body of request", signatureEntity.isMainBody()); + + } + + @Test public void signatureVerificationTest() throws Exception { AS2ClientConnection clientConnection = new AS2ClientConnection(AS2_VERSION, USER_AGENT, CLIENT_FQDN, TARGET_HOST, TARGET_PORT);