如何将SSH公钥转换为Java的PublicKey?解决转换报错问题
SSH公钥转java.security.PublicKey解析错误解决方法
问题描述
将*.pub文件中的SSH公钥转换为java.security.PublicKey时,出现如下异常:
java.lang.IllegalArgumentException: failed to construct sequence from byte[]: unexpected end-of-contents marker at org.bouncycastle.asn1.ASN1Sequence.getInstance(Unknown Source) at org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.getInstance(Unknown Source)
使用的Java代码:
import java.io.IOException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; public class PublicKeyUtil { private PublicKeyUtil() {} // (skip) public static PublicKey loadPublicKey(String encoded) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] publicBytes = Base64.getDecoder().decode(encoded); PublicKey publicKey = new JcaPEMKeyConverter().getPublicKey(SubjectPublicKeyInfo.getInstance(publicBytes)); // 报错位置 return KeyFactory.getInstance( publicKey.getAlgorithm(), new org.bouncycastle.jce.provider.BouncyCastleProvider()) .generatePublic(new X509EncodedKeySpec(publicBytes)); } // (skip) }
build.gradle配置:
implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1' implementation 'org.bouncycastle:bcpkix-jdk18on:1.78.1'
错误原因
SSH公钥的编码格式和X509 SubjectPublicKeyInfo的ASN.1结构不兼容:
- SSH公钥的Base64解码后,开头包含算法标识的字节段(比如
ssh-rsa的长度+字符串内容),并非纯ASN.1序列 - 直接将该字节数组传给
SubjectPublicKeyInfo.getInstance(),会因格式不匹配导致解析失败
解决方法
使用BouncyCastle提供的OpenSSHPublicKeyUtil工具类,专门处理SSH公钥的解析:
修改后的代码
import java.io.IOException; import java.security.PublicKey; import java.util.Base64; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class PublicKeyUtil { private PublicKeyUtil() {} public static PublicKey loadSshPublicKey(String sshPubKeyStr) throws IOException { // 分割SSH公钥字符串,提取Base64部分(跳过开头的算法标识,比如"ssh-rsa") String[] parts = sshPubKeyStr.split("\\s+"); if (parts.length < 2) { throw new IllegalArgumentException("无效的SSH公钥格式"); } byte[] sshPubBytes = Base64.getDecoder().decode(parts[1]); // 解析SSH公钥为AsymmetricKeyParameter AsymmetricKeyParameter keyParam = OpenSSHPublicKeyUtil.parsePublicKey(sshPubBytes); // 转换为java.security.PublicKey return new JcaPEMKeyConverter() .setProvider(new BouncyCastleProvider()) .getPublicKey(keyParam); } // 测试方法 public static void main(String[] args) throws IOException { // 示例:读取.pub文件的内容(一行) String sshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD... 注释内容"; PublicKey publicKey = loadSshPublicKey(sshPubKey); System.out.println("公钥算法:" + publicKey.getAlgorithm()); } }
代码说明
- 分割公钥字符串:SSH公钥通常格式为
算法标识 Base64编码内容 注释,分割后提取中间的Base64部分 - 解析SSH公钥:
OpenSSHPublicKeyUtil.parsePublicKey()会自动处理SSH公钥的特殊格式,提取出密钥参数 - 转换为JDK公钥对象:通过
JcaPEMKeyConverter将BouncyCastle的密钥参数转换为标准java.security.PublicKey,之后即可调用getAlgorithm()查看算法
补充说明
- 如果需要直接读取.pub文件,可通过
Files.readString(Paths.get("xxx.pub"))获取完整的公钥字符串 - 确保BouncyCastle Provider已正确注册,代码中通过
setProvider()指定,也可提前全局注册:Security.addProvider(new BouncyCastleProvider())
内容的提问来源于stack exchange,提问作者Bohyun
相关产品推荐
相关产品推荐

