无法在 Java 中解析 JWK

Posted

技术标签:

【中文标题】无法在 Java 中解析 JWK【英文标题】:Unable to parse JWK in Java 【发布时间】:2021-10-25 19:56:47 【问题描述】:

我实现了一个休息授权服务器,它使用com.nimbusds:nimbus-jose-jwt:9.13 包以 JWK 格式返回给定 keyId 的公钥。代码如下所示:

@RequestMapping(value = "/oauth2", produces = APPLICATION_JSON_VALUE)
public interface Rest 
...
    @GetMapping("/public-key/keyId")
    @Operation(summary = "Return the public key corresponding to the key id")
    JWK getPublicKey(@PathVariable String keyId);


public class RestController implements Rest 
.....
 public JWK getPublicKey(String keyId) 
         byte[] publicKeyBytes = ....
         RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
         JWK jwk = new RSAKey.Builder(publicKey)
                .keyID(keyId)
                .algorithm(new Algorithm(publicKey.getAlgorithm()))
                .keyUse(KeyUse.SIGNATURE)
                .build();
         return jwk;
    

此代码返回以下格式的 JWK 密钥:


    "keyStore": null,
    "private": false,
    "publicExponent": ,
    "modulus": ,
    "firstPrimeFactor": null,
    "secondPrimeFactor": null,
    "firstFactorCRTExponent": null,
    "secondFactorCRTExponent": null,
    "firstCRTCoefficient": null,
    "otherPrimes": [],
    "requiredParams": 
        "e": "some-valid-exponent",
        "kty": "RSA",
        "n": "some-valid-modulus"
    ,
    "privateExponent": null,
    "x509CertChain": null,
    "algorithm": 
        "name": "RSA",
        "requirement": null
    ,
    "keyOperations": null,
    "keyID": "some-valid-key-id",
    "x509CertURL": null,
    "x509CertThumbprint": null,
    "x509CertSHA256Thumbprint": null,
    "parsedX509CertChain": null,
    "keyUse": 
        "value": "sig"
    ,
    "keyType": 
        "value": "RSA",
        "requirement": "REQUIRED"
    

在客户端 (java) 上,我尝试使用以下代码解析 jwk:

public JWK getPublicKey(String keyId) 
 String json = restTemplate.getForObject(publicUrl + "/oauth2/public-key/" + keyId, String.class);
        try 
            return JWK.parse(json);
         catch (ParseException e) 
            log.error("Unable to parse JWK", e);
            return null;
        

但是,客户端无法解析密钥,因为parse 抛出异常 (Missing parameter "kty")。我看到JWK.parse 在主 JWT josn 主体中需要一个kty 键,而JWK 的默认序列化在requiredParams 键中嵌入了kty 键。当我尝试 jwk.toString() 时,我确实在主要 json 正文中看到了 kty 键。

为什么本地 JWK 对象的序列化/反序列化不能以直接的方式工作?在不实现自定义 jwt 结构或序列化器/反序列化器的情况下解决此问题的最佳方法是什么?

更新 1:如果我们将返回类型从 JWK 更改为 Map<String, Object>String 并在客户端处理反序列化,则此代码将起作用。但是,如果包本身为我们进行(反)序列化会更好。

【问题讨论】:

【参考方案1】:

答案是使用String 为那些面临这个问题的人进行(反)序列化。你为什么问?根据RFC,JWK 是 JSON 格式的字符串。虽然nimbusds:nimbus-jose-jwt 定义了一个JWK 对象,但任何返回有效JWK(或JWKSet)的API 都可以假定它是一个字符串。

我还向这个包的开发者提出了这个issue,他们建议使用StringMap<String, Object> 进行(反)序列化。

【讨论】:

以上是关于无法在 Java 中解析 JWK的主要内容,如果未能解决你的问题,请参考以下文章

尝试解码 Jwt 令牌时出现“尝试解码 Jwt 时发生错误:无法检索远程 JWK 集:”错误

在play框架中,无法编译文件XX.java。引发的错误是:无法在 Eclipse 中解析导入 XXX

import javax.annotation.* 无法在 Eclipse 的 Java 10 编译器中解析

无法解析项目:Android库和Java库模块依赖项

JSP编译错误:无法解析数组

Sonarqube 问题:原因:java.lang.IllegalStateException:无法解析引导索引中的条目 - 也在删除插件 auth aad