【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());
} }
【Java密码学】使用Bouncy Castle生成数字签名、数字信封的更多相关文章
- JAVA 解密pkcs7(smime.p7m)加密内容 ,公钥:.crt 私钥:.pem 使用Bouncy Castle生成数字签名、数字信封
第三方使用公钥.crt加密后返回的内容,需要使用私钥解密.pem 返回内容格式如下 MIME-Version: 1.0 Content-Disposition: attachment; filenam ...
- 在C#中保存Bouncy Castle生成的密钥对
在用Bouncy Castle的C#版API产生公钥和私钥 中产生了一对密钥对,可以用bouncy caslte提供的API进行保存 公钥方面的3个类,具体代码根据命名空间自行查看其源代码: Org. ...
- Java 密码学算法
Java 密码学算法 候捷老师在< 深入浅出MFC 2e(电子版)>中引用林语堂先生的一句话: 只用一样东西,不明白它的道理,实在不高明 只知道How,不知道Why,出了一点小问题时就无能 ...
- 用Bouncy Castle的C#版API产生公钥和私钥
开源API链接地址:The Legion of the Bouncy Castle Bouncy Castle,简称为BC,原本是java的一个开源JCE提供者,后来也提供了C#版本的API,我下载其 ...
- 加密 bouncy castle
1.去官方站点下载Bouncy Castle的JCE Provider包 bcprov-ext-jdk15-145.jar 2.把jar文件复制到 $JAVA_HOME$\jre\lib\ext 目录 ...
- Java Security:公钥私钥、数字签名、消息摘要是什么
1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥. 2. 鲍勃把公钥送给他的朋友们----帕蒂.道格.苏珊----每人一把. 3. 苏珊要给鲍勃写一封保密的信.她写完后用鲍勃的公钥加密,就可以达到保密的 ...
- bouncy castle的配置
Bouncy Castle 是一种用于 Java 平台的开放源码的轻量级密码术包.它支持大量的密码术算法,并提供 JCE 1.2.1 的实现.因为 Bouncy Castle 被设计成轻量级的,所以从 ...
- java工具类(三)之生成若干位随机数
java 生成若干位随机数的问题 在一次编程的过程中偶然碰到一个小问题,就是需要生成一个4位数的随机数,如果是一个不到4位大的数字,前面可以加0来显示.因为要求最后是一个4位的整数,不带小数点.当时就 ...
- Bouncy Castle Crypto API c# port
Bouncy Castle 是一种用于 Java 平台的开放源码的轻量级密码术包.它支持大量的密码术算法,并提供 JCE 1.2.1 的实现.现在有了C#的版本.下面是网站上的介绍 This port ...
随机推荐
- 【转】 Pro Android学习笔记(五十):ActionBar(3):搜索条
目录(?)[-] ActionBar中的搜索条 通过Menu item上定义search view 进行Searchable的配置 在activity中将search view关联searchable ...
- JAVA 编程思想三
1:JAVA可变参数? 参数个数不确定,但是类型确定: 可变参数位于最后一项,只支持一个可变参数: public void funciton1(int a, String ...args) { for ...
- MD5算法的c++实现
需要注意的几点: (1)md5存取的数据长度仅为64位,位于数据的最前端,大于令其自然溢出. (2)update函数和final函数处理得很繁琐,需要仔细分析. (3)16位md5码取32位md5码的 ...
- JVM原理解析
JVM主要的功能: 内存分配 程序调度 内存释放(栈等自动释放.堆垃圾回收) 异常处理 https://www.cnblogs.com/dingyingsi/p/3760447.html https: ...
- 使用 Node.js 实现简单的 Webhook
距离 Node.js 这个东西出来已经过了好久了,感觉现在的前端如果不会点 Node.js 就有点太落后于时代啦.我接触它是从去年暑假开始的,当时在写一个比较神奇的东西,就顺便接触了一下.虽然网传 n ...
- 我的笔记文档版本控制系统-MediaWiki-回到顶部/链接放大/升级
为了练习自己的JS.CSS基本功,这些天和MediaWiki干上了!^_^ 下面是我的MediaWiki新添加的功能: 回到顶部 链接放大 MediaWiki升级 回到顶部 回到顶部是很多网站的基本功 ...
- C# 绘制图表(柱状图,线性图,饼状图)
http://blog.csdn.net/gisfarmer/article/details/3736452 Chart饼状图,每根柱子的宽度: int a = Chart1.Series[" ...
- Mathematics Base - 期望、方差、协方差、相关系数总结
参考:<深度学习500问> 期望 在概率论和统计学中,数学期望(或均值,亦简称期望)是试验中每次可能结果的概率乘以其结果的总和.它反映随机变量平均取值的大小. 线性运算: \(E(ax+ ...
- python测试模块-pytest介绍
1.pytest介绍 pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高. 它具有如下特点: •非常容易 ...
- 洛谷P1282 多米诺骨牌
P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S ...