AS2 Protocol for Business Data Interchange via HTTP, Part 2

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.

as2 protocol for b2b integration

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/

Leave a comment