java - java.security.spec.InvalidKeySpecException:java.security.InvalidKeyException:无效的密钥格式
问题描述
我正在尝试在 Java 中获取用于 jwt 令牌验证的 RSA PublicKey,但我的尝试因以下异常而失败: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format。
我的代码:
String publicKeyString = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMk5pKytLeUpVaDhLYklLVU51OG0KUk5lRCtrU2tXZEh6MjBvSFZYK0g0cVd4WHlJSTk0MVdJUFU2WFpPc3lMeE9qZU1hb0ZRanJ6bDFwYnZQekUyRQpwMmhlK1BnQ1JteDNqOFlMVVd3dGpuQTVwTTFLWDhpNG5vTUw4RmlWY1U2NkE5RjRkZmRQR2MzY0tQQ2ZPbnorCmtBbW5qRllzajYzRCsrTThYSDRWaS9Vc0V3T1lzU05FR2RncUd2OTlTNHpVRzFzd2FqZ1NnODhvbTVaOC9Ja1AKY01LT3cvWkpvVHNDN3l1VlJnTC9xa3EwaDVkM2lXVXNNdXl1K0xoblRhTko4bW9WQmpJT2lQQkR0cEQyN1lzNgpCSGs1dEdBa3ZHZDg0N3c4SjVEeTFzYWlQS0pxelltcUx5akg3b3VlcERFczdEZ2UxZUlJeno5a1RnSkhKZHVzCnd3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
PublicKey publicKey = getPublicKey(Base64.getDecoder().decode(publicKeyString), "RSA");
和 getPublicKey 函数:
private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) {
PublicKey publicKey = null;
try {
KeyFactory kf = KeyFactory.getInstance(algorithm);
EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
System.out.println(keySpec.getFormat());
publicKey = kf.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
System.out.println("Could not reconstruct the public key, the given algorithm could not be found.");
} catch (InvalidKeySpecException e) {
e.printStackTrace();
System.out.println("Could not reconstruct the public key");
}
return publicKey;
}
我知道有许多类似的问题处理抛出的相同异常,我已经阅读了许多问题并尝试了解决方案,但没有任何效果。我将不胜感激任何帮助。
解决方案
您的数据不是Java crypto 要求的实际 X.509 公钥块 (SubjectPublicKeyInfo) 的 base64 编码。它实际上是PEM 文件的 base64 编码,它本身就是结构化和编码的。此外,这个 PEM 文件被标记为包含“RSA PUBLIC KEY”,这将是“原始”PKCS1 形式并且对于 Java 来说是错误的,但幸运的是,无论创建这个 PEM 文件是有缺陷的,而且 PEM 文件的主体实际上是一个 X.509 SubjectPublicKeyInfo根据 RFC7468 #13应该有 PEM 标签“PUBLIC KEY” 。(RFC5280 是 X.509 的配置文件。)
因此,您需要将字符串解码为 PEM,然后解析 PEM 以获得 X.509:
String orig = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMk5pKytLeUpVaDhLYklLVU51OG0KUk5lRCtrU2tXZEh6MjBvSFZYK0g0cVd4WHlJSTk0MVdJUFU2WFpPc3lMeE9qZU1hb0ZRanJ6bDFwYnZQekUyRQpwMmhlK1BnQ1JteDNqOFlMVVd3dGpuQTVwTTFLWDhpNG5vTUw4RmlWY1U2NkE5RjRkZmRQR2MzY0tQQ2ZPbnorCmtBbW5qRllzajYzRCsrTThYSDRWaS9Vc0V3T1lzU05FR2RncUd2OTlTNHpVRzFzd2FqZ1NnODhvbTVaOC9Ja1AKY01LT3cvWkpvVHNDN3l1VlJnTC9xa3EwaDVkM2lXVXNNdXl1K0xoblRhTko4bW9WQmpJT2lQQkR0cEQyN1lzNgpCSGs1dEdBa3ZHZDg0N3c4SjVEeTFzYWlQS0pxelltcUx5akg3b3VlcERFczdEZ2UxZUlJeno5a1RnSkhKZHVzCnd3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
String pem = new String(Base64.getDecoder().decode(orig)); // default cs okay for PEM
String[] lines = pem.split("\n"); lines[0] = lines[lines.length-1] = ""; String body = String.join("", lines);
// in general split on "\r?\n" (or delete "\r" and split on "\n")
//or instead:
//String body = pem.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----\n","").replaceAll("\n", ""); // or "\r?\n"
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey key = kf.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(body)));
// for test only:
System.out.println ( ((java.security.interfaces.RSAPublicKey)key).getModulus() );
请注意,您的特定数据具有使用单字符(NL aka \n
)换行符的 PEM,但 PEM 通常可以使用 NL 或 CRLF(\r\n
);看我的评论。
推荐阅读
- xaml - Xamarin.Forms 在 Xaml 中为“数据绑定”单个值
- flurry - Flurry 不在实时分析窗口中显示数据,也不捕获崩溃错误
- c# - 在 C# winform 中将实时数据传递给 Label 时出现数据绑定错误
- ios - 无法从 Swift 中的 UserDefaults 获取具有多个对象的 NSMutableArray
- python - PyCharm 2019.2 未显示异常回溯
- reactjs - JSP 读取 React JSX
- arduino - Arduino 和 Nextion 无法通信
- python - 在 python 中读取 FileMapping 对象(改编自 c++)
- javascript - 数据作为对象和数组传递?
- javascript - 如何让`this`指向javascript中的原始值?