如何为spring oauth2 jwt服务器生成正确的jwks端点?

mjqavswn  于 2024-01-05  发布在  Spring
关注(0)|答案(2)|浏览(185)

下发目标

为我的spring oauth2 jwt服务器配置有效的jwks

第一次尝试

遵循spring文档,我可以使用JWK Set URI的现成端点。它需要:

  1. @Import(AuthorizationServerEndpointsConfiguration.class)

字符串
我已经添加。通过执行器检查Map的端点没有过滤jw

第二次尝试

按照相同的配置,我尝试使用下一个代码:

  1. import com.nimbusds.jose.jwk.JWKSet;
  2. import com.nimbusds.jose.jwk.RSAKey;
  3. ...
  4. @FrameworkEndpoint
  5. class JwkSetEndpoint {
  6. KeyPair keyPair;
  7. public JwkSetEndpoint(KeyPair keyPair) {
  8. this.keyPair = keyPair;
  9. }
  10. @GetMapping("/.well-known/jwks.json")
  11. @ResponseBody
  12. public Map<String, Object> getKey(Principal principal) {
  13. RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
  14. RSAKey key = new RSAKey.Builder(publicKey).build();
  15. return new JWKSet(key).toJSONObject();
  16. }
  17. }


它产生

  1. {
  2. "keys" : [ {
  3. "kty" : "RSA",
  4. "e" : "AQAB",
  5. "n" : "mWI2jtKwvf0W1hdMdajch-mFx9FZe3CZnKNvT_d0-2O6V1Pgkz7L2FcQx2uoV7gHgk5mmb2MZUsy_rDKj0dMfLzyXqBcCRxD6avALwu8AAiGRxe2dl8HqIHyo7P4R1nUaea1WCZB_i7AxZNAQtcCcSvMvF2t33p3vYXY6SqMucMD4yHOTXexoWhzwRqjyyC8I8uCYJ-xIfQvaK9Q1RzKRj99IRa1qyNgdeHjkwW9v2Fd4O_Ln1Tzfnk_dMLqxaNsXPw37nw-OUhycFDPPQF_H4Q4-UDJ3ATf5Z2yQKkUQlD45OO2mIXjkWprAmOCi76dLB2yzhCX_plGJwcgb8XHEQ"
  6. } ]
  7. }


使用access_token ping资源服务器失败:

  1. {"error":"invalid_token","error_description":"Invalid JWT/JWS: kid is a required JOSE Header"}

第三次尝试

修改"/.well-known/jwks.json"jwt.io helps detect algorithm used for jwt)的响应:

  1. RSAKey key = new RSAKey.Builder(publicKey)
  2. .keyID("1")
  3. .keyUse(KeyUse.SIGNATURE)
  4. .algorithm(JWSAlgorithm.RS256)
  5. .build();


导致下一个响应:

  1. {
  2. "keys" : [ {
  3. "kty" : "RSA",
  4. "e" : "AQAB",
  5. "use" : "sig",
  6. "kid" : "1",
  7. "alg" : "RS256",
  8. "n" : "mWI2jtKwvf0W1hdMdajch-mFx9FZe3CZnKNvT_d0-2O6V1Pgkz7L2FcQx2uoV7gHgk5mmb2MZUsy_rDKj0dMfLzyXqBcCRxD6avALwu8AAiGRxe2dl8HqIHyo7P4R1nUaea1WCZB_i7AxZNAQtcCcSvMvF2t33p3vYXY6SqMucMD4yHOTXexoWhzwRqjyyC8I8uCYJ-xIfQvaK9Q1RzKRj99IRa1qyNgdeHjkwW9v2Fd4O_Ln1Tzfnk_dMLqxaNsXPw37nw-OUhycFDPPQF_H4Q4-UDJ3ATf5Z2yQKkUQlD45OO2mIXjkWprAmOCi76dLB2yzhCX_plGJwcgb8XHEQ"
  9. } ]
  10. }


使用access_token ping资源服务器会得到相同的结果:

  1. {"error":"invalid_token","error_description":"Invalid JWT/JWS: kid is a required JOSE Header"}

提问

有任何想法或例子如何配置/.well-known/jwks.json产生正确的jwks

附言

  • 如果我使用公钥作为资源服务器上的本地资源-它工作。
  • 我会很高兴任何工作解决方案(可能有人知道不同的jwks库,可以在spring-boot应用程序中使用)。
oxosxuxt

oxosxuxt1#

