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 数字签名、验签的实现过程。如遇到任何问题,欢迎留言。