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

Java加载加密私钥报错:无法验证加密私钥及DerValue异常

问题:加密后的PKCS8私钥加载失败抛出InvalidKeyException

背景

  • 执行命令生成未加密的RSA密钥对与证书:
    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes
    
    此未加密私钥key.pem搭配代码可正常运行。
  • 执行命令加密私钥:
    openssl pkcs8 -in key.pem -out key-encrypted.pem -topk8
    
    搭配加密后的key-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私钥

  1. 添加BouncyCastle依赖(以Maven为例):
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>1.70</version>
    </dependency>
    
  2. 修改私钥加载代码:
    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));
    
  3. 注册BouncyCastle安全提供者(代码开头添加):
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    

方案2:将加密私钥转换为PKCS12格式后加载

  1. 用OpenSSL将加密私钥和证书打包为PKCS12文件:
    openssl pkcs12 -export -in cert.pem -inkey key-encrypted.pem -out keystore.p12
    
    执行时需输入私钥口令,以及设置PKCS12文件的保护口令。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:25:00