【Java密码学】使用Bouncy Castle生成数字签名、数字信封
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()); } }