Java中如何用RSA公钥加密数据?及公钥加载异常解决
RSA公钥加密问题解决方法
先排查PEM格式问题
你遇到的java.security.InvalidKeyException: IOException: algid parse error not a sequence异常,大概率是PEM公钥格式不匹配导致的:
- 确认PEM头部尾部:必须是
-----BEGIN PUBLIC KEY-----(X.509格式)或-----BEGIN RSA PUBLIC KEY-----(PKCS#1格式),结尾需对应匹配。 - 清理PEM时别留多余字符:去掉头部尾部后,只保留Base64编码内容,删除所有空格、换行以外的杂项(换行全删也可,但不能有其他符号)。
- 格式不兼容处理:如果是PKCS#1格式的PEM,直接用
X509EncodedKeySpec会报错——因为X509对应的是带算法标识的SubjectPublicKeyInfo结构,而PKCS#1只有纯RSA公钥数据。可以把PKCS#1格式转成X509格式再处理,或者直接用RSAPublicKeySpec生成公钥。
用JWK的n和e生成RSAPublicKey
是的,直接使用JWK中的n(模)和e(公钥指数)生成RSAPublicKey是更可靠的方案,步骤如下:
- Base64URL解码:JWK里的
n和e是Base64URL编码,需要替换-为+、_为/,并补全=使长度为4的倍数。 - 转BigInteger:将解码后的字节数组转为正整数(BigInteger构造时传入1表示正数)。
- 构造公钥:通过
RSAPublicKeySpec和KeyFactory生成RSAPublicKey。
代码示例
处理PEM公钥(兼容PKCS#1和X509)
import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import org.bouncycastle.asn1.ASN1BitString; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERSequence; public class PublicKeyUtils { public static PublicKey getPublicKeyFromPEM(String pem) throws Exception { // 清理PEM内容 String cleanedPem = pem.replaceAll("-----BEGIN (RSA )?PUBLIC KEY-----", "") .replaceAll("-----END (RSA )?PUBLIC KEY-----", "") .replaceAll("\\s+", ""); byte[] keyBytes = Base64.getDecoder().decode(cleanedPem); try { // 尝试X509格式解析 X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePublic(spec); } catch (Exception e) { // 转为X509格式(PKCS#1转X509) DERSequence algorithmSeq = new DERSequence( new ASN1ObjectIdentifier("1.2.840.113549.1.1.1"), new DERNull() ); ASN1BitString publicKeyBitString = new DERBitString(keyBytes); DERSequence subjectPublicKeyInfo = new DERSequence(algorithmSeq, publicKeyBitString); byte[] x509Bytes = subjectPublicKeyInfo.getEncoded(); X509EncodedKeySpec spec = new X509EncodedKeySpec(x509Bytes); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePublic(spec); } } }
从JWK的n和e生成RSAPublicKey
import java.security.KeyFactory; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; import java.math.BigInteger; import java.util.Base64; public class JWKUtils { public static RSAPublicKey getRSAPublicKeyFromJWK(String n, String e) throws Exception { // 解码n String nBase64 = n.replace('-', '+').replace('_', '/'); while (nBase64.length() % 4 != 0) { nBase64 += "="; } byte[] nBytes = Base64.getDecoder().decode(nBase64); BigInteger modulus = new BigInteger(1, nBytes); // 解码e String eBase64 = e.replace('-', '+').replace('_', '/'); while (eBase64.length() % 4 != 0) { eBase64 += "="; } byte[] eBytes = Base64.getDecoder().decode(eBase64); BigInteger exponent = new BigInteger(1, eBytes); // 生成公钥 RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); KeyFactory kf = KeyFactory.getInstance("RSA"); return (RSAPublicKey) kf.generatePublic(spec); } }
总结
- 若PEM解析报错,优先检查格式类型,通过代码兼容两种格式即可解决。
- 使用JWK的n和e生成公钥更稳定,避免了PEM格式混淆的问题,推荐优先采用这种方式。
内容的提问来源于stack exchange,提问作者Dany
相关产品推荐
相关产品推荐

