Java 21:用原生API生成兼容OpenSSL的AES加密RSA私钥PEM
在Java 21中生成兼容OpenSSL的AES加密RSA私钥(原生API实现)
核心思路
要让私钥同时被Java和OpenSSL识别,必须遵循PKCS#8加密私钥标准。Java原生的EncryptedPrivateKeyInfo类可直接生成符合该标准的结构,结合Cipher完成AES加密后输出PEM格式即可解决兼容性问题。
完整实现代码
1. 生成RSA密钥对
import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; public class RSAKeyGenerator { public static KeyPair generateRSAKeyPair(int keySize) throws NoSuchAlgorithmException { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(keySize); return kpg.generateKeyPair(); } }
2. PEM加密私钥核心方法
import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; public class PemEncryptor { // 兼容OpenSSL的标准加密算法 private static final String ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding"; // 从密码生成AES密钥的标准算法 private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; private static final int ITERATION_COUNT = 65536; private static final int AES_KEY_LENGTH = 256; // 生产环境需使用随机生成的盐,此处为示例固定值 private static final byte[] SALT = "secure_random_salt".getBytes(); public static String pemEncrypt(java.security.PrivateKey privateKey, String password) throws Exception { // 1. 将私钥转换为PKCS#8 DER编码 byte[] privateKeyDer = privateKey.getEncoded(); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKeyDer); // 2. 通过PBKDF2从密码生成AES密钥 SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM); PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), SALT, ITERATION_COUNT, AES_KEY_LENGTH); SecretKey pbeKey = skf.generateSecret(pbeKeySpec); SecretKey aesKey = new SecretKeySpec(pbeKey.getEncoded(), "AES"); // 生成AES-CBC所需的随机IV(16字节,与AES块大小一致) byte[] iv = new byte[16]; new java.security.SecureRandom().nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv); // 3. 加密私钥的PKCS#8 DER数据 Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec); byte[] encryptedPrivateKey = cipher.doFinal(privateKeyDer); // 4. 构造符合PKCS#8标准的加密私钥结构 javax.crypto.EncryptedPrivateKeyInfo encryptedInfo = new javax.crypto.EncryptedPrivateKeyInfo( new javax.crypto.spec.PBEParameterSpec(SALT, ITERATION_COUNT), ENCRYPTION_ALGORITHM, encryptedPrivateKey ); // 5. 转换为PEM格式输出 byte[] encryptedInfoDer = encryptedInfo.getEncoded(); String base64Content = Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(encryptedInfoDer); return "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + base64Content + "\n-----END ENCRYPTED PRIVATE KEY-----"; } }
3. 测试代码
public class Main { public static void main(String[] args) throws Exception { // 生成2048位RSA密钥对 KeyPair keyPair = RSAKeyGenerator.generateRSAKeyPair(2048); // 加密私钥,示例密码为"mySecurePass123" String encryptedPem = PemEncryptor.pemEncrypt(keyPair.getPrivate(), "mySecurePass123"); System.out.println(encryptedPem); // OpenSSL兼容性验证:将输出保存为privatekey.pem,执行以下命令 // openssl pkcs8 -in privatekey.pem -topk8 -nocrypt -out decrypted.pem // 输入密码后可得到明文私钥,证明格式兼容 } }
关键细节说明
- PKCS#8标准适配:
EncryptedPrivateKeyInfo直接生成OpenSSL可识别的ASN.1结构,避免手动编码导致的ASN1错误。 - 算法兼容性:选用
AES/CBC/PKCS5Padding和PBKDF2WithHmacSHA256,均为Java和OpenSSL支持的标准算法。 - 安全性保障:使用随机IV和PBKDF2密钥派生,生产环境需将盐改为随机生成并与私钥一同存储。
常见问题排查
- ASN1编码错误:此前方法未严格遵循PKCS#8结构,使用
EncryptedPrivateKeyInfo可彻底解决该问题。 - InvalidKeyException:确保AES密钥通过
SecretKeySpec正确转换,算法名称使用"AES"而非带长度的变种。 - NoSuchAlgorithmException:Java 21默认支持上述所有算法,若出现异常需检查JDK版本是否为官方标准发行版。
内容的提问来源于stack exchange,提问作者rsenden
相关产品推荐
相关产品推荐

