java.security.InvalidKeyException:无效的密钥格式... PublicKey 错误

Posted

技术标签:

【中文标题】java.security.InvalidKeyException:无效的密钥格式... PublicKey 错误【英文标题】:java.security.InvalidKeyException: invalid key format ... PublicKey error 【发布时间】:2021-11-16 01:27:02 【问题描述】:

我知道有很多问题,但似乎没有一个适合我的需要。

我需要验证 JWT 令牌,我只有 PublicKey(这很好,因为我可以验证 jwt 的符号。

代码如下。

首先我们有公钥

private static final String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA88aLoh5l9W9UY3Hb+YGU\r\n"
            + "ZQBwVWuNTNpF5nm9uU+MqmB7EUmTYdD4Jk09zjBUutnggY10Jbjxdrv09HnHrqbj\r\n"
            + "+hyOyIXgSsVIK5bGZVHKLJzzZmWgn0QgcEXzR97J47MFeWkn0tEgDlP8LEMZE7ix\r\n"
            + "9WgoJHGGtUaQV5MM3U9S13zyRmaYekCVfLh2REHevb1aHgDReCLx92ZIdc9ldE9g\r\n"
            + "99v87E5zNKSv3AI8EVRt/Tpjfyuk7XTEqY6pz83tCfy1uch8NihhVjY8O2J3pilP\r\n"
            + "VW/L83GMJ5Shea0mE2Dq9Gh4lrVvAy+OHkarwGgdlYLwy1Dmof2SYCCq2SJyv5ZR\r\n"
            + "fwIDAQAB";

然后在我们得到的方法里面

JWTVerifier verifierToken =  JWT.require(Algorithm.RSA256((RSAPublicKey) getPublicFederaKey(), null)).build();
        DecodedJWT decodeJwt = verifierToken.verify(token);
        Map<String, Object> claims = new HashMap<String, Object>();
        
        String codiceFiscale = decodeJwt.getClaim("CODICEFISCALE").asString();
        System.err.println("CODICE FISCALE: "+codiceFiscale);
        
        if (decodeJwt.getExpiresAt().before(java.util.Calendar.getInstance().getTime())) 
              throw new RuntimeException("Expired token!");
        

如果您在 verifyToken 上看到它引用 getPublicFederaKey();

private PublicKey getPublicFederaKey() throws CustomException 
        PublicKey key = null;
        

        try
            
        String keyTrimmed = publicKey.trim().replace("\n", "").replace("\r", "");
        System.err.println(keyTrimmed);
        int stringLength = keyTrimmed.length(); //392
        String hexString = Hex.encodeHexString(publicKey.getBytes("UTF-8"));
        byte[] hexedPublicKey = DatatypeConverter.parseHexBinary(hexString);
        X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(hexedPublicKey);

        KeyFactory kf = KeyFactory.getInstance("RSA");

        key = kf.generatePublic(keySpecX509);
         catch (Exception e) 
        throw new CustomException(e.getMessage());
        
        return key;
        

我在这里看到了一个答案,并直接用字符串尝试了 hexParsing,但给了我错误的十六进制或类似的非法字符(我认为这是因为它有 af 字符)所以我猜对了,因为它正在等待对于十六进制而不是原始字符串。

然后在我尝试使用 Base64 之前类似的东西

rsa = Cipher.getInstance("RSA");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pkey.getBytes());
PublicKey pk = keyFactory.generatePublic(publicKeySpec);

最后,如您所见,我试图复制“Duncan Jones”的回复,但无济于事。

java.security.InvalidKeyException: invalid key format on generating RSA public key

在任何情况下,我需要的是字符串 publicKey(我剪切了 ----BEGIN---- -----END----),然后转换为 java.security.PublicKey转换为 java.security.interfaces.RSAPublicKey

非常感谢您。

【问题讨论】:

【参考方案1】:

最后这成功了。

byte[] decodedBytes = Base64.getMimeDecoder().decode(keyTrimmed.getBytes());
        X509EncodedKeySpec keySpecX509B = new X509EncodedKeySpec(decodedBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        RSAPublicKey generatePublic = (RSAPublicKey) keyFactory.generatePublic(keySpecX509B);

注意解码器上的区别...

包java.util.Base64.getMimeDecoder();

我通过 string.getBytes() raw,没有以前的 Base64 编码

重要的一课,从 API Provider 获取正确的文档。

来自 javadocs 的一些信息

公共类 Base64 扩展对象

此类仅包含用于获取 Base64 编码方案的编码器和解码器的静态方法。此类的实现支持 RFC 4648 和 RFC 2045 中指定的以下 Base64 类型。 •基本 使用 RFC 4648 和 RFC 2045 的表 1 中指定的“Base64 字母”进行编码和解码操作。编码器不添加任何换行(行分隔符)字符。解码器拒绝包含 base64 字母表之外的字符的数据。

•URL 和文件名安全 使用 RFC 4648 的表 2 中指定的“URL 和文件名安全 Base64 字母”进行编码和解码。编码器不添加任何换行符(行分隔符)。解码器拒绝包含 base64 字母表之外的字符的数据。

•MIME 使用 RFC 2045 的表 1 中指定的“Base64 字母”进行编码和解码操作。编码后的输出必须以每行不超过 76 个字符的形式表示,并使用回车“\r”紧跟换行“\n”作为行分隔符。没有行分隔符添加到编码输出的末尾。在解码操作中忽略所有未在 base64 字母表中找到的行分隔符或其他字符。

似乎我的密钥是这样组成的,它实际上需要 \r 和 \n。

【讨论】:

以上是关于java.security.InvalidKeyException:无效的密钥格式... PublicKey 错误的主要内容,如果未能解决你的问题,请参考以下文章