1. 概述
BouncyCastle 是一个补充Java默认加密扩展(JCE)的Java库。
本文将介绍如何使用BouncyCastle执行加密操作,如加密和签名。
2. Maven配置
开始使用前,需在pom.xml
中添加依赖:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.76</version>
</dependency>
⚠️ 最新版本可在Maven中央仓库查询。
3. 配置无限强度管辖策略文件
标准Java安装对加密强度有限制(如AES密钥长度不超过128位)。要突破限制,需配置无限强度管辖策略文件。
操作步骤:
- 从Oracle官网下载策略文件包
- 解压后得到两个JAR文件:
local_policy.jar
US_export_policy.jar
- 替换
{JAVA_HOME}/lib/security
目录下的同名文件
✅ Java 9+无需下载,直接设置属性即可:
Security.setProperty("crypto.policy", "unlimited");
验证配置是否生效:
int maxKeySize = javax.crypto.Cipher.getMaxAllowedKeyLength("AES");
System.out.println("Max Key Size for AES : " + maxKeySize);
输出应为:
Max Key Size for AES : 2147483647
若返回128,说明策略文件未正确安装。
4. 加密操作
4.1 准备证书和私钥
实现加密功能前,需准备证书和私钥。测试资源:
- Baeldung.cer
- Baeldung.p12(密码:
password
)
Java加载代码:
Security.addProvider(new BouncyCastleProvider());
CertificateFactory certFactory = CertificateFactory
.getInstance("X.509", "BC");
X509Certificate certificate = (X509Certificate) certFactory
.generateCertificate(new FileInputStream("Baeldung.cer"));
char[] keystorePassword = "password".toCharArray();
char[] keyPassword = "password".toCharArray();
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream("Baeldung.p12"), keystorePassword);
PrivateKey key = (PrivateKey) keystore.getKey("baeldung", keyPassword);
关键点:
- 动态添加
BouncyCastleProvider
(也可静态配置java.security
文件) - 使用
CertificateFactory
加载X.509证书 - 通过PKCS12密钥库获取私钥
证书和私钥主要用于非对称加密操作:
- 加密/解密
- 签名/验证
4.2 生成证书和密钥库
生成新证书需执行以下命令:
# 生成2048位私钥
openssl genrsa -out private-key.pem 2048
# 创建证书签名请求
openssl req -new -sha256 -key private-key.pem -out certificate-signed-request.csr
# 生成自签名证书
openssl req -x509 -sha256 -days 365 -key private-key.pem -in certificate-signed-request.csr -out Baeldung.cer
# 创建PKCS12密钥库
openssl pkcs12 -export -name baeldung -out Baeldung.p12 -inkey private-key.pem -in Baeldung.cer
⚠️ 生成后需将证书放入资源目录,并确保代码中引用正确的文件名。
4.3 CMS/PKCS7加密与解密
非对称加密中,通信双方需使用公钥证书和私钥:
- 发送方用接收方证书加密
- 接收方用对应私钥解密
加密实现:
public static byte[] encryptData(byte[] data,
X509Certificate encryptionCertificate)
throws CertificateEncodingException, CMSException, IOException {
byte[] encryptedData = null;
if (null != data && null != encryptionCertificate) {
CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator
= new CMSEnvelopedDataGenerator();
JceKeyTransRecipientInfoGenerator jceKey
= new JceKeyTransRecipientInfoGenerator(encryptionCertificate);
cmsEnvelopedDataGenerator.addRecipientInfoGenerator(transKeyGen);
CMSTypedData msg = new CMSProcessableByteArray(data);
OutputEncryptor encryptor
= new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC)
.setProvider("BC").build();
CMSEnvelopedData cmsEnvelopedData = cmsEnvelopedDataGenerator
.generate(msg,encryptor);
encryptedData = cmsEnvelopedData.getEncoded();
}
return encryptedData;
}
解密实现:
public static byte[] decryptData(
byte[] encryptedData,
PrivateKey decryptionKey)
throws CMSException {
byte[] decryptedData = null;
if (null != encryptedData && null != decryptionKey) {
CMSEnvelopedData envelopedData = new CMSEnvelopedData(encryptedData);
Collection<RecipientInformation> recipients
= envelopedData.getRecipientInfos().getRecipients();
KeyTransRecipientInformation recipientInfo
= (KeyTransRecipientInformation) recipients.iterator().next();
JceKeyTransRecipient recipient
= new JceKeyTransEnvelopedRecipient(decryptionKey);
return recipientInfo.getContent(recipient);
}
return decryptedData;
}
测试用例:
String secretMessage = "My password is 123456Seven";
System.out.println("Original Message : " + secretMessage);
byte[] stringToEncrypt = secretMessage.getBytes();
byte[] encryptedData = encryptData(stringToEncrypt, certificate);
System.out.println("Encrypted Message : " + new String(encryptedData));
byte[] rawData = decryptData(encryptedData, privateKey);
String decryptedMessage = new String(rawData);
System.out.println("Decrypted Message : " + decryptedMessage);
输出:
Original Message : My password is 123456Seven
Encrypted Message : 0�*�H��...
Decrypted Message : My password is 123456Seven
4.4 CMS/PKCS7签名与验证
签名实现:
public static byte[] signData(
byte[] data,
X509Certificate signingCertificate,
PrivateKey signingKey) throws Exception {
byte[] signedMessage = null;
List<X509Certificate> certList = new ArrayList<X509Certificate>();
CMSTypedData cmsData= new CMSProcessableByteArray(data);
certList.add(signingCertificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator();
ContentSigner contentSigner
= new JcaContentSignerBuilder("SHA256withRSA").build(signingKey);
cmsGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC")
.build()).build(contentSigner, signingCertificate));
cmsGenerator.addCertificates(certs);
CMSSignedData cms = cmsGenerator.generate(cmsData, true);
signedMessage = cms.getEncoded();
return signedMessage;
}
验证实现:
public static boolean verifSignedData(byte[] signedData)
throws Exception {
X509Certificate signCert = null;
ByteArrayInputStream inputStream
= new ByteArrayInputStream(signedData);
ASN1InputStream asnInputStream = new ASN1InputStream(inputStream);
CMSSignedData cmsSignedData = new CMSSignedData(
ContentInfo.getInstance(asnInputStream.readObject()));
SignerInformationStore signers
= cmsSignedData.getCertificates().getSignerInfos();
SignerInformation signer = signers.getSigners().iterator().next();
Collection<X509CertificateHolder> certCollection
= certs.getMatches(signer.getSID());
X509CertificateHolder certHolder = certCollection.iterator().next();
return signer
.verify(new JcaSimpleSignerInfoVerifierBuilder()
.build(certHolder));
}
测试用例:
byte[] signedData = signData(rawData, certificate, privateKey);
Boolean check = verifSignData(signedData);
System.out.println(check);
输出:
true
5. 总结
本文介绍了BouncyCastle库的基础加密操作。实际应用中,通常采用先签名后加密的方式:
- 发送方用私钥签名
- 接收方用公钥验证签名
- 接收方用私钥解密内容
完整代码示例见GitHub仓库。