How to generate a JSON Web Key (JWK)

JSON Web Keys (JWK) can be easily generated with the help of the Nimbus JOSE+JWT library:

Cryptographic keys can also be generated in another environment and then converted into JWK format. Here is an example how to import a key generated with OpenSSL.

You can also check out the command line JWK generator by Justin Richer built with this library.

RSA key pair

The only required parameter to generate an RSA key pair is the key length, which should be at least 2048 bits.

The JWK format allows the key to be decorated with metadata. An important piece of metadata is the key ID ("kid"), for key identification in databases and enabling key rollover. The usage parameter ("use") indicates the key's intended purpose - signing or encryption.

import java.util.*;

import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

// Generate 2048-bit RSA key pair in JWK format, attach some metadata
RSAKey jwk = new RSAKeyGenerator(2048)
    .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key (optional)
    .keyID(UUID.randomUUID().toString()) // give the key a unique ID (optional)
    .issueTime(new Date()) // issued-at timestamp (optional)
    .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());

An RSA key pair can also be generated with the standard Java cryptographic facilities and then converted to JWK format:

import java.security.*;
import java.security.interfaces.*;
import java.util.*;

import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

// Generate the RSA key pair
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair keyPair = gen.generateKeyPair();

// Convert to JWK format
JWK jwk = new RSAKey.Builder((RSAPublicKey)keyPair.getPublic())
    .privateKey((RSAPrivateKey)keyPair.getPrivate())
    .keyUse(KeyUse.SIGNATURE)
    .keyID(UUID.randomUUID().toString())
    .issueTime(new Date())
    .build();

A generated RSA key pair in JWK format:

{
  "kty" : "RSA",
  "kid" : "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df",
  "use" : "sig",
  "n"   : "pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w",
  "e"   : "AQAB",
  "d"   : "ksDmucdMJXkFGZxiomNHnroOZxe8AmDLDGO1vhs-POa5PZM7mtUPonxwjVmthmpbZzla-kg55OFfO7YcXhg-Hm2OWTKwm73_rLh3JavaHjvBqsVKuorX3V3RYkSro6HyYIzFJ1Ek7sLxbjDRcDOj4ievSX0oN9l-JZhaDYlPlci5uJsoqro_YrE0PRRWVhtGynd-_aWgQv1YzkfZuMD-hJtDi1Im2humOWxA4eZrFs9eG-whXcOvaSwO4sSGbS99ecQZHM2TcdXeAs1PvjVgQ_dKnZlGN3lTWoWfQP55Z7Tgt8Nf1q4ZAKd-NlMe-7iqCFfsnFwXjSiaOa2CRGZn-Q",
  "p"   : "4A5nU4ahEww7B65yuzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ--wwfpRwHvSxtNU9qXb8ewo-BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3InKF4JvIlchyqs0RQ8wx7lULqwnn0",
  "q"   : "ven83GM6SfrmO-TBHbjTk6JhP_3CMsIvmSdo4KrbQNvp4vHO3w1_0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEBpxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA-k4UoH_eQmGKGK44TRzYj5hZYGWIC8",
  "dp"  : "lmmU_AG5SGxBhJqb8wxfNXDPJjf__i92BgJT2Vp4pskBbr5PGoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ-m0_XSWx13v9t9DIbheAtgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpE",
  "dq"  : "mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe__EjuCBbwHfcT8OG3hWOv8vpzokQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p-AF2p6Yfahscjtq-GY9cB85NxLy2IXCC0PF--Sq9LOrTE9QV988SJy_yUrAjcZ5MmECk",
  "qi"  : "ldHXIrEmMZVaNwGzDF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uYiqewXfCKw_UngrJt8Xwfq1Zruz0YY869zPN4GiE9-9rzdZB33RBw8kIOquY3MK74FMwCihYx_LiU2YTHkaoJ3ncvtvg"
}

EC key pair

Elliptic Curve (EC) keys are based on curves with specific mathematical properties. The JOSE WG adopted three standard curves for EC keys and EC operations with the following designations: P-256, P-384 and P-521. The use of another curve, secp256k1, with JOSE was specified in RFC 8812.

EC signature algorithm Requires EC JWK with curve
ES256 P-256
ES256K secp256k1
ES384 P-384
ES512 P-521

To generate an EC key pair specify its curve:

import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

// Generate EC key pair in JWK format
ECKey jwk = new ECKeyGenerator(Curve.P_256)
    .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key (optional)
    .keyID(UUID.randomUUID().toString()) // give the key a unique ID (optional)
    .issueTime(new Date()) // issued-at timestamp (optional)
    .generate();

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

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

To generate an EC key pair with the standard Java facilities and convert it to JWK format:

import java.security.*;
import java.security.interfaces.*;
import java.util.*;

import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

// Generate EC key pair with P-256 curve
KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
gen.initialize(Curve.P_256.toECParameterSpec());
KeyPair keyPair = gen.generateKeyPair();

// Convert to JWK format
JWK jwk = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic())
    .privateKey((ECPrivateKey) keyPair.getPrivate())
    .build();

A generated EC P-256 key pair in JWK format:

{
  "kty" : "EC",
  "crv" : "P-256",
  "x"   : "SVqB4JcUD6lsfvqMr-OKUNUphdNn64Eay60978ZlL74",
  "y"   : "lf0u0pMj4lGAzZix5u4Cm5CMQIgMNpkwy163wtKYVKI",
  "d"   : "0g5vAEKzugrXaRbgKG0Tj2qJ5lMP4Bezds1_sTybkfk"
}

