By marone: August 2020 | last update: September 2020

Verify jws signature



Table of contents

Goal

About 3 years ago we wrote an article on how to create and review JWT. At that time we used the JJWT Library. Check out how to create and verify jwt.
Afterward, we wanted to know exactly how the signature validation works. Preferably without using third-party libraries. Simply said, just using only Java built-in functions.

JWT, JWS and JWE

JSON Web Token (JWT) is an open standard (RFC 7519) format for representing claims between two parties.
There are two ways to represent JWT, JWS and JWE. verify jwt signature A signed JWT/JWS token can be additionally encrypted to provide more integrity and confidentiality.

Used tools

JDK 1.8
Maven 3.2

How jws signature works

The Signature is calculated by using the Header and Payload data, a signing algorithm, and public key or a secret. The recipient can use the signature to find out if the message was not changed

Verify signature with secret

In this example the token was created with HS512 Algorithm and passing a secret, for more details go to this article.
verify jwt signature with secret
    @Test
    public void verfiySignature() throws Exception {
        String token = generateJwtToken();
        String[] parts = token.split("\\.");
        String calculatedSignature = calculateSignature(parts[0], parts[1], "12345678");
        assertTrue(parts[2].equals(calculatedSignature));
    }

    private String calculateSignature(String header, String payload, String secretKey) throws Exception {
        SecretKeySpec secret = new SecretKeySpec(secretKey.getBytes(), "HmacSHA512");
        Mac mac = Mac.getInstance("HmacSHA512");
        mac.init(secret);

        String body = header + "." + payload;
        byte[] hmacBytes = mac.doFinal(body.getBytes(StandardCharsets.UTF_8.name()));
        String calculatedSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(hmacBytes);

        return calculatedSignature;
    }

The verfiySignature method takes three parameters: header, payload and the secret. The header and payload are encoded whereas the secret value in plaintext.
Header and payload will be concatted with a period separator (.).
The MAC class uses HmacSHA512 algorithm to calculate the Message Authentication Code. The decision to use HmacSHA512 was taken from the header {alg=HS512}
At the end the calculated value from MAC class should equal the value of the delivered signature.

Verify signature with public key

Now we want to deal with a jwt token, which was created by an RSA Private Key. In order to verify the signature, we need the Public key. Here you can find article about how to create jwt token with RSA.

verify jwt signature with public key

   public static boolean verify(PublicKey publicKey, String header, String payload, String signature) {
        try {
            String data = header + "." + payload;
            Signature sig = Signature.getInstance("SHA256withRSA");
            sig.initVerify(publicKey);
            sig.update(data.getBytes());
            byte[] decodedSignature = Base64.getUrlDecoder().decode(signature);
            return sig.verify(decodedSignature);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

According to the header the signature should use SHA256withRSA.

Conclusion

Creating jwt with secret means that the same secret will be used to create and verify the signature. In contrast, an RSA based signature is generated by using the private key which is owned by the sender, whereas the recipient has to use the public key.

References