ssh-keygen生成的RSA公钥在Java中转换报错问题排查
RSA公钥解析错误:invalid key format 问题排查与解决
问题背景
使用ssh-keygen -b 2048 -t rsa生成RSA密钥对后,在Java Servlet中读取key_rsa.pub公钥文件时先后遇到两个错误:
- 初始代码直接对完整公钥字符串做Base64解码,抛出
Illegal base64 character错误 - 移除公钥开头的
ssh-rsa标识和末尾注释后,又在keyFactory.generatePublic(keySpec)处抛出java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
相关代码
Servlet读取公钥代码
@WebServlet(name = "Login", urlPatterns = "/login") public class Login extends HttpServlet { ... InputStream publicKeyInputStream = getClass().getClassLoader().getResourceAsStream("key_rsa.pub"); if (publicKeyInputStream == null) { out.println("fail - no public key"); out.flush(); return; } String publicKeyString = new String(publicKeyInputStream.readAllBytes(), StandardCharsets.UTF_8); try { PublicKey publicKey = util.RSAKeyGenerator.getPublicKey(publicKeyString); ... } catch (Exception e) { ... } }
修改后的RSAKeyGenerator.getPublicKey方法
public static PublicKey getPublicKey(String publicKeyString) throws Exception { publicKeyString = publicKeyString.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","").trim(); byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); return keyFactory.generatePublic(keySpec); }
公钥文件内容
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCko/IRJIBoIN6fe3k8mCzsereXnQlTSwHutyMqZh7/JqbWJA31036I0YZUuOJ0d4WAMDWktWrfn7dkAlt+dqQtAlyzPUrqOc/euQ0iZPYhrd8Gn9jn+8WAQyt9L3LemgQ1ZWnXgNUk5Z1Y1vcbp8GsRWWLrxR7uvtbonU7QDJv5vEFnleObPqKeqobbg1iUoaXYkw0IAqhCTNW3ZvfWQic4FeHeNNVtT8XUrGyNPZkSgQ8QHsvSvNiLJ2bROhBD4AUZKBN/XRvNs+OSJM7agGroznHGxpwj7jZ0V+MH1HvHt95+4SnFAQJVfjza3lUPoMIOphq24V6hLzCr17lPECj kleber@kleber-desktop
问题原因
ssh-keygen生成的公钥是SSH格式的RSA公钥,而Java的X509EncodedKeySpec仅支持解析X.509标准格式的公钥编码,两者结构存在本质差异:
- SSH公钥的Base64部分封装了自定义结构:先包含算法标识(
ssh-rsa)的字节长度与内容,再存储公钥的模和指数数据 - 直接解码SSH公钥的Base64内容,无法匹配X.509规范的密钥结构,因此抛出格式错误
解决方案
方案1:代码层面解析SSH公钥结构
修改getPublicKey方法,手动解析SSH公钥的内部结构,提取模和指数后构建标准RSA公钥:
public static PublicKey getPublicKey(String publicKeyString) throws Exception { // 移除前缀和末尾注释,保留纯Base64内容 publicKeyString = publicKeyString.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*", "").trim(); byte[] decoded = Base64.getDecoder().decode(publicKeyString); try (ByteArrayInputStream bais = new ByteArrayInputStream(decoded); DataInputStream dis = new DataInputStream(bais)) { // 跳过算法标识部分(长度+内容) int algLen = dis.readInt(); byte[] algBytes = new byte[algLen]; dis.readFully(algBytes); // 读取公钥模 int modLen = dis.readInt(); byte[] modBytes = new byte[modLen]; dis.readFully(modBytes); BigInteger modulus = new BigInteger(1, modBytes); // 读取公钥指数 int expLen = dis.readInt(); byte[] expBytes = new byte[expLen]; dis.readFully(expBytes); BigInteger exponent = new BigInteger(1, expBytes); // 构建标准RSA公钥规范并生成公钥 RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); return keyFactory.generatePublic(spec); } }
方案2:提前转换公钥格式
用工具将SSH公钥转换为X.509格式,之后直接用原代码读取:
- 执行以下命令转换公钥:
ssh-keygen -f key_rsa.pub -e -m PKCS8 > key_rsa_x509.pub
- 将转换后的
key_rsa_x509.pub放入项目资源目录,原getPublicKey方法无需修改即可正常解析
内容的提问来源于stack exchange,提问作者Kleber Mota
相关产品推荐
相关产品推荐

