构建Spring Bean时RSA私钥读取异常:部分场景成功部分失败
问题根源与解决方案
1. 私有方法的致命错误:String不可变导致密钥头尾未被移除
你的getRSAPrivateKeyFromPem方法里犯了一个基础Java错误:String是不可变对象,pem.replace(...)调用后会返回新字符串,但你没有将结果重新赋值给pem。这意味着两行replace操作完全无效,pem仍然包含-----BEGIN RSA PRIVATE KEY-----和-----END RSA PRIVATE KEY-----这些非Base64字符。
2. 场景差异的原因
- someRandomTestBean成功的可能:注入的
key值可能已被外部处理(比如配置文件中存储的密钥本来就没有头尾,或者Spring前置处理自动移除了标记),此时Base64解码能正常完成。另外不排除你注入的密钥实际是PKCS#8格式但头尾写成了PKCS#1,或者JDK实现做了兼容处理。 - anotherBean失败的原因:注入的
key是完整带头尾的原始内容,Base64解码时遇到非Base64字符导致解析失败,抛出algid parse error, not a sequence异常。 - TestUtil方法成功的真相:你说代码完全一致,但几乎可以肯定是视觉误差——TestUtil里的方法一定正确做了赋值操作:
或者TestUtil额外处理了换行符、空格等格式问题,而你的私有方法没有。pem = pem.replace("-----BEGIN RSA PRIVATE KEY-----", "") .replace("-----END RSA PRIVATE KEY-----", "");
3. 正确处理PKCS#1格式RSA私钥的方法
PKCS#1格式的私钥不能直接用PKCS8EncodedKeySpec解析,需要手动解析ASN.1结构,或者借助第三方库。
方案1:JDK原生手动解析(无需额外依赖)
private RSAPrivateKey getRSAPrivateKeyFromPem(String keyPem) throws Exception { // 正确移除标记、换行和空格 String pem = keyPem.replace("-----BEGIN RSA PRIVATE KEY-----", "") .replace("-----END RSA PRIVATE KEY-----", "") .replaceAll("\\s+", ""); byte[] encoded = Base64.getDecoder().decode(pem); // 解析PKCS#1的ASN.1结构 ASN1InputStream asn1InputStream = new ASN1InputStream(encoded); ASN1Sequence sequence = (ASN1Sequence) asn1InputStream.readObject(); BigInteger modulus = ((ASN1Integer) sequence.getObjectAt(1)).getValue(); BigInteger publicExponent = ((ASN1Integer) sequence.getObjectAt(2)).getValue(); BigInteger privateExponent = ((ASN1Integer) sequence.getObjectAt(3)).getValue(); BigInteger primeP = ((ASN1Integer) sequence.getObjectAt(4)).getValue(); BigInteger primeQ = ((ASN1Integer) sequence.getObjectAt(5)).getValue(); BigInteger primeExponentP = ((ASN1Integer) sequence.getObjectAt(6)).getValue(); BigInteger primeExponentQ = ((ASN1Integer) sequence.getObjectAt(7)).getValue(); BigInteger crtCoefficient = ((ASN1Integer) sequence.getObjectAt(8)).getValue(); RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); }
方案2:使用BouncyCastle库(更简洁)
先添加BouncyCastle依赖,然后用库直接解析:
private RSAPrivateKey getRSAPrivateKeyFromPem(String keyPem) throws Exception { Security.addProvider(new BouncyCastleProvider()); PEMParser pemParser = new PEMParser(new StringReader(keyPem)); JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); return (RSAPrivateKey) converter.getPrivateKey((PEMKeyPair) pemParser.readObject()); }
4. 为什么PKCS#8格式密钥全场景正常?
PKCS#8格式的私钥结构完全符合PKCS8EncodedKeySpec的要求,只要正确移除头尾和非Base64字符,就能被JDK原生解析。如果你的replace操作没生效,但某些场景下密钥被提前处理过,也能成功;当你使用标准PKCS#8密钥并修正代码后,所有场景自然都能正常工作。
内容的提问来源于stack exchange,提问作者Manchu Ratt
相关产品推荐
相关产品推荐

