使用Bouncy Castle生成數字簽名、數字信封
阿新 • • 發佈:2019-01-01
Bouncy Castle(輕量級密碼術包)是一種用於 Java 平臺的開放原始碼的輕量級密碼術包,它支援大量的密碼術演算法,並提供 JCE 1.2.1 的實現。最近專案上正好用到了Bouncy Castle,用於生成數字簽名、數字信封,去網上找了很久,都沒有找到合適的案例,而Bouncy Castle本身的文件也不多,最有用的就是官網上的Java Doc文件,因為這個問題也困擾了我好幾天,最後還是通過閱讀Java Doc文件找到了合適的類和方法,果然閱讀Doc文件還是很有必要的啊。好了,話不多說,把我寫的方法列出來,以防忘記,並給有同樣需求的同學提供一些參考,其中有些程式碼也是參考了網上的寫法,最有用的還是Doc文件裡提供的一些示例寫法,基本的需求已經能夠滿足了。
要使用Bouncy Castle,就需要引入相應的jar包,在官網就可以根據自己的需要進行下載,然後就可以使用了。
import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Provider; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSEnvelopedData; import org.bouncycastle.cms.CMSEnvelopedDataGenerator; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.CMSTypedData; import org.bouncycastle.cms.RecipientInformation; import org.bouncycastle.cms.RecipientInformationStore; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder; import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.Store; import org.bouncycastle.util.encoders.Base64; public class MessageUtil { private String ksType = "PKCS12"; /** * 生成數字簽名 * @param srcMsg 源資訊 * @param charSet 字元編碼 * @param certPath 證書路徑 * @param certPwd 證書密碼 * @return */ public byte[] signMessage(String srcMsg, String charSet, String certPath, String certPwd) { String priKeyName = null; char passphrase[] = certPwd.toCharArray(); try { Provider provider = new BouncyCastleProvider(); // 新增BouncyCastle作為安全提供 Security.addProvider(provider); // 載入證書 KeyStore ks = KeyStore.getInstance(ksType); ks.load(new FileInputStream(certPath), passphrase); if (ks.aliases().hasMoreElements()) { priKeyName = ks.aliases().nextElement(); } Certificate cert = (Certificate) ks.getCertificate(priKeyName); // 獲取私鑰 PrivateKey prikey = (PrivateKey) ks.getKey(priKeyName, passphrase); X509Certificate cerx509 = (X509Certificate) cert; List<Certificate> certList = new ArrayList<Certificate>(); certList.add(cerx509); CMSTypedData msg = (CMSTypedData) new CMSProcessableByteArray( srcMsg.getBytes(charSet)); Store certs = new JcaCertStore(certList); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); ContentSigner sha1Signer = new JcaContentSignerBuilder( "SHA1withRSA").setProvider("BC").build(prikey); gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder( new JcaDigestCalculatorProviderBuilder().setProvider("BC") .build()).build(sha1Signer, cerx509)); gen.addCertificates(certs); CMSSignedData sigData = gen.generate(msg, true); return Base64.encode(sigData.getEncoded()); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 驗證數字簽名 * @param signedData * @return */ public boolean signedDataVerify(byte[] signedData) { boolean verifyRet = true; try { // 新建PKCS#7簽名資料處理物件 CMSSignedData sign = new CMSSignedData(signedData); // 新增BouncyCastle作為安全提供 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // 獲得證書資訊 Store certs = sign.getCertificates(); // 獲得簽名者資訊 SignerInformationStore signers = sign.getSignerInfos(); Collection c = signers.getSigners(); Iterator it = c.iterator(); // 當有多個簽名者資訊時需要全部驗證 while (it.hasNext()) { SignerInformation signer = (SignerInformation) it.next(); // 證書鏈 Collection certCollection = certs.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder) certIt .next(); // 驗證數字簽名 if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder() .setProvider("BC").build(cert))) { verifyRet = true; } else { verifyRet = false; } } } catch (Exception e) { verifyRet = false; e.printStackTrace(); System.out.println("驗證數字簽名失敗"); } return verifyRet; } /** * 加密資料 * @param srcMsg 源資訊 * @param certPath 證書路徑 * @param charSet 字元編碼 * @return * @throws Exception */ public String envelopeMessage(String srcMsg, String certPath, String charSet) throws Exception { CertificateFactory certificatefactory; X509Certificate cert; // 使用公鑰對對稱金鑰進行加密 //若此處不加引數 "BC" 會報異常:CertificateException - certificatefactory = CertificateFactory.getInstance("X.509", "BC"); // 讀取.crt檔案;你可以讀取絕對路徑檔案下的crt,返回一個InputStream(或其子類)即可。 InputStream bais = new FileInputStream(certPath); cert = (X509Certificate) certificatefactory.generateCertificate(bais); //新增數字信封 CMSTypedData msg = new CMSProcessableByteArray(srcMsg.getBytes(charSet)); CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator(); edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator( cert).setProvider("BC")); CMSEnvelopedData ed = edGen.generate(msg, new JceCMSContentEncryptorBuilder(PKCSObjectIdentifiers.rc4) .setProvider("BC").build()); String rslt = new String(Base64.encode(ed.getEncoded())); System.out.println(rslt); return rslt; } /** * 解密資料 * @param encode 加密後的密文 * @param certPath 證書路徑 * @param certPwd 證書密碼 * @param charSet 字元編碼 * @return * @throws Exception */ public String openEnvelope(String encode, String certPath, String certPwd, String charSet) throws Exception { //獲取密文 CMSEnvelopedData ed = new CMSEnvelopedData(Base64.decode(encode.getBytes())); RecipientInformationStore recipients = ed.getRecipientInfos(); Collection c = recipients.getRecipients(); Iterator it = c.iterator(); // 載入證書 KeyStore ks = KeyStore.getInstance(ksType); ks.load(new FileInputStream(certPath), certPwd.toCharArray()); String priKeyName = null; if (ks.aliases().hasMoreElements()) { priKeyName = ks.aliases().nextElement(); } // 獲取私鑰 PrivateKey prikey = (PrivateKey) ks.getKey(priKeyName, certPwd.toCharArray()); byte[] recData = null; //解密 if (it.hasNext()) { RecipientInformation recipient = (RecipientInformation) it.next(); recData = recipient.getContent(new JceKeyTransEnvelopedRecipient( prikey).setProvider("BC")); } return new String(recData, charSet); } public MessageUtil() { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } }