設計 AS2 協議的主要目的,是基於 HTTP 協議之上實現安全的結構化電子商業數據交換。在這系列文章的第一部分,咱們大致瞭解了 AS2 爲什麼這麼優秀。咱們做爲 B2B 集成平臺 AS2Gateway 的開發者,已經在 AS2 協議這方面工做了不少年。在本篇文章中,咱們但願給予更多的看法關於 AS2 協議,如何使用幾行 java 代碼和 S/MIME 格式去構造一個 AS2 消息。java
廢話很少說,讓咱們如今開始。AS2 消息的基本結構:他由 MIME 格式數據組成,並存在於 HTTP 消息體裏面,再加上一些特有的 AS2 消息頭部。
AS2 消息的最終結構以下圖所示。在本文中,咱們會從一個簡單的文檔開始,一步一步生成最終的加密過的 HTTP 消息體。
git
譯者注:咱們看到最外層是 HTTP 數據包,AS2 消息的實際內容 (使用非對稱加密算法加密過的) 是掛載到 HTTP BODY (HTTP請求體) 裏面的。AS2 協議重點就在於如何生成/解析這個 Encrypted HTTP Body (加密過的 HTTP 請求體)。
解密事後的 AS2 消息中還包含了基礎文檔 (Functional Document) 和數字簽名 (Ditital Signature),AS2 協議規定應用軟件須要校驗這個數據簽名 (Digital Signature) 來確保數據完整性,具體作法是算法
首先,讓咱們看一個 MIME 消息樣例。下面的樣例代碼使用了 JavaMail 和 Apache Tika,用來生成一個 MIME 消息編程
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());
生成的 MIME 消息結構以下所示,注意 MIME 的頭部信息和消息內容 (消息內容經過 base64 編碼,由於咱們在頭部指定了該編碼格式)api
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
如今咱們看看 S/MIME 如何發揮做用。S/MIME 提供了兩種安全措施,數字簽名 (Digital Signature) 和信息加密 (Message Encryption)。這兩項措施是 S/MIME 消息安全性的基礎。數字簽名提供身份認證,消息不能否認性以及數據完整性校驗。信息加密服務則提供了數據機密性以及數據完整性。下面的代碼片段展現瞭如何對 MIME 消息進行簽名,這裏咱們使用了 Bouncy Castle S/MIME API, Bouncy Castle Crypto package, 以及 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();
簽署事後,MIME 消息以下如示
譯者注:第一部分爲實際內容 "sample text content one" (經base64編碼),第二部分爲數字簽名session
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--
// 加載partner的數字證書 CertificateFactory fact = CertificateFactory.getInstance("X.509"); FileInputStream is = new FileInputStream("/home/rajind/Downloads/partner-cert.pem"); X509Certificate cert = (X509Certificate) fact.generateCertificate(is); // 建立加密器 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()); // 進行加密 MimeBodyPart encryptedPart = encryptor.generate(finalMessage, jceCMSContentEncryptorBuilder.build()); // 設置加密後的內容 finalMessage = new MimeMessage(session); finalMessage.setContent(encryptedPart.getContent(), encryptedPart.getContentType()); finalMessage.setHeader("Content-Transfer-Encoding", "base64"); finalMessage.saveChanges();
通過簽字 (sign) 和加密 (encrypt) 後,MIME 消息變成了下面那樣app
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=
你可能注意到上面代碼片斷中硬編碼的證書和算法。在實際的 AS2 B2B 通訊場景中,這些參數須要很是方便去配置。
下一步就是要加入與 AS2 有關的 HTTP 頭部,並把消息發送出去,接着是解析收到的消息。這些將會在將來的文章中介紹。dom
P.S. 請注意上述代碼片段只是給你一個關於 AS2 消息處理的初印象,他們也許不符合編程標準,也沒有異步處理。異步