有什么新进展吗?
我们正在研究基于签名的身份验证,https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12
其中一个要求是实现/. well-known/jwks.json端点,这样我们就不必有单独的公钥分发机制。
我还没有这样做,但它看起来像:

  1. KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  2. return JWKSet.load(keyStore, (name) -> null).toPublicJWKSet();

字符串

uqcuzwp8

uqcuzwp82#

要生成JWKS端点,您可以使用一些好的库,如nimbus-jose-jwt,但也可以完全不使用外部库。
为此,您需要生成密钥文件:

  1. ssh-keygen -t rsa -b 4096 -m PEM -f rs256.key

字符串
并转换为Java可以使用的内容:

  1. openssl pkcs8 -topk8 -inform PEM -in rs256.key -out rs256.pem -nocrypt


然后可以使用以下代码生成JWKS端点响应:

  1. import java.util.Map;
  2. import java.util.HashMap;
  3. import java.util.List;
  4. import java.util.ArrayList;
  5. import java.io.IOException;
  6. import java.nio.file.Files;
  7. import java.nio.file.Paths;
  8. import java.util.Base64;
  9. import java.security.PrivateKey;
  10. import java.security.spec.PKCS8EncodedKeySpec;
  11. import java.security.spec.InvalidKeySpecException;
  12. import java.security.KeyFactory;
  13. import java.security.NoSuchAlgorithmException;
  14. import java.security.PublicKey;
  15. import java.security.spec.RSAPublicKeySpec;
  16. import java.security.interfaces.RSAPrivateCrtKey;
  17. import java.security.interfaces.RSAPublicKey;
  18. class JwksEndpoint {
  19. public static void main(String[] args) {
  20. // *******************************
  21. // Load private key data from file
  22. // *******************************
  23. String filePath = "rs256.pem";
  24. String privateKeyContent;
  25. try {
  26. privateKeyContent = new String(Files.readAllBytes(Paths.get(filePath)))
  27. .replace("-----BEGIN PRIVATE KEY-----", "")
  28. .replace("-----END PRIVATE KEY-----", "")
  29. .replace("\n", "");
  30. } catch (IOException e) {
  31. throw new RuntimeException(e);
  32. }
  33. // ******************
  34. // Create private key
  35. // ******************
  36. KeyFactory keyFactory;
  37. try {
  38. keyFactory = KeyFactory.getInstance("RSA");
  39. } catch (NoSuchAlgorithmException e) {
  40. throw new RuntimeException(e);
  41. }
  42. PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
  43. Base64.getDecoder().decode(privateKeyContent)
  44. );
  45. PrivateKey privateKey; // Use this key also to sign JWT!
  46. try {
  47. privateKey = keyFactory.generatePrivate(privateKeySpec);
  48. } catch (InvalidKeySpecException e) {
  49. throw new RuntimeException(e);
  50. }
  51. // *****************
  52. // Create public key
  53. // *****************
  54. RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(
  55. ((RSAPrivateCrtKey) privateKey).getModulus(),
  56. ((RSAPrivateCrtKey) privateKey).getPublicExponent()
  57. );
  58. PublicKey publicKey;
  59. try {
  60. publicKey = keyFactory.generatePublic(publicKeySpec);
  61. } catch (InvalidKeySpecException e) {
  62. throw new RuntimeException(e);
  63. }
  64. // *********************
  65. // Prepare JWKS response
  66. // *********************
  67. RSAPublicKey rsa = (RSAPublicKey) publicKey;
  68. Map<String, Object> keyObject = new HashMap<>();
  69. keyObject.put("kty", rsa.getAlgorithm());
  70. keyObject.put("kid", "1");
  71. keyObject.put(
  72. "n",
  73. Base64.getUrlEncoder().encodeToString(rsa.getModulus().toByteArray())
  74. );
  75. keyObject.put(
  76. "e",
  77. Base64.getUrlEncoder().encodeToString(rsa.getPublicExponent().toByteArray())
  78. );
  79. keyObject.put("alg", "RS256");
  80. keyObject.put("use", "sig");
  81. List<Map<String, Object>> keyList = new ArrayList<>();
  82. keyList.add(keyObject);
  83. Map<String, List<Map<String, Object>>> jwksResponse = new HashMap<>();
  84. jwksResponse.put("keys", keyList);
  85. System.out.println(jwksResponse);
  86. }
  87. }


你可能需要一些像org.json或Jackson这样的库来将JWKS数据序列化为JSON,除非你想使用StringBuilder。

展开查看全部

相关问题