使用带密码保护的RSA密钥进行加密和解密
本文介绍如何使用Java JCE库操作带密码保护的RSA密钥进行加密解密。由于CloudHSM SDK不支持密码保护的密钥,需要使用OpenSSL和Java标准JCE库实现。文章包含完整的密钥生成、格式转换、加密解密的OpenSSL命令和Java代码示例,并提供了可直接运行的Maven项目配置,适合需要在Java应用中处理密码保护密钥的开发场景。
使用带密码保护的RSA密钥进行加密和解密
一、背景
注意:CloudHSM SDK 5的JCE Provider SDK不支持操作“受到密码保护的密钥”,这是因为CloudHSM的SDK要求必须由CloudHSM来保护密钥而不是通过密码保护,并且CloudHSM不支持上传带有密码的密钥。由于这两个限制,操作带有密码保护的密钥就不能使用CloudHSM的SDK了,需要使用OpenSSL库和独立的Java JCE库操作。
二、在本地创建带有密码保护的密钥
在安装有openssl的环境下,创建密钥:
openssl genrsa -aes256 -out private-with-password.key 2048
输入密码,完成密钥创建。由此在当前目录下获得了private-with-password.key有密码保护的私钥。
接下来由私钥生成公钥。
openssl rsa -in private-with-password.key -pubout -out public.key
由此在当前目录下获得了公钥public.key。
三、调用OpenSSL库使用带有密码保护的密钥进行加密、解密操作
1、准备测试文本
现在生成一个文本文件,用于进行加密测试。
echo "hello world" > plaintext.txt
2、用公钥加密
首先使用公钥对被加密的文本进行加密。因为公钥不是密码保护,因此不会提示输入密码。
openssl pkeyutl -encrypt -pubin -inkey public.key -in plaintext.txt -out encrypted.bin
当前目录下获得了encrypted.bin就是加密后的密文。
3、用私钥解密
解密有两个方法:
- 1是在执行解密命令后,命令行会等待输入密码,才能完成解密;
- 2是把密码作为参数传入直接完成解密。
# 方法1:命令提示输入密码
openssl pkeyutl -decrypt -inkey private-with-password.key -in encrypted.bin -out decrypted.txt
# 方法2:命令行中指定密码(本例密码是1qazxsw2)
openssl pkeyutl -decrypt -inkey private-with-password.key -passin pass:1qazxsw2 -in encrypted.bin -out decrypted.txt
由此获得decrypted.txt就是解密后的文件。
四、使用Java代码实现加密和解密
需要注意的是,CloudHSM SDK 5的JCE Provider SDK不支持受到密码保护的密钥操作,因为CloudHSM的SDK要求必须由CloudHSM来保护密钥,而CloudHSM是不支持上传带有密码的密钥到CloudHSM内的,因此这样也就不能使用CloudHSM的SDK了。
解决办法:使用Java标准的JCE库,或者外部的BouncyCastle库。由于BouncyCastle容易出现旧版本的安全隐患,因此这里以Java JCE为例。
1、使用OpenSSL转换密钥格式
为了不依赖BouncyCastle库,这里将原来的PEM证书转换为DER格式。命令如下:
# 1. 转换私钥(此处嵌入明文密钥1qazxsw2)
openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES -inform PEM -outform DER \
-in private-with-password.key \
-out private-encrypted.der \
-passin pass:1qazxsw2 \
-passout pass:1qazxsw2
# 2. 转换公钥
openssl rsa -pubin -in public.key -outform DER -out public.der
由此获得了public.der和private-encrypted.der两个文件。
2、Java源代码
新建src/main/java/com/example/RsaEncryptDecrypt.java文件。内容如下:
package com.example;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RsaEncryptDecrypt {
public static void main(String[] args) {
try {
String publicKeyFile = "public.der";
String privateKeyFile = "private-encrypted.der";
String password = "1qazxsw2";
String plaintextFile = "plaintext.txt";
String encryptedFile = "encrypted.bin";
String decryptedFile = "decrypted.txt";
System.out.println("=== RSA Encryption/Decryption Demo (Pure Java) ===\n");
// 1. Load public key (DER format)
System.out.println("1. Loading public key from: " + publicKeyFile);
PublicKey publicKey = loadPublicKey(publicKeyFile);
System.out.println(" Public key loaded successfully\n");
// 2. Encrypt file with public key
System.out.println("2. Encrypting file: " + plaintextFile);
byte[] plaintext = Files.readAllBytes(Paths.get(plaintextFile));
System.out.println(" Original content: " + new String(plaintext));
byte[] encrypted = encrypt(plaintext, publicKey);
Files.write(Paths.get(encryptedFile), encrypted);
System.out.println(" Encrypted data saved to: " + encryptedFile + "\n");
// 3. Load password-protected private key (DER format)
System.out.println("3. Loading password-protected private key from: " + privateKeyFile);
PrivateKey privateKey = loadPrivateKey(privateKeyFile, password);
System.out.println(" Private key loaded successfully with password\n");
// 4. Decrypt file with private key
System.out.println("4. Decrypting file: " + encryptedFile);
byte[] encryptedData = Files.readAllBytes(Paths.get(encryptedFile));
byte[] decrypted = decrypt(encryptedData, privateKey);
Files.write(Paths.get(decryptedFile), decrypted);
System.out.println(" Decrypted content: " + new String(decrypted));
System.out.println(" Decrypted data saved to: " + decryptedFile + "\n");
System.out.println("=== Success! ===");
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
private static PublicKey loadPublicKey(String filename) throws Exception {
byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
private static PrivateKey loadPrivateKey(String filename, String password) throws Exception {
// Read encrypted private key in DER format
byte[] encryptedKeyBytes = Files.readAllBytes(Paths.get(filename));
EncryptedPrivateKeyInfo encryptedKeyInfo = new EncryptedPrivateKeyInfo(encryptedKeyBytes);
// Decrypt the private key using password
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(encryptedKeyInfo.getAlgName());
Key secretKey = secretKeyFactory.generateSecret(keySpec);
// Get the decrypted private key
PKCS8EncodedKeySpec privateSpec = encryptedKeyInfo.getKeySpec(secretKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(privateSpec);
}
private static byte[] encrypt(byte[] plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plaintext);
}
private static byte[] decrypt(byte[] encrypted, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encrypted);
}
}
3、POM文件
新建pom.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>password-protected-key</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.example.RsaEncryptDecrypt</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.RsaEncryptDecrypt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
4、加解密测试
这里在一个代码中同时测试加密和解密。先进行构建。
mvn clean package
然后运行代码。
java -jar target/password-protected-key-1.0-SNAPSHOT.jar
返回结果如下:
=== RSA Encryption/Decryption Demo (Pure Java) ===
1. Loading public key from: public.der
Public key loaded successfully
2. Encrypting file: plaintext.txt
Original content: hello world
Encrypted data saved to: encrypted.bin
3. Loading password-protected private key from: private-encrypted.der
Private key loaded successfully with password
4. Decrypting file: encrypted.bin
Decrypted content: hello world
Decrypted data saved to: decrypted.txt
=== Success! ===
由此实现了Java JCE对文本文件的加密和解密。
五、验证测试
在全新的临时目录 /tmp/test-password-key 中,严格按照本文步骤执行:
1、验证结果
第一部分 - 创建密钥:
- ✅ 生成密码保护的私钥 private-with-password.key
- ✅ 生成公钥 public.key
第二部分 - OpenSSL 加密解密:
- ✅ 创建测试文件 plaintext.txt
- ✅ 用公钥加密生成 encrypted.bin
- ✅ 用私钥解密生成 decrypted.txt
- ✅ 解密内容正确:“hello world”
第三部分 - Java 实现:
- ✅ 转换私钥为 DER 格式 private-encrypted.der
- ✅ 转换公钥为 DER 格式 public.der
- ✅ 创建 Java 源代码
- ✅ 创建 POM 文件
- ✅ Maven 编译成功(BUILD SUCCESS)
- ✅ JAR 运行成功
- ✅ 输出与 MD 文件中的示例完全一致
2、生成的文件清单
plaintext.txt # 原始明文
encrypted.bin # 加密文件
decrypted.txt # 解密文件(与原文一致)
private-with-password.key # PEM 格式私钥
public.key # PEM 格式公钥
private-encrypted.der # DER 格式私钥
public.der # DER 格式公钥
pom.xml # Maven 配置
src/main/java/... # Java 源代码
target/*.jar # 可执行 JAR
3、结论
Markdown文件完全正确,可以直接使用! 所有命令、代码、输出示例都经过验证,可以从头到尾顺利执行。
最后修改于 2026-03-12