The main idea of the AS2 protocol defined in the RFC 4130 is on how we can exchange structured business data securely using the HTTP transfer protocol. In part one of the AS2 article series, we looked at an overview of the AS2 (Applicability Statement 2) protocol and why it wins over the other B2B protocols. We, the developers of the B2B integration platform AS2Gateway, have been working with AS2 protocol for quite some time now and today in this article we hope to give away some insight on how an AS2 message is composed using S/MIME format with a few lines of Java code.
Without further ado, let’s jump right in. The basic structure of an AS2 message consists of a MIME format inside an HTTP message with a few additional specific AS2 headers. The final structure of the composed AS2 message will be as follows. In this article, we’ll go in a step-by-step manner to create the final encrypted HTTP body starting from a document.
MIME Message Generation
First of all, we’ll look at a sample MIME message. Following code sample can be used to generate a MIME message in Java mainly using JavaMail.
Properties props = System.getProperties(); Session session = Session.getDefaultInstance(props, null); MimeMessage finalMessage = new MimeMessage(session); Tika tika = new Tika(); File file = new File("/home/rajind/sample-text-file.txt"); String mimeType = tika.detect(file); finalMessage.setDataHandler(new DataHandler(new FileDataSource(file))); finalMessage.setHeader("Content-Type", mimeType); finalMessage.setHeader("Content-Transfer-Encoding", "base64"); finalMessage.setFileName(file.getName());
The structure of the output MIME message will look as follows. Note the MIME headers and then the content (which is base64 encoded since we have used the content transfer encoding as base64).
Message-ID: <1642534850.0.1512980924095@rajind-ENVY> MIME-Version: 1.0 Content-Type: text/plain; name=sample-text-file.txt Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=sample-text-file.txt c2FtcGxlIHRleHQgY29udGVudCBvbmUK
Signing the MIME Message
Now we’ll look at how S/MIME comes into play. S/MIME provides two security services, namely Digital Signatures and Message Encryption. These two services are the core of S/MIME-based message security. Digital signatures provide, Authentication, Nonrepudiation and Data Integrity while encryption provides Confidentiality and again Data Integrity. The following snippet shows how the signing of a MIME message occurs. Here we have used Bouncy Castle S/MIME API, Bouncy Castle Crypto package and Bouncy Castle Java APIs for CMS, PKCS, EAC, TSP, CMP, CRMF, OCSP, and certificate generation.
// loading identity store FileInputStream is = new FileInputStream("/home/rajind/Downloads/keystore.jks"); KeyStore identityKeystore = KeyStore.getInstance(KeyStore.getDefaultType()); String password = "password"; identityKeystore.load(is, password.toCharArray()); // extracting certificate from identity store X509Certificate signCert = (X509Certificate) identityKeystore.getCertificate("as2gx"); List certList = new ArrayList(); certList.add(signCert); Store certs = new JcaCertStore(certList); // create the generator for creating an smime/signed message SMIMESignedGenerator signer = new SMIMESignedGenerator(); signer.setContentTransferEncoding("base64"); // extracting private key from identity store Key key = identityKeystore.getKey("as2gx", password.toCharArray()); KeyPair keyPair; if (key instanceof PrivateKey) { Certificate cert = identityKeystore.getCertificate("as2gx"); PublicKey publicKey = cert.getPublicKey(); keyPair = new KeyPair(publicKey, (PrivateKey) key); } else { throw new UnrecoverableKeyException("Identity store does not contain keypair for alias " + "as2gx"); } // add a signer to the generator signer.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC") .build("SHA1WITHRSA", keyPair.getPrivate(), signCert)); // add our pool of certs and certs (if any) to go with the signature signer.addCertificates(certs); MimeMultipart signedMimeMultipart = signer.generate(finalMessage, "BC"); finalMessage = new MimeMessage(session); // set the content of the signed message finalMessage.setContent(signedMimeMultipart); finalMessage.saveChanges();
After signing the MIME message will look as follows.
Message-ID: <1990160809.3.1512983999570@rajind-ENVY> MIME-Version: 1.0 Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-1; boundary="----=_Part_2_77269878.1512983999569" ------=_Part_2_77269878.1512983999569 Content-Type: text/plain; name=sample-text-file.txt Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=sample-text-file.txt c2FtcGxlIHRleHQgY29udGVudCBvbmUK ------=_Part_2_77269878.1512983999569 Content-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="smime.p7s" Content-Description: S/MIME Cryptographic Signature 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 agAAAAAAAA== ------=_Part_2_77269878.1512983999569--
Encrypting the MIME Message
Then the following snippet can be used for encryption.
// loading public cert of partner CertificateFactory fact = CertificateFactory.getInstance("X.509"); FileInputStream is = new FileInputStream("/home/rajind/Downloads/partner-cert.pem"); X509Certificate cert = (X509Certificate) fact.generateCertificate(is); // create the encryptor SMIMEEnvelopedGenerator encryptor = new SMIMEEnvelopedGenerator(); encryptor.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC")); encryptor.setContentTransferEncoding("base64"); JceCMSContentEncryptorBuilder jceCMSContentEncryptorBuilder = new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(SMIMEEnvelopedGenerator.DES_EDE3_CBC)).setProvider("BC"); jceCMSContentEncryptorBuilder.setSecureRandom(new SecureRandom()); // encrypting MimeBodyPart encryptedPart = encryptor.generate(finalMessage, jceCMSContentEncryptorBuilder.build()); // setting encrypted content finalMessage = new MimeMessage(session); finalMessage.setContent(encryptedPart.getContent(), encryptedPart.getContentType()); finalMessage.setHeader("Content-Transfer-Encoding", "base64"); finalMessage.saveChanges();
After both signing and encryption, the composed MIME message will look as follows.
Message-ID: <347808407.5.1512984099462@rajind-ENVY> MIME-Version: 1.0 Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type=enveloped-data Content-Transfer-Encoding: base64 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 AAAAAAA=
That’s all for composing a MIME message with a single attachment. You might notice that there are various parameters like sign and encrypt certificates, and algorithms which were hardcoded in above snippets. In a real-world AS2 B2B communication scenario these values need to be easily configurable. AS2Gateway provides all these features with a set of simple UI configurations. Sign up and give it a try as well.
The next steps would be simply adding the AS2 related headers and sending the message over, and then parsing a received message. We’ll cover these in upcoming articles of the series.
P.S. Please note that the code snippets are just to give a quick idea of the process and they may not contain proper coding standards or exception handling.
References:
AS2 Protocol for Business Data Interchange via HTTP, Part 1 – https://notebookbft.wordpress.com/2017/06/09/as2-protocol-for-business-data-interchange-via-http-part-1-overview/