You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中用SUN/SunJCE生成PBES2加密的PKCS#8密钥?

仅用SUN/SunJCE创建PBES2加密PKCS#8私钥的问题

已实现加密私钥转明文私钥,但无法实现明文私钥转加密私钥,卡在PBES2算法参数配置步骤,仅允许使用SUN/SunJCE,不能依赖BouncyCastle/IAIK JCE。

遇到两种错误情况:

  • 使用PBES2初始化AlgorithmParameters时,ASN.1中的密码算法/密钥派生函数未初始化
  • 配置好所有ASN.1字段后,SUN/SunJCE不接受PBEWithHmacSHA256AndAES_256这个长算法名,仅识别PBES2

明文私钥

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQguwofTpfqBCC6NPhF
WB5b6IfkD8C1mj2fdxyrYCCE/s+hRANCAAQLYKgHgOgGTWYDCaPO60XpdYXPYtao
nz1YtmZHooR5QOz22QM4Iwyrkky01qsDJbWaXjQDTxWDwm2nMQuiEp2M
-----END PRIVATE KEY-----

PBES2加密私钥(密码:"something")

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIH0MF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBClpAgdFf+Ezj405avG
//ZgAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQgic0LMiWWQ4c4ZaA
O6dMYwSBkBKZhUitcgjw3uKYWnWGDzSRHUgvgKLf73D85anZ1yMO71t/ZCX7bC29
cVECnVSgL8fvyp/JZG1HDMJzLPNnENsESipk4wFgKnIWh/vlxT5mNSY4nFO/3My4
gRzlY1XJ+AA4qflU8tb0HfMTM42dmH2+DALmMsJgplntqX/9svv7h7pfsVoMckQX
dr/OetPvsA==
-----END ENCRYPTED PRIVATE KEY-----

测试代码

class Pbes2Test {

    @Test
    void canParseThisKey() {
        File file = new File("src/test/resources/key-encrypted.der");
        byte[] fileData = FileUtils.readFileToByteArray(file);

        EncryptedPrivateKeyInfo encInfo = new EncryptedPrivateKeyInfo(fileData);
        assertEquals("PBEWithHmacSHA256AndAES_256", encInfo.getAlgName())

        // so KDF is PBKDF2 and cipher is AES-256-CBC
        byte[] cipherText = encInfo.getEncryptedData();

        // get: salt, iv, iterationCount
        AlgorithmParameters algParameters = encInfo.getAlgParameters();
        var paramSpec = algParameters.getParameterSpec(PBEParameterSpec.class);
        AlgorithmParameterSpec ivSpec = paramSpec.getParameterSpec();

        PBEKeySpec spec = new PBEKeySpec("something".toCharArray(),
                paramSpec.getSalt(),
                paramSpec.getIterationCount(),
                32 * 8);

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        SecretKey myKey = skf.generateSecret(spec);

        byte[] myKeyData = myKey.getEncoded();
        SecretKeySpec keySpec = new SecretKeySpec(myKeyData, "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

        cipher.update(cipherText);
        cipher.doFinal();

        System.out.println("Profit!!");
    }

    @Test
    void cannnotCreateEncryptedPKCS8DER() throws Exception {
        IvParameterSpec ivSpec;
        PBEParameterSpec paramSpec;
        PBEKeySpec pbeKeySpec;

        File file = new File("src/test/resources/key-plain.der");
        byte[] plaintext = FileUtils.readFileToByteArray(file);

        byte[]  salt = new byte[16];
        byte[]  iv = new byte[16];
        SecureRandom random = new SecureRandom();
        int iterationCount = 2048;

        random.nextBytes(salt);
        random.nextBytes(iv);

        ivSpec = new IvParameterSpec(iv);

        // ---- do cryptography
        pbeKeySpec = new PBEKeySpec("something".toCharArray(),
                salt,
                iterationCount,
                32 * 8);

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        SecretKey myKey = skf.generateSecret(pbeKeySpec);

        byte[] myKeyData = myKey.getEncoded();
        SecretKeySpec keySpec = new SecretKeySpec(myKeyData, "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        ByteArrayOutputStream buf = new ByteArrayOutputStream();

        buf.write(cipher.update(plaintext));
        buf.write(cipher.doFinal());
        buf.close();

        System.out.printf("Successfully created encryptedData!! size = %d\n", buf.size());

        // ---- ASN.1
        EncryptedPrivateKeyInfo encryptedPrivateKey;
        File file2;

        // Take 1: this looks wrong; where is AES-256-CBC, PBKDF2 added to the AlgorithmParameters?
        AlgorithmParameters outputParameters = AlgorithmParameters.getInstance("PBES2");
        paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
        outputParameters.init(paramSpec);

        // FAIL: Cipher AES-256-CBC, KDF PBKDF2 not initialized
        encryptedPrivateKey = new EncryptedPrivateKeyInfo(outputParameters, buf.toByteArray());

        file2 = new File("test-output/TEST-key-encrypted.der");
        FileUtils.writeByteArrayToFile(file2, encryptedPrivateKey.getEncoded());

        // Take 2: this almost works but has the wrong name!
        AlgorithmParameters outputParameters = AlgorithmParameters.getInstance("PBEWithHmacSHA256AndAES_256");
        paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
        outputParameters.init(paramSpec);

        // FAIL: constructor only accepts "PBES2" and not "PBEWithHmacSHA256AndAES"
        encryptedPrivateKey = new EncryptedPrivateKeyInfo(outputParameters, buf.toByteArray());

        file2 = new File("test-output/TEST-key-encrypted.der");
        FileUtils.writeByteArrayToFile(file2, encryptedPrivateKey.getEncoded());
    }

}

异常信息

尝试1的异常(ASN.1部分未初始化)

java.lang.NullPointerException: Cannot invoke "sun.security.util.ObjectIdentifier.encode(sun.security.util.DerOutputStream)" because "oid" is null
    at java.base/sun.security.util.DerOutputStream.putOID(DerOutputStream.java:317)
    at java.base/com.sun.crypto.provider.PBES2Parameters.engineGetEncoded(PBES2Parameters.java:378)
    at java.base/java.security.AlgorithmParameters.getEncoded(AlgorithmParameters.java:380)
    at java.base/sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:111)
    at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:480)
    at java.base/javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:191)
    at integration.keystore.OpenSSLTest.foStackExchage(OpenSSLTest.java:278)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

        DerOutputStream encryptionScheme = new DerOutputStream();
        // algorithm is id-aes128-CBC or id-aes256-CBC
        encryptionScheme.putOID(cipherAlgo_OID);

尝试2的异常(算法名不被识别)

java.security.NoSuchAlgorithmException: unrecognized algorithm name: PBEWithHmacSHA256AndAES_256
    at java.base/sun.security.x509.AlgorithmId.get(AlgorithmId.java:477)
    at java.base/javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:191)
    at integration.keystore.OpenSSLTest.foStackExchange(OpenSSLTest.java:278)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

注意事项

  • IAIK提供专门的PBES2ParametersSpec类用于配置PBES2参数
  • BouncyCastle提供PBES2Parameters类处理PBES2参数
  • SUN/SunJCE存在相关已知问题,无法直接通过标准API构造完整的PBES2 AlgorithmParameters

内容的提问来源于stack exchange,提问作者Anthony Alba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 08:54:51