Creating a JWK

March 1, 2020

I wanted to create a JWK so that I could self-sign my JWT tokens during unit testing. The following code will generate a new JWK:

package com.drumcoder.diary.test.support.keygen;

import java.util.UUID;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;

/**
 * This program will generate a JWK key which can then be used for signing a JWT
 * during test code.
 *
 */
public class GenerateJwk {

    public static void main(String[] args) throws JOSEException {

        // Generate 2048-bit RSA key pair in JWK format, attach some metadata
        final RSAKey jwk = new RSAKeyGenerator(2048).keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key
                .keyID(UUID.randomUUID().toString()) // give the key a unique ID
                .generate();

        // Output the private and public RSA JWK parameters
        System.out.println(jwk);

        // Output the public RSA JWK parameters only
        System.out.println(jwk.toPublicJWK());

    }
}

I used the values generated from this code in two constants:

private static final String TEST_PRIVATE_KEY = "{\"p\":\"6PTEoZQWtYRlqQS7...\"}";
private static final String TEST_PUBLIC_KEY = "{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"...}";

The following code can then be used to generate a bearer token:

private String createTestLoginAndCreateJwt(int expiryMinutes, List<String> roles, String username) 
        throws MalformedURLException, IOException, ParseException, JOSEException {
        final RSAKey rsaJWK = this.fetchPublicKey("NoTenant", "TEST");
        final RSAKey rsaPrivateJWK = RSAKey.parse(TEST_PRIVATE_KEY);

        // Create RSA-signer with the private key
        final JWSSigner signer = new RSASSASigner(rsaPrivateJWK);

        // Prepare JWT with claims set
        JSONArray claimsList = new JSONArray();
        if (roles != null) {
            for (String role : roles) {
                claimsList.add(role);
            }
        } else {
            claimsList.add("DiaryUser");
        }
        System.out.println(claimsList);
        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(username)
                    .issuer("https://test.drumcoder.co.uk")
                    .expirationTime(new Date(new Date().getTime() + (60 * 1000 * expiryMinutes)))
                    .claim("roles", claimsList).build();

        final SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJWK.getKeyID()).build(), claimsSet);

        // Compute the RSA signature
        signedJWT.sign(signer);

        // To serialize to compact form, produces something like
        // eyJhbGciOiJSUzI1NiJ9.SW4gUlNBIHdlIHRydXN0IQ.IRMQENi4nJyp4er2L...
        final String s = signedJWT.serialize();
        System.out.println(s);
        return s;
    }

    private RSAKey fetchPublicKey(String tenantId, String kid) throws MalformedURLException, IOException, ParseException {
        return RSAKey.parse(TEST_PUBLIC_KEY);
    }

The JWT produced can be checked at http://jwt.io