Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am trying to read a Json Web Token(JWT) generated from Google OpenID Connect's Id Token in order to get the claims and to verify using jjwt library. I have tried several ways to fix it with the code below.


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

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

 System.out.println(claims);


but I am getting this error:

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

Please what could be the right approach to follow ?

like image 512
John Erbynn Avatar asked Feb 03 '20 11:02

John Erbynn


2 Answers

I think I have been able to fix the gotcha by parsing the publicKey as an RSAPublicKey. Below is the nitty-gritty of how i went about it.

 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();
        }

Hope it helps :).

like image 66
John Erbynn Avatar answered Oct 28 '22 10:10

John Erbynn


This can be done like below, generate a token using private key signing and parse claims with the public key

  @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);
    }
}

and generating token and parsing can be done like this

@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;
}
like image 2
Suresh Kb Avatar answered Oct 28 '22 10:10

Suresh Kb