secp256k1

The secp256k1 curve was supported by the default (built-in) Java Cryptography Architecture (JCA) provider in Java 7, 8 and LTS 11, but unfortunately is no longer available in Java LTS 17. To generate an EC key with this curve you can resort to the alternative BouncyCastle JCA provider:

import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton;

ECKey jwk = new ECKeyGenerator(Curve.SECP256K1)
    .keyUse(KeyUse.SIGNATURE)
    .keyID("1")
    .provider(BouncyCastleProviderSingleton.getInstance())
    .generate();

Remember to also set the BouncyCastle provider when signing and verifying secp256k1 curve signatures.

Octet key pair

Octet key pairs are used to represent Edwards curve keys. They bear the JWK type designation "OKP" and are used for JSON Web Signatures (JWS) with Ed25519 / Ed448 and JSON Web Encryption (JWE) with ECDH with X25519 / X448.

Starting with v6.0 the Nimbus JOSE+JWT library can generate OKP JWKs with an Ed25519 or X25519 curve with help of the optional Tink dependency. Edwards curve cryptography is not supported by the standard Java JCA yet. Check the library's pom.xml for the expected Tink dependency version. Nimbus JOSE+JWT v9.26 expects the following Tink dependency:

<dependency>
    <groupId>com.google.crypto.tink</groupId>
    <artifactId>tink</artifactId>
    <version>1.7.0</version>
</dependency>

To generate an OKP JWK just specify the name of the Edwards curve and any key metadata required by your application:

import java.util.*;

import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

// Generate Ed25519 Octet key pair in JWK format, attach some metadata
OctetKeyPair jwk = new OctetKeyPairGenerator(Curve.Ed25519)
    .keyUse(KeyUse.SIGNATURE) // indicate the intended use of the key (optional)
    .keyID(UUID.randomUUID().toString()) // give the key a unique ID (optional)
    .issueTime(new Date()) // issued-at timestamp (optional)
    .generate();

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

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

Example Ed25519 key in JWK format:

{
  "kty" : "OKP",
  "crv" : "Ed25519",
  "x"   : "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
  "d"   : "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
  "use" : "sig",
  "kid" : "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ"
}

Octet sequence key

The octet sequence JWK format is intended for representing secret keys, such as keys for use in HMAC and AES. A secret key is essentially a random array of bytes that cannot be practically guessed.

HMAC key

HMAC computation requires a secret key which length must match the size of the output hash. You can also use longer keys, but they will be truncated.

HMAC algorithm Required key size
HS256 256 bits
HS384 384 bits
HS512 512 bits

To a generate a secret 256-bit JWK for HS216:

import java.util.*;

import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

OctetSequenceKey jwk = new OctetSequenceKeyGenerator(256)
    .keyID(UUID.randomUUID().toString()) // give the key some ID (optional)
    .algorithm(JWSAlgorithm.HS256) // indicate the intended key alg (optional)
    .issueTime(new Date()) // issued-at timestamp (optional)
    .generate();

// Output
System.out.println(jwk);

You can also use Java's SecureRandom or the dedicated KeyGenerator to generate the key bytes and then use the bytes to create a JWK:

import javax.crypto.*;

import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.*;

// Generate a secret key with 256 bits
SecretKey hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey();

// Convert to JWK format
JWK jwk = new OctetSequenceKey.Builder(hmacKey)
    .keyID(UUID.randomUUID().toString())
    .algorithm(JWSAlgorithm.HS256)
    .issueTime(new Date())
    .build();

Example secret key in JWK format:

{ 
  "kty" : "oct",
  "kid" : "0afee142-a0af-4410-abcc-9f2d44ff45b5",
  "alg" : "HS256",
  "k"   : "FdFYFzERwC2uCBB46pZQi4GG85LujR8obt-KWRBICVQ"
}

AES key

Symmetric JWE requires an AES key. For example, direct encryption with A128GCM requires a 128 bit AES key.

As with HMAC above, you can use the provided the OctetSequenceKeyGenerator or Java's standard KeyGenerator.

To generate a 128-bit AES JWK directly:

import java.util.*;

import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.gen.*;

OctetSequenceKey jwk = new OctetSequenceKeyGenerator(128)
    .keyID(UUID.randomUUID().toString()) // give the key some ID (optional)
    .algorithm(EncryptionMethod.A128GCM) // indicate the intended key alg (optional)
    .issueTime(new Date()) // issued-at timestamp (optional)
    .generate();

// Output
System.out.println(jwk);

To generate the AES key using Java's standard facility, then convert to JWK format:

import javax.crypto.*;

import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.*;

// Generate a secret AES key with 128 bits
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(128);
SecretKey aesKey = gen.generateKey();

// Convert to JWK format
JWK jwk = new OctetSequenceKey.Builder(aesKey)
    .keyID(UUID.randomUUID().toString())
    .algorithm(EncryptionMethod.A128GCM)
    .issueTime(new Date())
    .build();

Example 128 bit AES key as JWK:

{
  "kty" : "oct",
  "kid" : "283e6c58-7962-411a-8468-8b26d6f8e89a",
  "k"   : "c7WsUB6msAgIdDxTnT13Yw"
}