/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.edi.as2.module.test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.reflect.Method; import java.security.MessageDigest; import java.security.cert.CertStore; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.X509Certificate; import java.util.ArrayList; import javax.activation.CommandMap; import javax.activation.MailcapCommandMap; import javax.mail.internet.InternetHeaders; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import junit.framework.Assert; 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.x509.X509Name; import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; import org.bouncycastle.mail.smime.SMIMESignedGenerator; import org.bouncycastle.util.encoders.Base64; import org.junit.Ignore; import org.junit.Test; import hk.hku.cecid.edi.as2.dao.AS2DAOHandler; import hk.hku.cecid.edi.as2.dao.MessageDAO; import hk.hku.cecid.edi.as2.dao.MessageDVO; import hk.hku.cecid.edi.as2.dao.PartnershipDAO; import hk.hku.cecid.edi.as2.dao.PartnershipDVO; import hk.hku.cecid.edi.as2.dao.RepositoryDVO; import hk.hku.cecid.edi.as2.module.IncomingMessageProcessor; import hk.hku.cecid.edi.as2.pkg.AS2Header; import hk.hku.cecid.edi.as2.pkg.AS2Message; import hk.hku.cecid.edi.as2.pkg.DispositionNotification; import hk.hku.cecid.edi.as2.pkg.DispositionNotificationOption; import hk.hku.cecid.edi.as2.pkg.DispositionNotificationOptions; import hk.hku.cecid.piazza.commons.activation.InputStreamDataSource; import hk.hku.cecid.piazza.commons.io.IOHandler; import hk.hku.cecid.piazza.commons.security.KeyStoreManager; import hk.hku.cecid.piazza.commons.security.SMimeMessage; import hk.hku.cecid.piazza.commons.test.SystemComponentTest; /** * Unit Test IncomingMessageProcessor * * @author Jumbo Cheung * */ @Ignore public class IncomingMessageProcessorTest extends SystemComponentTest<IncomingMessageProcessor> { // Testing Resources private static final String CREATE_TABLE_SQL = "create.sql"; private static final String DROP_TABLE_SQL = "drop.sql"; private static final String MOCK_AS2_MSG = "mock.as2"; private static final String COMPONENT_KEYSTORE_MANAGER = "keystore-manager"; // Variables private PartnershipDVO partnershipDVO; private AS2Message as2Message; private String msgId; private KeyStoreManager keyMan; @Override public String getSystemComponentId() { return "incoming-message-processor"; } // @Before @Override public void setUp() throws Exception { commitSQL(MessageDAO.class, CREATE_TABLE_SQL); LOG.debug("Set up"); //Setting Mail Cap MailcapCommandMap mailcaps = new MailcapCommandMap(); mailcaps.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature"); mailcaps.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime"); mailcaps.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature"); mailcaps.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime"); mailcaps.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed"); mailcaps.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml"); mailcaps.addMailcap("application/deflate;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); mailcaps.addMailcap("message/disposition-notification;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); mailcaps.addMailcap("application/EDI-X12;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); mailcaps.addMailcap("application/EDIFACT;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); mailcaps.addMailcap("application/edi-consent;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); mailcaps.addMailcap("application/XML;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); mailcaps.addMailcap("application/octet-stream;; x-java-content-handler=hk.hku.cecid.piazza.commons.activation.ByteStreamDataContentHandler"); CommandMap.setDefaultCommandMap(mailcaps); //Prepare the Partnership DVO PartnershipDAO partnershipDAO = (PartnershipDAO) TARGET.getDAOFactory().createDAO(PartnershipDAO.class); partnershipDVO = (PartnershipDVO) partnershipDAO.createDVO(); partnershipDVO.setIsDisabled(false); partnershipDVO.setPartnershipId("IncomingMessageProcessorTest"); partnershipDVO.setAs2From("as2From"); partnershipDVO.setAs2To("as2To"); partnershipDVO.setSubject("OutgoingMessageProcessor Unit Test"); partnershipDVO.setIsSyncReply(false); partnershipDVO.setReceiptAddress("http://127.0.0.1:8080/corvus/httpd/as2/inbound"); partnershipDVO.setRecipientAddress("http://127.0.0.1:8080/corvus/httpd/as2/inbound"); partnershipDVO.setIsReceiptRequired(false); partnershipDVO.setIsReceiptSignRequired(true); partnershipDVO.setIsInboundEncryptRequired(false); partnershipDVO.setIsInboundSignRequired(false); partnershipDVO.setIsOutboundCompressRequired(false); partnershipDVO.setIsOutboundEncryptRequired(false); partnershipDVO.setIsOutboundSignRequired(false); partnershipDVO.setSignAlgorithm(PartnershipDVO.ALG_SIGN_SHA1); partnershipDVO.setEncryptAlgorithm(PartnershipDVO.ALG_ENCRYPT_3DES); partnershipDVO.setMicAlgorithm(PartnershipDVO.ALG_MIC_SHA1); partnershipDVO.setVerifyCert(IOHandler.readBytes(FIXTURE_LOADER.getResourceAsStream("security/corvus.cer"))); partnershipDVO.setEncryptCert(IOHandler.readBytes(FIXTURE_LOADER.getResourceAsStream("security/corvus.cer"))); partnershipDAO.create(partnershipDVO); //Initialise AS2 Message msgId = RANDOM.toString(); AS2Message as2Msg = new AS2Message(); as2Msg.setFromPartyID("as2To"); as2Msg.setToPartyID("as2From"); as2Msg.setMessageID(msgId); as2Msg.setHeader(AS2Header.SUBJECT, partnershipDVO.getSubject()); as2Msg.setHeader(AS2Header.RECEIPT_DELIVERY_OPTION, partnershipDVO.getRecipientAddress()); as2Msg.setHeader(AS2Header.DISPOSITION_NOTIFICATION_TO, partnershipDVO.getReceiptAddress()); DispositionNotificationOptions dnos = new DispositionNotificationOptions(); DispositionNotificationOption option = dnos.addOption(DispositionNotificationOptions.SIGNED_RECEIPT_PROTOCOL); option.addValue(DispositionNotificationOption.SIGNED_RECEIPT_PROTOCOL_PKCS7); option = dnos.addOption(DispositionNotificationOptions.SIGNED_RECEIPT_MICALG); option.addValue(SMIMESignedGenerator.DIGEST_SHA1); as2Msg.setHeader(AS2Header.DISPOSITION_NOTIFICATION_OPTIONS, option.toString()); // Set Content to Message InputStream ins = FIXTURE_LOADER.getResourceAsStream(MOCK_AS2_MSG); ByteArrayInputStream bIns = new ByteArrayInputStream(IOHandler.readBytes(ins)); as2Msg.setContent(new InputStreamDataSource(bIns, "xml", MOCK_AS2_MSG), "application/XML"); as2Message = as2Msg; // Initilaize Keystore-Manager keyMan = (KeyStoreManager)TARGET.getComponent(COMPONENT_KEYSTORE_MANAGER); } // @After @Override public void tearDown() throws Exception { LOG.debug("Tear down"); commitSQL(MessageDAO.class, DROP_TABLE_SQL); } @Test public void testSignatureVerfication() { try { PartnershipDAO partnershipDAO = (PartnershipDAO) TARGET .getDAOFactory().createDAO(PartnershipDAO.class); PartnershipDVO dvo = (PartnershipDVO) partnershipDAO.createDVO(); dvo.setPartnershipId("IncomingMessageProcessorTest"); partnershipDAO.retrieve(dvo); Assert.assertEquals("as2From", dvo.getAS2From()); PartnershipDVO pDvo = (PartnershipDVO) (partnershipDAO.findByParty( "as2From", "as2To")); // Caculate MIC value String expectedMIC = calculateMIC(as2Message.getBodyPart()); // Sign message /* * MimeBodyPart signedPart = signMessage(as2Message.getBodyPart()); * as2Message.setBodyPart(encryptedPart); */ SMimeMessage smime = new SMimeMessage(as2Message.getBodyPart(), partnershipDVO.getVerifyX509Certificate(), keyMan .getPrivateKey()); smime.setContentTransferEncoding(SMimeMessage.CONTENT_TRANSFER_ENC_BINARY); smime.setDigestAlgorithm(SMimeMessage.DIGEST_ALG_SHA1); smime = smime.sign(); as2Message.setBodyPart(smime.getBodyPart()); partnershipDVO.setIsInboundSignRequired(true); PartnershipDAO partnershioDAO = (PartnershipDAO) TARGET .getDAOFactory().createDAO(PartnershipDAO.class); partnershioDAO.persist(partnershipDVO); // Insert Message Record to Database AS2DAOHandler daoHandler = new AS2DAOHandler(TARGET.getDAOFactory()); RepositoryDVO requestRepositoryDVO = daoHandler .createRepositoryDVO(as2Message, true); MessageDVO requestMessageDVO = daoHandler.createMessageDVO( as2Message, true); requestMessageDVO.setStatus(MessageDVO.STATUS_RECEIVED); daoHandler.createMessageStore().storeMessage(requestMessageDVO, requestRepositoryDVO); // Invoke Target Method Method m = TARGET.getClass().getDeclaredMethod( "processReceivedMessage", AS2Message.class); m.setAccessible(true); AS2Message responseMsg = (AS2Message) m.invoke(TARGET, as2Message); // check if the response message has been generated Assert.assertNotNull(responseMsg); // Assert the content of the reply DispositionNotification dn = responseMsg .getDispositionNotification(); Assert.assertEquals(msgId, dn.getOriginalMessageID()); Assert.assertNull(dn.getDisposition().getDescription()); Assert.assertNull(dn.getDisposition().getModifier()); Assert.assertEquals(expectedMIC, dn.getReceivedContentMIC()); } catch (Exception exp) { exp.printStackTrace(); Assert.fail(); } } @Test public void testDecryption() throws Exception{ try{ // Caculate MIC value String expectedMIC = calculateMIC(as2Message.getBodyPart()); //Sign message as2Message.setBodyPart(encrypt(as2Message.getBodyPart())); //Update the Partnership Record in Database partnershipDVO.setIsInboundEncryptRequired(true); PartnershipDAO partnershioDAO = (PartnershipDAO) TARGET.getDAOFactory().createDAO(PartnershipDAO.class); partnershioDAO.persist(partnershipDVO); // Insert Message Record to Database AS2DAOHandler daoHandler = new AS2DAOHandler(TARGET.getDAOFactory()); RepositoryDVO requestRepositoryDVO = daoHandler.createRepositoryDVO(as2Message, true); MessageDVO requestMessageDVO = daoHandler.createMessageDVO(as2Message, true); requestMessageDVO.setStatus(MessageDVO.STATUS_RECEIVED); daoHandler.createMessageStore().storeMessage(requestMessageDVO, requestRepositoryDVO); // Invoke Target Method Method m = TARGET.getClass().getDeclaredMethod("processReceivedMessage", AS2Message.class); m.setAccessible(true); AS2Message responseMsg = (AS2Message)m.invoke(TARGET, as2Message); // check if the response message has been generated Assert.assertNotNull(responseMsg); //Assert the content of the reply DispositionNotification dn = responseMsg.getDispositionNotification(); Assert.assertEquals(msgId, dn.getOriginalMessageID()); Assert.assertNull(dn.getDisposition().getDescription()); Assert.assertNull(dn.getDisposition().getModifier()); Assert.assertEquals(expectedMIC, dn.getReceivedContentMIC()); }catch(Exception exp){ exp.printStackTrace(); Assert.fail(); } } @Test public void testDecryptionSignatureVerfication() throws Exception{ try{ // Caculate MIC value String expectedMIC = calculateMIC(as2Message.getBodyPart()); // Update the Partnership Record partnershipDVO.setIsInboundSignRequired(true); partnershipDVO.setIsInboundEncryptRequired(true); PartnershipDAO partnershioDAO = (PartnershipDAO) TARGET.getDAOFactory().createDAO(PartnershipDAO.class); partnershioDAO.persist(partnershipDVO); //Sign message MimeBodyPart signedPart = signMessage(as2Message.getBodyPart()); MimeBodyPart encryptedPart = encrypt(signedPart); as2Message.setBodyPart(encryptedPart); // Insert Message Record to Database AS2DAOHandler daoHandler = new AS2DAOHandler(TARGET.getDAOFactory()); RepositoryDVO requestRepositoryDVO = daoHandler.createRepositoryDVO(as2Message, true); MessageDVO requestMessageDVO = daoHandler.createMessageDVO(as2Message, true); requestMessageDVO.setStatus(MessageDVO.STATUS_RECEIVED); daoHandler.createMessageStore().storeMessage(requestMessageDVO, requestRepositoryDVO); // Invoke Target Method Method m = TARGET.getClass().getDeclaredMethod("processReceivedMessage", AS2Message.class); m.setAccessible(true); AS2Message responseMsg = (AS2Message)m.invoke(TARGET, as2Message); // check if the response message has been generated Assert.assertNotNull(responseMsg); //Assert the content of the reply DispositionNotification dn = responseMsg.getDispositionNotification(); Assert.assertEquals(msgId, dn.getOriginalMessageID()); Assert.assertNull(dn.getDisposition().getDescription()); Assert.assertNull(dn.getDisposition().getModifier()); Assert.assertEquals(expectedMIC, dn.getReceivedContentMIC()); }catch(Exception exp){ exp.printStackTrace(); Assert.fail(); } } private MimeBodyPart signMessage(MimeBodyPart bodyPart) throws Exception{ X509Certificate cert = partnershipDVO.getVerifyX509Certificate(); /* Create the SMIMESignedGenerator */ SMIMECapabilityVector capabilities = new SMIMECapabilityVector(); capabilities.addCapability(SMIMECapability.dES_EDE3_CBC); capabilities.addCapability(SMIMECapability.rC2_CBC, 128); capabilities.addCapability(SMIMECapability.dES_CBC); ASN1EncodableVector attributes = new ASN1EncodableVector(); attributes.add(new SMIMEEncryptionKeyPreferenceAttribute( new IssuerAndSerialNumber(new X509Name(cert.getIssuerDN().getName()), cert.getSerialNumber())) ); attributes.add(new SMIMECapabilitiesAttribute(capabilities)); SMIMESignedGenerator signer = new SMIMESignedGenerator(); signer.setContentTransferEncoding("base64"); signer.addSigner(keyMan.getPrivateKey(), partnershipDVO.getVerifyX509Certificate(), SMIMESignedGenerator.DIGEST_SHA1, new AttributeTable(attributes), null); // Add the list of certs to the generator ArrayList certList = new ArrayList(); certList.add(cert); CertStore certs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC"); signer.addCertificatesAndCRLs(certs); // Sign body part MimeMultipart mm = signer.generate(bodyPart, "BC"); InternetHeaders headers = new InternetHeaders(); boolean isContentTypeFolded = new Boolean(System.getProperty("mail.mime.foldtext","true")).booleanValue(); headers.setHeader("Content-Type", isContentTypeFolded? mm.getContentType():mm.getContentType().replaceAll("\\s", " ")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mm.writeTo(baos); MimeBodyPart signedPart = new MimeBodyPart(headers, baos.toByteArray()); return signedPart; } private MimeBodyPart encrypt(MimeBodyPart bodyPart) throws Exception{ // Create Encrypter SMIMEEnvelopedGenerator encrypter = new SMIMEEnvelopedGenerator(); encrypter.setContentTransferEncoding("base64"); encrypter.addKeyTransRecipient(partnershipDVO.getEncryptX509Certificate()); // Encrypt BodyPart MimeBodyPart encryptedPart = encrypter.generate(bodyPart, SMIMEEnvelopedGenerator.DES_EDE3_CBC, "BC"); return encryptedPart; } private String calculateMIC(MimeBodyPart bodyPart) throws Exception{ // By default, MIC calculate with Headers ByteArrayOutputStream contentBAOS = new ByteArrayOutputStream(); bodyPart.writeTo(contentBAOS); byte[] content = (contentBAOS.toByteArray()); MessageDigest md = MessageDigest.getInstance( SMIMESignedGenerator.DIGEST_SHA1, "BC"); md.update(content); byte[] digest = md.digest(); String digestString = new String(Base64.encode(digest)); return digestString + ", " + DispositionNotificationOption.SIGNED_RECEIPT_MICALG_SHA1; } }