zoukankan      html  css  js  c++  java
  • 【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());
        }
    
    
    }
  • 相关阅读:
    oracle 12c迁移数据及遇到的问题
    用最有效率的方法计算 2 乘以 8?
    Java 中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
    是否可以在 static 环境中访问非 static 变量?
    什么是值传递和引用传递?
    Java 支持的数据类型有哪些?
    JDK、JRE、JVM 分别是什么关系?
    idea设置方法注释
    Java中&和&&的区别
    int和Integer有什么区别?
  • 原文地址:https://www.cnblogs.com/qiuxiangmuyu/p/6195215.html
Copyright © 2011-2022 走看看