如何在 Java 中使用令牌和公钥验证 JWT 签名

Posted

技术标签:

【中文标题】如何在 Java 中使用令牌和公钥验证 JWT 签名【英文标题】:How to verify JWT signature using a token and public key in Java 【发布时间】:2018-01-06 23:20:11 【问题描述】:

我有一个字符串形式的令牌,我下载了公共证书并从中创建了一个公钥,如下所示。

但我不确定如何使用这么多信息进行验证。

我找到了适用于 C# 和 .NET 的解决方案,但没有找到适用于 Java 的解决方案。 请注意,我没有 jks 文件或私钥。

    FileInputStream fin = new FileInputStream("d://public.crt");
    CertificateFactory f = CertificateFactory.getInstance("X.509");
    X509Certificate certificate = (X509Certificate)f.generateCertificate(fin);
    PublicKey pk = certificate.getPublicKey();

【问题讨论】:

【参考方案1】:

如果您询问 JSON WebToken,您可以按照以下代码示例进行操作:

import javax.xml.bind.DatatypeConverter;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Claims;

//Sample method to validate and read the JWT
private void parseJWT(String jwt) 

    //This line will throw an exception if it is not a signed JWS (as expected)
    Claims claims = Jwts.parser()         
       .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
       .parseClaimsJws(jwt).getBody();
    System.out.println("ID: " + claims.getId());
    System.out.println("Subject: " + claims.getSubject());
    System.out.println("Issuer: " + claims.getIssuer());
    System.out.println("Expiration: " + claims.getExpiration());

更多阅读,可以访问点击here

【讨论】:

什么是 apiKey 对象,鉴于我只有一个公钥证书文件,我如何获取它的秘密。 apiKey 是 com.stormpath.sdk.api.ApiKey 的对象,用于存放 apikey。字符串路径 = "资源/.stormpath/apiKey.properties"; ApiKey apiKey = ApiKeys.builder().setFileLocation(path).build(); 这不是回答用户的问题。使用密钥验证是使用 HS256 (hmac),而使用公钥验证是 RS256。【参考方案2】:

使用 Auth0 库 (com.auth0:java-jwt) 在 Java 中验证 JWT:

    检索已签署密钥的算法,例如:

    // Load your public key from a file
    final PublicKey ecdsa256PublicKey = getPublicKey(...);
    final Algorithm algorithm = Algorithm.ECDSA256((ECPublicKey) ecdsa256PublicKey, null);
    

    使用相应的算法验证其签名:

    final DecodedJWT decodedJWT = JWT.decode("J.W.T[...]");
    // Will throw a SignatureVerificationException if the token's signature is invalid
    algorithm.verify(decodedJWT);
    

【讨论】:

【参考方案3】:

我做了这样的事情来验证 JWT

try 
        DecodedJWT decodedJWT = JWT.decode(jwt); // your string
        JwkProvider provider =  new JwkProviderBuilder(new URL("JWKS URL")).build();
        Jwk jwk = provider.get(decodedJWT.getKeyId());
        Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);

        Verification verifier = JWT.require(algorithm);
        verifier.build().verify(decodedJWT);
     catch (JWTVerificationException | JwkException | MalformedURLException e) 
        // throw your exception
    

JwkProviderBuilder 可能很昂贵,因此如果您使用的是 Spring,您可以将其提取为另一种方法并使用 @PostConstruct 进行注释以进行优化。

【讨论】:

我已经试过了,程序在 "provider.get(decodedJWT.getKeyId())" 行 您遇到的错误是什么?您确定您传递的 JWT 或 JWKs URL 正确吗?【参考方案4】:

使用 RSA 密钥的工作示例如下所示:

/* Verification of JWT */
try 
    String token = "some-token";
    String publicKey = "some-key";
    
    //Convert public key string to RSAPublicKey
    byte[] publicKeyByteArr = Base64.getDecoder().decode(publicKey);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKey rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyByteArr));

    //If the token has an invalid signature, JWTVerificationException will raise.
    Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null);
    JWTVerifier verifier = JWT.require(algorithm)
                //.withIssuer("auth0")
                .build(); //Reusable verifier instance
    DecodedJWT jwt = verifier.verify(token);

catch(InvalidKeySpecException | NoSuchAlgorithmException | JWTVerificationException e) 
    logger.info("JWT verification is failed");
    throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);

很明显,但请注意,token 和 publicKey 是任意的。

【讨论】:

以上是关于如何在 Java 中使用令牌和公钥验证 JWT 签名的主要内容,如果未能解决你的问题,请参考以下文章

如何验证 Apple 登录的 Jwt 令牌(后端验证)。如何从 Java 中的模数和指数 (n,e) 生成 RSA 公钥

如何在 AspNetCore 中仅使用公钥验证使用 x509 签名的 JWT 令牌

如何在节点中使用 x.509 证书验证 JWT 令牌?

Spring Boot OAuth2 资源服务器:库如何在没有公钥的情况下验证 JWT 令牌?

Okta 使用 OpenID Connect 配置中的公钥验证 JWT 令牌

JWT 令牌签名验证 javascript