JSON Web Token (JWT) with RSA signature

RSA-based JSON Web Signatures (JWS) provide integrity, authenticity and non-repudation to JSON Web Tokens (JWT).

The minimum recommended RSA key size is 2048 bits.

The Nimbus JOSE+JWT supports all standard RSA digital signature algorithms:

  • RS256 - RSA PKCS#1 signature with SHA-256
  • RS384 - RSA PKCS#1 signature with SHA-384
  • RS512 - RSA PKCS#1 signature with SHA-512
  • PS256 - RSA PSS signature with SHA-256
  • PS384 - RSA PSS signature with SHA-384
  • PS512 - RSA PSS signature with SHA-512

The PKCS#1 type of RSA signatures is the most widely used and supported.

The example uses the key ID ("kid") parameter of the JWS header to indicate the signing key and simplify key roll-over. The exact method by which the recipient establishes the public RSA key candidate(s) to check the signature must be specified by the application's security protocol.

The JWT includes a set of claims or assertions, packaged in a JSON object. Note that the SignedJWT.verify method only checks the validity of the signature. The claims, which treatment is application specific, must therefore be subsequently checked by your application code.

Example code:

import java.util.Date;

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


// RSA signatures require a public and private RSA key pair, the public key 
// must be made known to the JWS recipient in order to verify the signatures
RSAKey rsaJWK = new RSAKeyGenerator(2048)
    .keyID("123")
    .generate();
RSAKey rsaPublicJWK = rsaJWK.toPublicJWK();

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

// Prepare JWT with claims set
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
    .subject("alice")
    .issuer("https://c2id.com")
    .expirationTime(new Date(new Date().getTime() + 60 * 1000))
    .build();

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
// mZq3ivwoAjqa1uUkSBKFIX7ATndFF5ivnt-m8uApHO4kfIFOrW7w2Ezmlg3Qd
// maXlS9DhN0nUk_hGI3amEjkKd0BWYCB8vfUbUv0XGjQip78AI4z1PrFRNidm7
// -jPDm5Iq0SZnjKjCNS5Q15fokXZc8u0A
String s = signedJWT.serialize();

// On the consumer side, parse the JWS and verify its RSA signature
signedJWT = SignedJWT.parse(s);

JWSVerifier verifier = new RSASSAVerifier(rsaPublicJWK);
assertTrue(signedJWT.verify(verifier));

// Retrieve / verify the JWT claims according to the app requirements
assertEquals("alice", signedJWT.getJWTClaimsSet().getSubject());
assertEquals("https://c2id.com", signedJWT.getJWTClaimsSet().getIssuer());
assertTrue(new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime()));