Pkcs7Attach 数字签名、验签、解密

Attached模式,又称P7A,即签名值中包含被签名的原文,但明文必须经过ASN.1编码。

实现步骤如下:

第一步:创建证书

生成keystore文件
keytool -genkey -alias ynhr -keyalg RSA -keysize 1024 -keypass yw@123 -validity 36500 -keystore test.keystore -storepass yw@123

导出私钥文件
keytool -importkeystore -srcstoretype JKS -srckeystore test.keystore -srcstorepass yw@123 -srcalias ynhr -srckeypass yw@123 -deststoretype PKCS12 -destkeystore keystore.p12 -deststorepass yw@123 -destalias client -destkeypass yw@123 -noprompt

相关证书的创建在网上随处都可以找到。

第二步:加密解密实现

这里我直接放上源码,之前在网上找了一些,但是结果都不是太全,后来自己通过多次实验,并用于项目实践。

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;

import java.io.File;
import java.io.FileInputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.*;
@Slf4j
public class Pkcs7AttachUtil {

    /**
     * 加密算法
     */
    private static final String KEY_ALGORITHM = "SHA1withRSA";

    public static void main(String[] args) throws Exception {
        //加载密钥库文件
        String pfxFile = System.getProperty("user.dir")+"/distfile/keystore.p12";
        String password = "yw@123";
        String content = "你好";
        String signValue = Pkcs7AttachUtil.signature(content,pfxFile,password);
        System.out.println("Signature: " + signValue);

//        String signValue = "MIAGCSqGSIb3DQEHAqCAMIACAQExDTALBglghkgBZQMEAgEwgAYJKoZIhvcNAQcBoIAEBuS9oOWlvQAAAACggDCCAj4wggGnoAMCAQICBCRRhwAwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCODYxCzAJBgNVBAgTAllOMQswCQYDVQQHEwJLTTENMAsGA1UEChMEVEVTVDEMMAoGA1UECxMDSlVOMQswCQYDVQQDEwJMSTAgFw0yNDA1MDgwNjQ4NTdaGA8yMTI0MDQxNDA2NDg1N1owUTELMAkGA1UEBhMCODYxCzAJBgNVBAgTAllOMQswCQYDVQQHEwJLTTENMAsGA1UEChMEVEVTVDEMMAoGA1UECxMDSlVOMQswCQYDVQQDEwJMSTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAseM4bZyOUZuVgn/XOM1sHsJiFIfIq/Isl1XUUkWM/bMwPWnelJsQYlU1pCgDr2fQLF1n9rfOpjllcENbqLc1L+uSqyAlLjvpR27h+1AstBh5HcUWbE/w+yg7ZCccBoLXh5VCCRnrUE0U1N+gp+GcJ4Sq6PkfsFqGhDqkLfd6/KkCAwEAAaMhMB8wHQYDVR0OBBYEFI5BuV/Tpa+sbKFP70uASkpoKdqgMA0GCSqGSIb3DQEBCwUAA4GBAImqJzPoihCV01Eq9JRYvOFEMr7ykilYeOmbu+1esQChawe9oX7S//WpIxitMFMr5cTnXIPfevMwfb9kelboecXrwBaA5QIEEmCpC4qjFS4u7wHWB4o/E0u/DQM/vnX2ssyw7bm9K9H13fiAZD4BWWxBXs7fuOExoqr6o3ugNJOOAAAxggEAMIH9AgEBMFkwUTELMAkGA1UEBhMCODYxCzAJBgNVBAgTAllOMQswCQYDVQQHEwJLTTENMAsGA1UEChMEVEVTVDEMMAoGA1UECxMDSlVOMQswCQYDVQQDEwJMSQIEJFGHADALBglghkgBZQMEAgEwDQYJKoZIhvcNAQELBQAEgYBqfbiUaGili1vCqoSUdYPURU7NFYygjXktGAfu/AuKg+lXsQUvJSAx50v8OgBHL2OTvyCRni+P2K/sZ2TVvfZOJSkBXrvJ5ffXPQ8D1gJtzGE0xiKO9C718T1poRa6oVgCrrhan66yUsohkM/Ul6fgdaL6MAk+/TmvprIFHBqR0wAAAAAAAA==";

        Pkcs7AttachUtil.unsign(Base64.getDecoder().decode(signValue));
        boolean flag = Pkcs7AttachUtil.verifySignature(Base64.getDecoder().decode(signValue),pfxFile,password);
        System.out.println(flag);
    }

