blob: c3f22929af4dce9e973ddbfb543673baaefb9665 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.as2;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.as2.api.AS2AsynchronousMDNManager;
import org.apache.camel.component.as2.api.AS2Charset;
import org.apache.camel.component.as2.api.AS2CompressionAlgorithm;
import org.apache.camel.component.as2.api.AS2Constants;
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.AS2ServerConnection;
import org.apache.camel.component.as2.api.AS2ServerManager;
import org.apache.camel.component.as2.api.AS2SignatureAlgorithm;
import org.apache.camel.component.as2.api.AS2SignedDataGenerator;
import org.apache.camel.component.as2.api.entity.AS2DispositionModifier;
import org.apache.camel.component.as2.api.entity.AS2DispositionType;
import org.apache.camel.component.as2.api.entity.AS2MessageDispositionNotificationEntity;
import org.apache.camel.component.as2.api.entity.ApplicationEDIEntity;
import org.apache.camel.component.as2.api.entity.ApplicationPkcs7MimeCompressedDataEntity;
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.DispositionMode;
import org.apache.camel.component.as2.api.entity.DispositionNotificationMultipartReportEntity;
import org.apache.camel.component.as2.api.entity.MimeEntity;
import org.apache.camel.component.as2.api.entity.MultipartSignedEntity;
import org.apache.camel.component.as2.api.util.AS2Utils;
import org.apache.camel.component.as2.api.util.EntityUtils;
import org.apache.camel.component.as2.api.util.HttpMessageUtils;
import org.apache.camel.component.as2.api.util.MicUtils;
import org.apache.camel.component.as2.api.util.MicUtils.ReceivedContentMic;
import org.apache.camel.component.as2.internal.AS2ApiCollection;
import org.apache.camel.component.as2.internal.AS2ClientManagerApiMethod;
import org.apache.camel.http.common.HttpMessage;
import org.apache.camel.test.AvailablePortFinder;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.protocol.HttpDateGenerator;
import org.apache.http.protocol.HttpRequestHandler;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.ZlibExpanderProvider;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test class for {@link org.apache.camel.component.as2.api.AS2ClientManager} APIs.
*/
public class AS2ClientManagerIntegrationTest extends AbstractAS2TestSupport {
private static final Logger LOG = LoggerFactory.getLogger(AS2ClientManagerIntegrationTest.class);
private static final String PATH_PREFIX = AS2ApiCollection.getCollection().getApiName(AS2ClientManagerApiMethod.class).getName();
private static final String SERVER_FQDN = "server.example.com";
private static final String ORIGIN_SERVER_NAME = "AS2ClientManagerIntegrationTest Server";
private static final String AS2_VERSION = "1.1";
private static final String REQUEST_URI = "/";
private static final String SUBJECT = "Test Case";
private static final String AS2_NAME = "878051556";
private static final String FROM = "mrAS@example.org";
private static final String MDN_FROM = "as2Test@server.example.com";
private static final String MDN_SUBJECT_PREFIX = "MDN Response:";
private static final String EDI_MESSAGE =
"UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'\n"
+ "UNH+00000000000117+INVOIC:D:97B:UN'\n"
+ "BGM+380+342459+9'\n"
+ "DTM+3:20060515:102'\n"
+ "RFF+ON:521052'\n"
+ "NAD+BY+792820524::16++CUMMINS MID-RANGE ENGINE PLANT'\n"
+ "NAD+SE+005435656::16++GENERAL WIDGET COMPANY'\n"
+ "CUX+1:USD'\n"
+ "LIN+1++157870:IN'\n"
+ "IMD+F++:::WIDGET'\n"
+ "QTY+47:1020:EA'\n"
+ "ALI+US'\n"
+ "MOA+203:1202.58'\n"
+ "PRI+INV:1.179'\n"
+ "LIN+2++157871:IN'\n"
+ "IMD+F++:::DIFFERENT WIDGET'\n"
+ "QTY+47:20:EA'\n"
+ "ALI+JP'\n"
+ "MOA+203:410'\n"
+ "PRI+INV:20.5'\n"
+ "UNS+S'\n"
+ "MOA+39:2137.58'\n"
+ "ALC+C+ABG'\n"
+ "MOA+8:525'\n"
+ "UNT+23+00000000000117'\n"
+ "UNZ+1+00000000000778'\n";
private static final String EDI_MESSAGE_CONTENT_TRANSFER_ENCODING = "7bit";
private static final String EXPECTED_AS2_VERSION = AS2_VERSION;
private static final String EXPECTED_MDN_SUBJECT = MDN_SUBJECT_PREFIX + SUBJECT;
private static final String[] SIGNED_RECEIPT_MIC_ALGORITHMS = new String[] {"sha1", "md5"};
private static final String DISPOSITION_NOTIFICATION_OPTIONS = "signed-receipt-protocol=optional,pkcs7-signature; signed-receipt-micalg=optional,sha1";
private static final int PARTNER_TARGET_PORT = 8888;
private static final int MDN_TARGET_PORT = AvailablePortFinder.getNextAvailable();
private static final String RECIPIENT_DELIVERY_ADDRESS = "http://localhost:" + MDN_TARGET_PORT + "/handle-receipts";
private static final String REPORTING_UA = "Server Responding with MDN";
private static AS2ServerConnection serverConnection;
private static KeyPair serverSigningKP;
private static List<X509Certificate> serverCertList;
private static RequestHandler requestHandler;
private static final HttpDateGenerator DATE_GENERATOR = new HttpDateGenerator();
private KeyPair issueKP;
private X509Certificate issueCert;
private KeyPair signingKP;
private KeyPair decryptingKP;
private X509Certificate signingCert;
private List<X509Certificate> certList;
private AS2SignedDataGenerator gen;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
Security.addProvider(new BouncyCastleProvider());
setupKeysAndCertificates();
// Create and populate certificate store.
JcaCertStore certs = new JcaCertStore(certList);
// Create capabilities vector
SMIMECapabilityVector capabilities = new SMIMECapabilityVector();
capabilities.addCapability(SMIMECapability.dES_EDE3_CBC);
capabilities.addCapability(SMIMECapability.rC2_CBC, 128);
capabilities.addCapability(SMIMECapability.dES_CBC);
// Create signing attributes
ASN1EncodableVector attributes = new ASN1EncodableVector();
attributes.add(new SMIMEEncryptionKeyPreferenceAttribute(new IssuerAndSerialNumber(new X500Name(signingCert.getIssuerDN().getName()), signingCert.getSerialNumber())));
attributes.add(new SMIMECapabilitiesAttribute(capabilities));
for (String signingAlgorithmName : AS2SignedDataGenerator
.getSupportedSignatureAlgorithmNamesForKey(signingKP.getPrivate())) {
try {
this.gen = new AS2SignedDataGenerator();
this.gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC")
.setSignedAttributeGenerator(new AttributeTable(attributes))
.build(signingAlgorithmName, signingKP.getPrivate(), signingCert));
this.gen.addCertificates(certs);
break;
} catch (Exception e) {
this.gen = null;
continue;
}
}
if (this.gen == null) {
throw new Exception("failed to create signing generator");
}
}
@Test
public void plainMessageSendTest() throws Exception {
final Map<String, Object> headers = new HashMap<>();
// parameter type is String
headers.put("CamelAS2.requestUri", REQUEST_URI);
// parameter type is String
headers.put("CamelAS2.subject", SUBJECT);
// parameter type is String
headers.put("CamelAS2.from", FROM);
// parameter type is String
headers.put("CamelAS2.as2From", AS2_NAME);
// parameter type is String
headers.put("CamelAS2.as2To", AS2_NAME);
// parameter type is org.apache.camel.component.as2.api.AS2MessageStructure
headers.put("CamelAS2.as2MessageStructure", AS2MessageStructure.PLAIN);
// parameter type is org.apache.http.entity.ContentType
headers.put("CamelAS2.ediMessageContentType", ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII));
// parameter type is String
headers.put("CamelAS2.ediMessageTransferEncoding", EDI_MESSAGE_CONTENT_TRANSFER_ENCODING);
// parameter type is String
headers.put("CamelAS2.dispositionNotificationTo", "mrAS2@example.com");
final Triple<HttpEntity, HttpRequest, HttpResponse> result = executeRequest(headers);
HttpEntity responseEntity = result.getLeft();
HttpRequest request = result.getMiddle();
HttpResponse response = result.getRight();
assertNotNull("send result", result);
LOG.debug("send: " + result);
assertNotNull("Request", request);
assertTrue("Request does not contain body", request instanceof HttpEntityEnclosingRequest);
HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
assertNotNull("Request body", entity);
assertTrue("Request body does not contain EDI entity", entity instanceof ApplicationEDIEntity);
String ediMessage = ((ApplicationEDIEntity)entity).getEdiMessage();
assertEquals("EDI message is different", EDI_MESSAGE.replaceAll("[\n\r]", ""), ediMessage.replaceAll("[\n\r]", ""));
assertNotNull("Response", response);
assertTrue("Unexpected response type", HttpMessageUtils.getHeaderValue(response, AS2Header.CONTENT_TYPE).startsWith(AS2MimeType.MULTIPART_REPORT));
assertEquals("Unexpected mime version", AS2Constants.MIME_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.MIME_VERSION));
assertEquals("Unexpected AS2 version", EXPECTED_AS2_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_VERSION));
assertEquals("Unexpected MDN subject", EXPECTED_MDN_SUBJECT, HttpMessageUtils.getHeaderValue(response, AS2Header.SUBJECT));
assertEquals("Unexpected MDN from", MDN_FROM, HttpMessageUtils.getHeaderValue(response, AS2Header.FROM));
assertEquals("Unexpected AS2 from", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_FROM));
assertEquals("Unexpected AS2 to", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_TO));
assertNotNull("Missing message id", HttpMessageUtils.getHeaderValue(response, AS2Header.MESSAGE_ID));
assertNotNull("Response entity", responseEntity);
assertTrue("Unexpected response entity type", responseEntity instanceof DispositionNotificationMultipartReportEntity);
DispositionNotificationMultipartReportEntity reportEntity = (DispositionNotificationMultipartReportEntity)responseEntity;
assertEquals("Unexpected number of body parts in report", 2, reportEntity.getPartCount());
MimeEntity firstPart = reportEntity.getPart(0);
assertEquals("Unexpected content type in first body part of report", ContentType.create(AS2MimeType.TEXT_PLAIN, AS2Charset.US_ASCII).toString(), firstPart.getContentTypeValue());
MimeEntity secondPart = reportEntity.getPart(1);
assertEquals("Unexpected content type in second body part of report",
ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, AS2Charset.US_ASCII).toString(),
secondPart.getContentTypeValue());
assertTrue("", secondPart instanceof AS2MessageDispositionNotificationEntity);
AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity = (AS2MessageDispositionNotificationEntity) secondPart;
assertEquals("Unexpected value for reporting UA", ORIGIN_SERVER_NAME, messageDispositionNotificationEntity.getReportingUA());
assertEquals("Unexpected value for final recipient", AS2_NAME, messageDispositionNotificationEntity.getFinalRecipient());
assertEquals("Unexpected value for original message ID", HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID), messageDispositionNotificationEntity.getOriginalMessageId());
assertEquals("Unexpected value for disposition mode", DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, messageDispositionNotificationEntity.getDispositionMode());
assertEquals("Unexpected value for disposition type", AS2DispositionType.PROCESSED, messageDispositionNotificationEntity.getDispositionType());
}
@Test
public void envelopedMessageSendTest() throws Exception {
final Map<String, Object> headers = new HashMap<>();
// parameter type is String
headers.put("CamelAS2.requestUri", REQUEST_URI);
// parameter type is String
headers.put("CamelAS2.subject", SUBJECT);
// parameter type is String
headers.put("CamelAS2.from", FROM);
// parameter type is String
headers.put("CamelAS2.as2From", AS2_NAME);
// parameter type is String
headers.put("CamelAS2.as2To", AS2_NAME);
// parameter type is org.apache.camel.component.as2.api.AS2MessageStructure
headers.put("CamelAS2.as2MessageStructure", AS2MessageStructure.ENCRYPTED);
// parameter type is org.apache.http.entity.ContentType
headers.put("CamelAS2.ediMessageContentType", ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII));
// parameter type is String
headers.put("CamelAS2.ediMessageTransferEncoding", EDI_MESSAGE_CONTENT_TRANSFER_ENCODING);
// parameter type is String
headers.put("CamelAS2.dispositionNotificationTo", "mrAS2@example.com");
// parameter type is org.apache.camel.component.as2.api.AS2EncryptionAlgorithm
headers.put("CamelAS2.encryptingAlgorithm", AS2EncryptionAlgorithm.AES128_CBC);
// parameter type is java.security.cert.Certificate[]
headers.put("CamelAS2.encryptingCertificateChain", certList);
final Triple<HttpEntity, HttpRequest, HttpResponse> result = executeRequest(headers);
HttpEntity responseEntity = result.getLeft();
HttpRequest request = result.getMiddle();
HttpResponse response = result.getRight();
assertNotNull("send result", result);
LOG.debug("send: " + result);
assertNotNull("Request", request);
assertTrue("Request does not contain body", request instanceof HttpEntityEnclosingRequest);
HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
assertNotNull("Request body", entity);
assertTrue("Request body does not contain ApplicationPkcs7Mime entity", entity instanceof ApplicationPkcs7MimeEnvelopedDataEntity);
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]", ""));
assertNotNull("Response", response);
assertTrue("Unexpected response type", HttpMessageUtils.getHeaderValue(response, AS2Header.CONTENT_TYPE).startsWith(AS2MimeType.MULTIPART_REPORT));
assertEquals("Unexpected mime version", AS2Constants.MIME_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.MIME_VERSION));
assertEquals("Unexpected AS2 version", EXPECTED_AS2_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_VERSION));
assertEquals("Unexpected MDN subject", EXPECTED_MDN_SUBJECT, HttpMessageUtils.getHeaderValue(response, AS2Header.SUBJECT));
assertEquals("Unexpected MDN from", MDN_FROM, HttpMessageUtils.getHeaderValue(response, AS2Header.FROM));
assertEquals("Unexpected AS2 from", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_FROM));
assertEquals("Unexpected AS2 to", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_TO));
assertNotNull("Missing message id", HttpMessageUtils.getHeaderValue(response, AS2Header.MESSAGE_ID));
assertNotNull("Response entity", responseEntity);
assertTrue("Unexpected response entity type", responseEntity instanceof DispositionNotificationMultipartReportEntity);
DispositionNotificationMultipartReportEntity reportEntity = (DispositionNotificationMultipartReportEntity)responseEntity;
assertEquals("Unexpected number of body parts in report", 2, reportEntity.getPartCount());
MimeEntity firstPart = reportEntity.getPart(0);
assertEquals("Unexpected content type in first body part of report", ContentType.create(AS2MimeType.TEXT_PLAIN, AS2Charset.US_ASCII).toString(), firstPart.getContentTypeValue());
MimeEntity secondPart = reportEntity.getPart(1);
assertEquals("Unexpected content type in second body part of report",
ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, AS2Charset.US_ASCII).toString(),
secondPart.getContentTypeValue());
assertTrue("", secondPart instanceof AS2MessageDispositionNotificationEntity);
AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity = (AS2MessageDispositionNotificationEntity) secondPart;
assertEquals("Unexpected value for reporting UA", ORIGIN_SERVER_NAME, messageDispositionNotificationEntity.getReportingUA());
assertEquals("Unexpected value for final recipient", AS2_NAME, messageDispositionNotificationEntity.getFinalRecipient());
assertEquals("Unexpected value for original message ID", HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID), messageDispositionNotificationEntity.getOriginalMessageId());
assertEquals("Unexpected value for disposition mode", DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, messageDispositionNotificationEntity.getDispositionMode());
assertEquals("Unexpected value for disposition type", AS2DispositionType.PROCESSED, messageDispositionNotificationEntity.getDispositionType());
}
@Test
public void multipartSignedMessageTest() throws Exception {
final Map<String, Object> headers = new HashMap<>();
// parameter type is String
headers.put("CamelAS2.requestUri", REQUEST_URI);
// parameter type is String
headers.put("CamelAS2.subject", SUBJECT);
// parameter type is String
headers.put("CamelAS2.from", FROM);
// parameter type is String
headers.put("CamelAS2.as2From", AS2_NAME);
// parameter type is String
headers.put("CamelAS2.as2To", AS2_NAME);
// parameter type is org.apache.camel.component.as2.api.AS2MessageStructure
headers.put("CamelAS2.as2MessageStructure", AS2MessageStructure.SIGNED);
// parameter type is org.apache.http.entity.ContentType
headers.put("CamelAS2.ediMessageContentType", ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII));
// parameter type is String
headers.put("CamelAS2.ediMessageTransferEncoding", EDI_MESSAGE_CONTENT_TRANSFER_ENCODING);
// parameter type is org.apache.camel.component.as2.api.AS2SignatureAlgorithm
headers.put("CamelAS2.signingAlgorithm", AS2SignatureAlgorithm.SHA512WITHRSA);
// parameter type is java.security.cert.Certificate[]
headers.put("CamelAS2.signingCertificateChain", certList.toArray(new Certificate[0]));
// parameter type is java.security.PrivateKey
headers.put("CamelAS2.signingPrivateKey", signingKP.getPrivate());
// parameter type is String
headers.put("CamelAS2.dispositionNotificationTo", "mrAS2@example.com");
// parameter type is String[]
headers.put("CamelAS2.signedReceiptMicAlgorithms", SIGNED_RECEIPT_MIC_ALGORITHMS);
final Triple<HttpEntity, HttpRequest, HttpResponse> result = executeRequest(headers);
HttpEntity responseEntity = result.getLeft();
HttpRequest request = result.getMiddle();
HttpResponse response = result.getRight();
assertNotNull("send result", result);
LOG.debug("send: " + result);
assertNotNull("Request", request);
assertTrue("Request does not contain body", request instanceof HttpEntityEnclosingRequest);
HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
assertNotNull("Request body", entity);
assertTrue("Request body does not contain EDI entity", entity instanceof MultipartSignedEntity);
MimeEntity signedEntity = ((MultipartSignedEntity)entity).getSignedDataEntity();
assertTrue("Signed entity wrong type", signedEntity instanceof ApplicationEDIEntity);
ApplicationEDIEntity ediMessageEntity = (ApplicationEDIEntity) signedEntity;
String ediMessage = ediMessageEntity.getEdiMessage();
assertEquals("EDI message is different", EDI_MESSAGE.replaceAll("[\n\r]", ""), ediMessage.replaceAll("[\n\r]", ""));
assertNotNull("Response", response);
String contentTypeHeaderValue = HttpMessageUtils.getHeaderValue(response, AS2Header.CONTENT_TYPE);
ContentType responseContentType = ContentType.parse(contentTypeHeaderValue);
assertEquals("Unexpected response type", AS2MimeType.MULTIPART_SIGNED, responseContentType.getMimeType());
assertEquals("Unexpected mime version", AS2Constants.MIME_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.MIME_VERSION));
assertEquals("Unexpected AS2 version", EXPECTED_AS2_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_VERSION));
assertEquals("Unexpected MDN subject", EXPECTED_MDN_SUBJECT, HttpMessageUtils.getHeaderValue(response, AS2Header.SUBJECT));
assertEquals("Unexpected MDN from", MDN_FROM, HttpMessageUtils.getHeaderValue(response, AS2Header.FROM));
assertEquals("Unexpected AS2 from", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_FROM));
assertEquals("Unexpected AS2 to", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_TO));
assertNotNull("Missing message id", HttpMessageUtils.getHeaderValue(response, AS2Header.MESSAGE_ID));
assertNotNull("Response entity", responseEntity);
assertTrue("Unexpected response entity type", responseEntity instanceof MultipartSignedEntity);
MultipartSignedEntity responseSignedEntity = (MultipartSignedEntity) responseEntity;
assertTrue("Signature for response entity is invalid", responseSignedEntity.isValid());
MimeEntity responseSignedDataEntity = responseSignedEntity.getSignedDataEntity();
assertTrue("Signed entity wrong type", responseSignedDataEntity instanceof DispositionNotificationMultipartReportEntity);
DispositionNotificationMultipartReportEntity reportEntity = (DispositionNotificationMultipartReportEntity)responseSignedDataEntity;
assertEquals("Unexpected number of body parts in report", 2, reportEntity.getPartCount());
MimeEntity firstPart = reportEntity.getPart(0);
assertEquals("Unexpected content type in first body part of report", ContentType.create(AS2MimeType.TEXT_PLAIN, AS2Charset.US_ASCII).toString(), firstPart.getContentTypeValue());
MimeEntity secondPart = reportEntity.getPart(1);
assertEquals("Unexpected content type in second body part of report",
ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, AS2Charset.US_ASCII).toString(),
secondPart.getContentTypeValue());
ApplicationPkcs7SignatureEntity signatureEntity = responseSignedEntity.getSignatureEntity();
assertNotNull("Signature Entity", signatureEntity);
assertTrue("", secondPart instanceof AS2MessageDispositionNotificationEntity);
AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity = (AS2MessageDispositionNotificationEntity) secondPart;
assertEquals("Unexpected value for reporting UA", ORIGIN_SERVER_NAME, messageDispositionNotificationEntity.getReportingUA());
assertEquals("Unexpected value for final recipient", AS2_NAME, messageDispositionNotificationEntity.getFinalRecipient());
assertEquals("Unexpected value for original message ID", HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID), messageDispositionNotificationEntity.getOriginalMessageId());
assertEquals("Unexpected value for disposition mode", DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, messageDispositionNotificationEntity.getDispositionMode());
assertEquals("Unexpected value for disposition type", AS2DispositionType.PROCESSED, messageDispositionNotificationEntity.getDispositionType());
ReceivedContentMic receivedContentMic = messageDispositionNotificationEntity.getReceivedContentMic();
ReceivedContentMic computedContentMic = MicUtils.createReceivedContentMic((HttpEntityEnclosingRequest)request, decryptingKP.getPrivate());
assertEquals("Received content MIC does not match computed", computedContentMic.getEncodedMessageDigest(), receivedContentMic.getEncodedMessageDigest());
}
@Test
public void compressedMessageTest() throws Exception {
final Map<String, Object> headers = new HashMap<>();
// parameter type is String
headers.put("CamelAS2.requestUri", REQUEST_URI);
// parameter type is String
headers.put("CamelAS2.subject", SUBJECT);
// parameter type is String
headers.put("CamelAS2.from", FROM);
// parameter type is String
headers.put("CamelAS2.as2From", AS2_NAME);
// parameter type is String
headers.put("CamelAS2.as2To", AS2_NAME);
// parameter type is org.apache.camel.component.as2.api.AS2MessageStructure
headers.put("CamelAS2.as2MessageStructure", AS2MessageStructure.PLAIN_COMPRESSED);
// parameter type is org.apache.http.entity.ContentType
headers.put("CamelAS2.ediMessageContentType", ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII));
// parameter type is String
headers.put("CamelAS2.ediMessageTransferEncoding", EDI_MESSAGE_CONTENT_TRANSFER_ENCODING);
// parameter type is org.apache.camel.component.as2.api.AS2CompressionAlgorithm
headers.put("CamelAS2.compressionAlgorithm", AS2CompressionAlgorithm.ZLIB);
// parameter type is String
headers.put("CamelAS2.dispositionNotificationTo", "mrAS2@example.com");
// parameter type is String[]
headers.put("CamelAS2.signedReceiptMicAlgorithms", SIGNED_RECEIPT_MIC_ALGORITHMS);
final Triple<HttpEntity, HttpRequest, HttpResponse> result = executeRequest(headers);
HttpEntity responseEntity = result.getLeft();
HttpRequest request = result.getMiddle();
HttpResponse response = result.getRight();
assertNotNull("send result", result);
LOG.debug("send: " + result);
assertNotNull("Request", request);
assertTrue("Request does not contain body", request instanceof HttpEntityEnclosingRequest);
HttpEntity entity = ((HttpEntityEnclosingRequest)request).getEntity();
assertNotNull("Request body", entity);
assertTrue("Request body does not contain EDI entity", entity instanceof ApplicationPkcs7MimeCompressedDataEntity);
MimeEntity compressedEntity = ((ApplicationPkcs7MimeCompressedDataEntity)entity).getCompressedEntity(new ZlibExpanderProvider());
assertTrue("Signed entity wrong type", compressedEntity instanceof ApplicationEDIEntity);
ApplicationEDIEntity ediMessageEntity = (ApplicationEDIEntity) compressedEntity;
String ediMessage = ediMessageEntity.getEdiMessage();
assertEquals("EDI message is different", EDI_MESSAGE.replaceAll("[\n\r]", ""), ediMessage.replaceAll("[\n\r]", ""));
assertNotNull("Response", response);
String contentTypeHeaderValue = HttpMessageUtils.getHeaderValue(response, AS2Header.CONTENT_TYPE);
ContentType responseContentType = ContentType.parse(contentTypeHeaderValue);
assertEquals("Unexpected response type", AS2MimeType.MULTIPART_SIGNED, responseContentType.getMimeType());
assertEquals("Unexpected mime version", AS2Constants.MIME_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.MIME_VERSION));
assertEquals("Unexpected AS2 version", EXPECTED_AS2_VERSION, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_VERSION));
assertEquals("Unexpected MDN subject", EXPECTED_MDN_SUBJECT, HttpMessageUtils.getHeaderValue(response, AS2Header.SUBJECT));
assertEquals("Unexpected MDN from", MDN_FROM, HttpMessageUtils.getHeaderValue(response, AS2Header.FROM));
assertEquals("Unexpected AS2 from", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_FROM));
assertEquals("Unexpected AS2 to", AS2_NAME, HttpMessageUtils.getHeaderValue(response, AS2Header.AS2_TO));
assertNotNull("Missing message id", HttpMessageUtils.getHeaderValue(response, AS2Header.MESSAGE_ID));
assertNotNull("Response entity", responseEntity);
assertTrue("Unexpected response entity type", responseEntity instanceof MultipartSignedEntity);
MultipartSignedEntity responseSignedEntity = (MultipartSignedEntity) responseEntity;
assertTrue("Signature for response entity is invalid", responseSignedEntity.isValid());
MimeEntity responseSignedDataEntity = responseSignedEntity.getSignedDataEntity();
assertTrue("Signed entity wrong type", responseSignedDataEntity instanceof DispositionNotificationMultipartReportEntity);
DispositionNotificationMultipartReportEntity reportEntity = (DispositionNotificationMultipartReportEntity)responseSignedDataEntity;
assertEquals("Unexpected number of body parts in report", 2, reportEntity.getPartCount());
MimeEntity firstPart = reportEntity.getPart(0);
assertEquals("Unexpected content type in first body part of report", ContentType.create(AS2MimeType.TEXT_PLAIN, AS2Charset.US_ASCII).toString(), firstPart.getContentTypeValue());
MimeEntity secondPart = reportEntity.getPart(1);
assertEquals("Unexpected content type in second body part of report",
ContentType.create(AS2MimeType.MESSAGE_DISPOSITION_NOTIFICATION, AS2Charset.US_ASCII).toString(),
secondPart.getContentTypeValue());
ApplicationPkcs7SignatureEntity signatureEntity = responseSignedEntity.getSignatureEntity();
assertNotNull("Signature Entity", signatureEntity);
assertTrue("", secondPart instanceof AS2MessageDispositionNotificationEntity);
AS2MessageDispositionNotificationEntity messageDispositionNotificationEntity = (AS2MessageDispositionNotificationEntity) secondPart;
assertEquals("Unexpected value for reporting UA", ORIGIN_SERVER_NAME, messageDispositionNotificationEntity.getReportingUA());
assertEquals("Unexpected value for final recipient", AS2_NAME, messageDispositionNotificationEntity.getFinalRecipient());
assertEquals("Unexpected value for original message ID", HttpMessageUtils.getHeaderValue(request, AS2Header.MESSAGE_ID), messageDispositionNotificationEntity.getOriginalMessageId());
assertEquals("Unexpected value for disposition mode", DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, messageDispositionNotificationEntity.getDispositionMode());
assertEquals("Unexpected value for disposition type", AS2DispositionType.PROCESSED, messageDispositionNotificationEntity.getDispositionType());
ReceivedContentMic receivedContentMic = messageDispositionNotificationEntity.getReceivedContentMic();
ReceivedContentMic computedContentMic = MicUtils.createReceivedContentMic((HttpEntityEnclosingRequest)request, decryptingKP.getPrivate());
assertEquals("Received content MIC does not match computed", computedContentMic.getEncodedMessageDigest(), receivedContentMic.getEncodedMessageDigest());
}
@Test
public void asyncMDNTest() throws Exception {
AS2AsynchronousMDNManager mdnManager = new AS2AsynchronousMDNManager(AS2_VERSION, ORIGIN_SERVER_NAME, SERVER_FQDN,
certList.toArray(new X509Certificate[0]), signingKP.getPrivate());
// Create plain edi request message to acknowledge
ApplicationEDIEntity ediEntity = EntityUtils.createEDIEntity(EDI_MESSAGE,
ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), null, false);
HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", REQUEST_URI);
HttpMessageUtils.setHeaderValue(request, AS2Header.SUBJECT, SUBJECT);
String httpdate = DATE_GENERATOR.getCurrentDate();
HttpMessageUtils.setHeaderValue(request, AS2Header.DATE, httpdate);
HttpMessageUtils.setHeaderValue(request, AS2Header.AS2_TO, AS2_NAME);
HttpMessageUtils.setHeaderValue(request, AS2Header.AS2_FROM, AS2_NAME);
String originalMessageId = AS2Utils.createMessageId(SERVER_FQDN);
HttpMessageUtils.setHeaderValue(request, AS2Header.MESSAGE_ID, originalMessageId);
HttpMessageUtils.setHeaderValue(request, AS2Header.DISPOSITION_NOTIFICATION_OPTIONS,
DISPOSITION_NOTIFICATION_OPTIONS);
EntityUtils.setMessageEntity(request, ediEntity);
// Create response for MDN creation.
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
httpdate = DATE_GENERATOR.getCurrentDate();
response.setHeader(AS2Header.DATE, httpdate);
response.setHeader(AS2Header.SERVER, REPORTING_UA);
// Create a receipt for edi message
Map<String, String> extensionFields = new HashMap<>();
extensionFields.put("Original-Recipient", "rfc822;" + AS2_NAME);
AS2DispositionModifier dispositionModifier = AS2DispositionModifier.createWarning("AS2 is cool!");
String[] failureFields = new String[] {"failure-field-1"};
String[] errorFields = new String[] {"error-field-1"};
String[] warningFields = new String[] {"warning-field-1"};
DispositionNotificationMultipartReportEntity mdn = new DispositionNotificationMultipartReportEntity(request,
response, DispositionMode.AUTOMATIC_ACTION_MDN_SENT_AUTOMATICALLY, AS2DispositionType.PROCESSED,
dispositionModifier, failureFields, errorFields, warningFields, extensionFields, null, "boundary",
true, serverSigningKP.getPrivate(), "Got your message!");
// Send MDN
@SuppressWarnings("unused")
HttpCoreContext httpContext = mdnManager.send(mdn, RECIPIENT_DELIVERY_ADDRESS);
}
@BeforeClass
public static void setupTest() throws Exception {
setupServerKeysAndCertificates();
receiveTestMessages();
}
@AfterClass
public static void teardownTest() throws Exception {
if (serverConnection != null) {
serverConnection.stopListening("/");
serverConnection.close();
}
}
public static class RequestHandler implements HttpRequestHandler {
private HttpRequest request;
private HttpResponse response;
@Override
public void handle(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
LOG.info("Received test message: " + request);
context.setAttribute(AS2ServerManager.FROM, MDN_FROM);
context.setAttribute(AS2ServerManager.SUBJECT, MDN_SUBJECT_PREFIX);
this.request = request;
this.response = response;
}
public HttpRequest getRequest() {
return request;
}
public HttpResponse getResponse() {
return response;
}
}
private Triple<HttpEntity, HttpRequest, HttpResponse> executeRequest(Map<String, Object> headers) throws Exception {
HttpEntity responseEntity = requestBodyAndHeaders("direct://SEND", EDI_MESSAGE, headers);
return new ImmutableTriple(responseEntity, requestHandler.getRequest(), requestHandler.getResponse());
}
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
public void configure() {
Processor proc = new Processor() {
public void process(org.apache.camel.Exchange exchange) throws Exception {
HttpMessage message = exchange.getIn(HttpMessage.class);
@SuppressWarnings("unused")
String body = message.getBody(String.class);
}
};
// test route for send
from("direct://SEND").to("as2://" + PATH_PREFIX + "/send?inBody=ediMessage");
from("jetty:http://localhost:" + MDN_TARGET_PORT + "/handle-receipts").process(proc);
}
};
}
private static void setupServerKeysAndCertificates() throws Exception {
Security.addProvider(new BouncyCastleProvider());
//
// set up our certificates
//
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(1024, new SecureRandom());
String issueDN = "O=Punkhorn Software, C=US";
KeyPair issueKP = kpg.generateKeyPair();
X509Certificate issueCert = Utils.makeCertificate(
issueKP, issueDN, issueKP, issueDN);
//
// certificate we sign against
//
String signingDN = "CN=William J. Collins, E=punkhornsw@gmail.com, O=Punkhorn Software, C=US";
serverSigningKP = kpg.generateKeyPair();
X509Certificate signingCert = Utils.makeCertificate(
serverSigningKP, signingDN, issueKP, issueDN);
serverCertList = new ArrayList<>();
serverCertList.add(signingCert);
serverCertList.add(issueCert);
}
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(), serverSigningKP.getPrivate());
requestHandler = new RequestHandler();
serverConnection.listen("/", requestHandler);
}
private void setupKeysAndCertificates() throws Exception {
//
// set up our certificates
//
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(1024, new SecureRandom());
String issueDN = "O=Punkhorn Software, C=US";
issueKP = kpg.generateKeyPair();
issueCert = Utils.makeCertificate(
issueKP, issueDN, issueKP, issueDN);
//
// certificate we sign against
//
String signingDN = "CN=William J. Collins, E=punkhornsw@gmail.com, O=Punkhorn Software, C=US";
signingKP = kpg.generateKeyPair();
signingCert = Utils.makeCertificate(
signingKP, signingDN, issueKP, issueDN);
certList = new ArrayList<>();
certList.add(signingCert);
certList.add(issueCert);
// keys used to encrypt/decrypt
decryptingKP = signingKP;
}
}