如何在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
相关产品推荐
相关产品推荐