    /**
     * Pkcs7Detach 签名
     * @param content 内容
     * @param pfxFile 证书路径
     * @param password 证书密码
     * @return
     */
    public static String signature(String content,String pfxFile,String password){
        if (StringUtils.isEmpty(content)) {
            return "加密内容为空";
        }
        if (StringUtils.isEmpty(pfxFile)) {
            return "证书路径为空";
        }
        if (StringUtils.isEmpty(password)) {
            return "证书密码为空";
        }
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            //创建密钥对
            KeyStore keyStore = KeyStore.getInstance("PKCS12");

            File file = new File(pfxFile);
            FileInputStream fis = new FileInputStream(file);
            keyStore.load(fis, password.toCharArray());
            fis.close();

            //获取别名
            Enumeration enumas = keyStore.aliases();
            String alias = null;
            while (enumas.hasMoreElements()) {
                alias = (String) enumas.nextElement();
            }

            //准备密钥对
            Key key = keyStore.getKey(alias, password.toCharArray());
            KeyPair keyPair = null;
            Certificate cert = null;
            if (key instanceof PrivateKey) {
                //获取用户证书
                cert = keyStore.getCertificate(alias);
                PublicKey publicKey = cert.getPublicKey();
                //初始化密钥对和证书
                keyPair = new KeyPair(publicKey, (PrivateKey) key);
            }

            // 需要被签名的数据
            byte[] dataToSign = content.getBytes();

            // 创建Signer信息
            CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator();

            JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());

            JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(KEY_ALGORITHM);
            X509Certificate cerx509 = (X509Certificate) cert;
            List<X509Certificate> certList = new ArrayList<>();
            certList.add(cerx509);
            Store certs = new JcaCertStore(certList);
            infoGeneratorBuilder.build(contentSignerBuilder.build(keyPair.getPrivate()), cerx509);
            ContentSigner contentSigner = new JcaContentSignerBuilder(cerx509.getSigAlgName()).setProvider("BC").build(keyPair.getPrivate());
            signedDataGenerator.addSignerInfoGenerator(infoGeneratorBuilder.setDirectSignature(true).build(contentSigner, cerx509));
            signedDataGenerator.addCertificates(certs);
            // 添加内容和设置Pkcs7Detach方式
            CMSSignedData signedData = signedDataGenerator.generate(new CMSProcessableByteArray(dataToSign), true);

            // 输出签名结果
            byte[] signature = signedData.getEncoded();
            log.info("Signature: " + Base64.getEncoder().encodeToString(signature));
            log.info("Pkcs7Attach 签名成功:{}",Base64.getEncoder().encodeToString(signature));
            return Base64.getEncoder().encodeToString(signature);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("签名异常:"+e.getMessage());
        }
        return null;
    }

    /**
     * 验签
     * @param var1 加密信息
     * @param pfxFile 证书路径
     * @param password 证书密码
     * @return
     */
    public static boolean verifySignature(byte[] var1,String pfxFile,String password ) {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            //创建密钥对
            KeyStore keyStore = KeyStore.getInstance("PKCS12");

            File file = new File(pfxFile);
            FileInputStream fis = new FileInputStream(file);
            keyStore.load(fis, password.toCharArray());
            fis.close();

            //获取别名
            Enumeration enumas = keyStore.aliases();
            String alias = null;
            while (enumas.hasMoreElements()) {
                alias = (String) enumas.nextElement();
            }

            //准备密钥对
            Key key = keyStore.getKey(alias, password.toCharArray());
            Certificate cert = null;
            if (key instanceof PrivateKey) {
                //获取用户证书
                cert = keyStore.getCertificate(alias);
            }

            // 加载已签名的数据
            CMSSignedData sigData = new CMSSignedData(var1);

            // 创建SignerInformationVerifier
            SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder()
                    .setProvider("BC") // 使用BouncyCastle作为安全提供者
                    .build(cert.getPublicKey());

            // 验证签名
            Collection<SignerInformation> signers = sigData.getSignerInfos().getSigners();
            for (SignerInformation signer : signers) {
                if (signer.verify(verifier)) {
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("验签失败:"+e.getMessage());
        }

        return false;
    }

    /**
     * 解密
     * @param signedBytes 签名内容
     * @return
     */
    public static String unsign(byte[] signedBytes) {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            String signedStr = Base64.getEncoder().encodeToString(signedBytes);
            CMSSignedData signedData = new CMSSignedData(Base64.getDecoder().decode(signedStr.getBytes()));
            Store store = signedData.getCertificates();
            SignerInformationStore signers = signedData.getSignerInfos();
            Collection<SignerInformation> coll = signers.getSigners();
            Iterator<SignerInformation> it = coll.iterator();
            StringBuilder content = new StringBuilder();
            while(it.hasNext()) {
                SignerInformation signer = it.next();
                Collection certs = store.getMatches(signer.getSID());
                Iterator certIt = certs.iterator();
                X509CertificateHolder holder = (X509CertificateHolder)certIt.next();
                X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder);
                boolean result = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
                if(result) {
                    CMSProcessable sc = signedData.getSignedContent();
                    byte[] data = (byte[])sc.getContent();
                    content.append(new String(data));
                }
            }
            log.info("oragin content: "+ content);
            log.info("Pkcs7Attach 解密成功:{}", content);
            return content.toString();
        } catch (Exception e) {
            log.error("解密失败:"+e.getMessage());
        }

        return null;
    }

}

以上就是Pkcs7Attach 数字签名、验签的实现过程。如遇到任何问题,欢迎留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值