blob: e199217e04b3e4160280907e0b8988ff06ec472c [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.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.List;
import java.util.concurrent.TimeUnit;
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.AS2Header;
import org.apache.camel.component.as2.api.AS2MediaType;
import org.apache.camel.component.as2.api.AS2MessageStructure;
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.ApplicationPkcs7SignatureEntity;
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;
import org.apache.camel.component.as2.internal.AS2ServerManagerApiMethod;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpVersion;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.bouncycastle.asn1.ASN1EncodableVector;
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.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test class for {@link org.apache.camel.component.as2.api.AS2ServerManager} APIs.
*/
public class AS2ServerManagerIntegrationTest extends AbstractAS2TestSupport {
private static final Logger LOG = LoggerFactory.getLogger(AS2ServerManagerIntegrationTest.class);
private static final String PATH_PREFIX = AS2ApiCollection.getCollection().getApiName(AS2ServerManagerApiMethod.class).getName();
private static final String METHOD = "POST";
private static final String TARGET_HOST = "localhost";
private static final int TARGET_PORT = 8888;
private static final String AS2_VERSION = "1.1";
private static final String USER_AGENT = "Camel AS2 Endpoint";
private static final String REQUEST_URI = "/";
private static final String AS2_NAME = "878051556";
private static final String SUBJECT = "Test Case";
private static final String FROM = "mrAS@example.org";
private static final String CLIENT_FQDN = "example.org";
private static final String DISPOSITION_NOTIFICATION_TO = "mrAS@example.org";
private static final String[] SIGNED_RECEIPT_MIC_ALGORITHMS = new String[] {"sha1", "md5"};
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'";
private AS2SignedDataGenerator gen;
private KeyPair issueKP;
private X509Certificate issueCert;
private KeyPair signingKP;
private X509Certificate signingCert;
private List<X509Certificate> certList;
@Test
public void receivePlainEDIMessageTest() 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.PLAIN,
ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), null, null, null,
DISPOSITION_NOTIFICATION_TO, SIGNED_RECEIPT_MIC_ALGORITHMS);
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(AS2MediaType.APPLICATION_EDIFACT));
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 ApplicationEDIFACTEntity);
ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) entity;
assertTrue("Unexpected content type for entity", ediEntity.getContentType().getValue().startsWith(AS2MediaType.APPLICATION_EDIFACT));
assertTrue("Entity not set as main body of request", ediEntity.isMainBody());
String rcvdMessage = ediEntity.getEdiMessage().replaceAll("\r", "");
assertEquals("EDI message does not match", EDI_MESSAGE, rcvdMessage);
}
@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);
clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME, AS2MessageStructure.SIGNED,
ContentType.create(AS2MediaType.APPLICATION_EDIFACT, AS2Charset.US_ASCII), null,
certList.toArray(new Certificate[0]), signingKP.getPrivate(), DISPOSITION_NOTIFICATION_TO,
SIGNED_RECEIPT_MIC_ALGORITHMS);
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(AS2MediaType.MULTIPART_SIGNED));
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 MultipartSignedEntity);
MultipartSignedEntity signedEntity = (MultipartSignedEntity)entity;
assertTrue("Entity not set as main body of request", signedEntity.isMainBody());
assertTrue("Request contains invalid number of mime parts", signedEntity.getPartCount() == 2);
// Validated first mime part.
assertTrue("First mime part incorrect type ", signedEntity.getPart(0) instanceof ApplicationEDIFACTEntity);
ApplicationEDIFACTEntity ediEntity = (ApplicationEDIFACTEntity) signedEntity.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 ", signedEntity.getPart(1) instanceof ApplicationPkcs7SignatureEntity);
ApplicationPkcs7SignatureEntity signatureEntity = (ApplicationPkcs7SignatureEntity) signedEntity.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());
// Validate Signature
assertTrue("Signature is invalid", signedEntity.isValid());
}
private void setupSigningGenerator() throws Exception {
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));
gen = SigningUtils.createSigningGenerator(certList.toArray(new X509Certificate[0]), signingKP.getPrivate());
gen.addCertificates(certs);
}
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);
}
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
public void configure() {
// test route for listen
from("as2://" + PATH_PREFIX + "/listen?requestUriPattern=/")
.to("mock:as2RcvMsgs");
}
};
}
}