【问题标题】:Java offline validation of JWT access token from Keycloak来自 Keycloak 的 JWT 访问令牌的 Java 离线验证
【发布时间】:2020-09-24 21:41:26
【问题描述】:

我从 Keycloak http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/token 获得签名的 JWT 令牌:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqQThGdzdhRk1rTGhGc2......",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzMWRjZTBjNS01MGU0LTQxZjMtODAxNC1kMTcyMjdk....",
"token_type": "bearer",
"not-before-policy": 0,
"session_state": "bd1728eb-ceda-43cf-a6e6-d637ba0da5e3",
"scope": "email profile"
}

所以访问令牌是:

eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqQThGdzdhRk1rTGhGc2......

我在 Keycloak 中查询http://localhost:8080/auth/realms/Myrealm/

{
"realm": "MyRealm",
"public_key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKQF8ewcJ/pDzhgzbTfFCoS1FLjZyO5z7CbmeWl.......",
"token-service": "http://localhost:8080/auth/realms/Myrealm/protocol/openid-connect",
"account-service": "http://localhost:8080/auth/realms/Myrealm/account",
"tokens-not-before": 0
}

所以公钥是:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKQF8ewcJ/pDzhgzbTfFCoS1FLjZyO5z7CbmeWl.......

现在我正在尝试进行如下离线验证:

import java.security.KeyFactory;
import java.security.spec.X509EncodedKeySpec;
import java.security.PublicKey;

String keyFromKeycloak = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKQF8ewcJ/pDzhgzbTfFCoS1FLjZyO5z7CbmeWl.......";
byte[] bytes = keyFromKeycloak.getBytes();
KeyFactory factory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(bytes);
PublicKey pk = factory.generatePublic(encodedKeySpec);

但是我得到了无效密钥的异常:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
    at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:239)
    at java.base/java.security.KeyFactory.generatePublic(KeyFactory.java:352)
Caused by: java.security.InvalidKeyException: invalid key format
    at java.base/sun.security.x509.X509Key.decode(X509Key.java:386)
    at java.base/sun.security.x509.X509Key.decode(X509Key.java:401)
    at java.base/sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:122)
    at java.base/sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:330)
    at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:235)
    ... 3 more

我需要 PublicKey 对象来验证访问令牌,如下所示:

Algorithm algorithm = Algorithm.RSA256(
    (RSAPublicKey) pk,
    null);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);

怎么了?我看到很多带有此代码的示例,所以我怀疑问题出在密钥本身。

【问题讨论】:

  • 使用 Base64.getDecoder().decode(keyFromKeycloak) 并将这个字节数组传递给 x509 规范。
  • 我错过了。谢谢!发布答案,以便我接受。
  • 能否分享您用于第二个 sn-p 代码的库/包名称:AlgorithmJWTJWTVerifierDecodedJWT
  • @xbmono 是 com.auth0.java-jwt

标签: java validation jwt keycloak


【解决方案1】:

您的 keyFromKeycloak 字符串是 Base64 编码的 DER SubjectPublicKeyInfo。您应该首先对其进行解码,然后将其传递给X509EncodedKeySpec 构造函数:

String keyFromKeycloak = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKQF8ewcJ/pDzhgzbTfFCoS1FLjZyO5z7CbmeWl.......";
byte[] keyBytes = Base64.getDecoder().decode(keyFromKeycloak);
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(keyBytes);

【讨论】:

    猜你喜欢
    • 2022-07-04
    • 2021-08-24
    • 1970-01-01
    • 2018-06-24
    • 2020-10-27
    • 2021-11-16
    • 2022-10-15
    • 2019-07-28
    • 2021-09-01
    相关资源
    最近更新 更多