使用带密码保护的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.derprivate-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