Java加载加密私钥报错:无法验证加密私钥及DerValue异常
问题:加密后的PKCS8私钥加载失败抛出InvalidKeyException
背景
- 执行命令生成未加密的RSA密钥对与证书:
此未加密私钥openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodeskey.pem搭配代码可正常运行。 - 执行命令加密私钥:
搭配加密后的openssl pkcs8 -in key.pem -out key-encrypted.pem -topk8key-encrypted.pem运行代码时,在生成私钥的步骤抛出错误:java.security.InvalidKeyException: IOException : DerValue.getBigIntegerInternal, not expected 48
现有代码
加载证书代码
byte[] certData = Files.readAllBytes(certPath); InputStream st = new ByteArrayInputStream(certData); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); certificate = (X509Certificate) certFactory.generateCertificate(st);
加载私钥代码
List<String> keys = Files.readAllLines(keyPath); // 已移除首尾行:-----BEGIN PRIVATE KEY----- 和 -----END PRIVATE KEY----- String key = String.join("", keys); // 解析为PKCS8格式 byte[] decoded = Base64.decodeBase64(key); KeyFactory kf = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(decoded); // 生成私钥 PrivateKey pk = kf.generatePrivate(ks); // 验证代码 Signature s = Signature.getInstance("SHA256withRSA"); s.initSign(pk); byte[] sign = s.sign(); s.initVerify(certificate.getPublicKey()); s.verify(sign);
问题原因
加密后的key-encrypted.pem是带口令保护的PKCS#8加密格式,而现有代码直接按照未加密的PKCS#8原始格式解析,忽略了加密层的处理,导致解析DER编码时格式不匹配,抛出异常。
解决方法
方案1:使用BouncyCastle库解析加密PKCS8私钥
- 添加BouncyCastle依赖(以Maven为例):
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.70</version> </dependency> - 修改私钥加载代码:
import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; import org.bouncycastle.operator.InputDecryptorProvider; import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; // 读取加密私钥文件 PEMParser parser = new PEMParser(new FileReader(keyPath)); Object obj = parser.readObject(); parser.close(); // 处理加密的PKCS8私钥 PKCS8EncryptedPrivateKeyInfo encryptedKeyInfo = (PKCS8EncryptedPrivateKeyInfo) obj; char[] password = "你的私钥口令".toCharArray(); // 替换为加密时设置的口令 // 构建解密器 InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder() .build(password); JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); // 解密得到私钥 PrivateKey pk = converter.getPrivateKey(encryptedKeyInfo.decryptPrivateKeyInfo(decryptorProvider)); - 注册BouncyCastle安全提供者(代码开头添加):
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
方案2:将加密私钥转换为PKCS12格式后加载
- 用OpenSSL将加密私钥和证书打包为PKCS12文件:
执行时需输入私钥口令,以及设置PKCS12文件的保护口令。openssl pkcs12 -export -in cert.pem -inkey key-encrypted.pem -out keystore.p12 - Java代码加载PKCS12密钥库获取私钥:
KeyStore keyStore = KeyStore.getInstance("PKCS12"); char[] storePassword = "PKCS12文件口令".toCharArray(); // 替换为设置的PKCS12口令 char[] keyPassword = "私钥口令".toCharArray(); // 替换为加密私钥的口令 try (InputStream is = new FileInputStream("keystore.p12")) { keyStore.load(is, storePassword); } // 别名可通过keyStore.aliases()遍历获取 PrivateKey pk = (PrivateKey) keyStore.getKey("别名", keyPassword);
内容的提问来源于stack exchange,提问作者SnakeWalking
相关产品推荐
相关产品推荐

