【问题标题】:Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance只能为 HMAC 签名指定密钥字节。请指定 PublicKey 或 PrivateKey 实例
【发布时间】:2020-05-19 04:03:33
【问题描述】:

我正在尝试读取从 Google OpenID Connect 的 Id 令牌生成的 Json Web 令牌(JWT),以获取声明并使用 jjwt 库进行验证。我已经尝试了几种方法来使用下面的代码修复它。


 String publicKeyFromJsonFile = "-----BEGIN PUBLIC KEY-----xxxxxxx-----END PUBLIC KEY-----"

 Claims claims = Jwts.parser()
                .setSigningKey(publicKeyFromJsonFile)
                .parseClaimsJws(jwt).getBody();

 System.out.println(claims);


但我收到此错误:

java.lang.IllegalArgumentException: Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance

请问什么是正确的方法?

【问题讨论】:

  • 看起来您必须使用 KeyFactory 解析您的公钥,然后将生成的实例传递给您的库。
  • 嘿 @michalk .... 我最终用这段代码 sn-p 修复了 publicKey ---------------- --- X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.getDecoder().decode(PUBLIC_KEY)); ________ RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(keySpecX509);
  • 它与那个 sn-p 临时工作得很好。它现在给出不同的错误。也就是说,JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted .......我不知道这是否是你真正推荐的。如果没有,你能帮帮我吗?谢谢。

标签: java openid-connect jjwt


【解决方案1】:

我想我已经能够通过将 publicKey 解析为 RSAPublicKey 来解决问题。下面是我如何去做的细节。

 public static Optional<RSAPublicKey> getParsedPublicKey(){
       // public key content...excluding '---PUBLIC KEY---' and '---END PUBLIC KEY---'
        String PUB_KEY =System.getenv("PUBLIC_KEY") ; 

       // removes white spaces or char 20
        String PUBLIC_KEY = "";
          if (!PUB_KEY.isEmpty()) {
            PUBLIC_KEY = PUB_KEY.replace(" ", "");
        }

        try {
            byte[] decode = com.google.api.client.util.Base64.decodeBase64(PUBLIC_KEY);
            X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(decode);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(keySpecX509);
            return Optional.of(pubKey);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
            System.out.println("Exception block | Public key parsing error ");
            return Optional.empty();
        }

希望它有所帮助:)。

【讨论】:

    【解决方案2】:

    这可以像下面这样完成,使用私钥签名生成令牌并使用公钥解析声明

      @Configuration
      public class KeyGeneratorConfig {
    
    
        @Value("${jwt.privateKey}")
        private String privateKey; //Encoded private key string
    
    
        @Value("${jwt.publicKey}")
        private String publicKey;//Encoded public key string
    
    
        @Bean
        public PrivateKey generatePrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
    
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PKCS8EncodedKeySpec privKeySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
            return kf.generatePrivate(privKeySpecPKCS8);
        }
    
        @Bean
        public PublicKey generatePublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
    
            KeyFactory kf = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pubKeySpecX509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
            return kf.generatePublic(pubKeySpecX509EncodedKeySpec);
        }
    }
    

    生成令牌和解析可以这样完成

    @Autowired
    private PublicKey publicKey;
    
    @Autowired
    private PrivateKey privateKey;
    
    private String doGenerateToken(Map claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate("token"))
                .signWith(SignatureAlgorithm.RS512, privateKey)
                .compact();
    }
    
    public Claims getClaimsFromToken(String token) throws ExpiredJwtException, UnsupportedJwtException,
            MalformedJwtException, SignatureException, IllegalArgumentException {
        Claims claims;
    
        claims = Jwts.parser()
                .setSigningKey(publicKey)
                .parseClaimsJws(token)
                .getBody();
    
        return claims;
    }
    

    【讨论】:

      【解决方案3】:

      一个好的方法是使用 JWT.IO 网页手动验证令牌 - 如 my article 中所述 - 然后到 apply the equivalent code - 尽管我的代码是 NodeJS。

      对您如何手动验证 id 令牌感兴趣 - 您能否解释一下涉及哪些客户端和 API - 可能有更标准的方式来实现您的目标。

      【讨论】:

      • 好的。因此,我使用 Angular 在客户端使用 Google OpenID 方法生成 tokenID,并将令牌向下传递到 Java(特别是 Spring MVC)中的服务器端,以使用jjwt 进行验证,并将响应发送回客户端之前和微-如果令牌实际有效,则可以访问服务。希望你明白:)
      • 好的 - 听起来很标准,但通常是向 API 发送访问令牌而不是 id 令牌,因为访问令牌是可以更新的短期 API 凭证。否则,您的 JWT 验证技术方法看起来不错。
      猜你喜欢
      • 2020-10-01
      • 2018-10-16
      • 2017-01-30
      • 2022-09-25
      • 2016-04-15
      • 2010-11-05
      • 2011-09-03
      • 2012-05-31
      • 2018-02-01
      相关资源
      最近更新 更多