22

Auth0 provides two JWT libraries, one for Node: node-jsonwebtoken, and one for Java: java-jwt.

I created the private/public key pair, and used it successfully in Node with node-jsonwebtoken:

var key = fs.readFileSync('private.key');
var pem = fs.readFileSync('public.pem');

var header = {...};
var payload = {...};

header.algorithm = "RS256";
var message = jsonwebtoken.sign(payload, key, header);
var decoded = jsonwebtoken.verify(message, pem, {algorithm: "RS256"});

But I found no way of doing the same in Java with java-jwt.

Anyone has a working example of how to use private/public keys for JWT in Java?

1
  • 1
    Hi Predrag, I have the same requirement where I have to create a signed token with private key using java and verify the token with public key in node JS .. I am struck with how to load the privatekey (private key.pem) file and create the signed token with RS256 algorithm.. it would be great if you share any samples. Commented Jun 2, 2019 at 16:33

5 Answers 5

36

I used the following code for JWT in Java. Try it.

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JWTJavaWithPublicPrivateKey {

    public static void main(String[] args) {

        System.out.println("generating keys");
        Map<String, Object> rsaKeys = null;

        try {
            rsaKeys = getRSAKeys();
        } catch (Exception e) {

            e.printStackTrace();
        }
        PublicKey publicKey = (PublicKey) rsaKeys.get("public");
        PrivateKey privateKey = (PrivateKey) rsaKeys.get("private");

        System.out.println("generated keys");

        String token = generateToken(privateKey);
        System.out.println("Generated Token:\n" + token);

        verifyToken(token, publicKey);

    }

    public static String generateToken(PrivateKey privateKey) {
        String token = null;
        try {
            Map<String, Object> claims = new HashMap<String, Object>();

            // put your information into claim
            claims.put("id", "xxx");
            claims.put("role", "user");
            claims.put("created", new Date());

            token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS512, privateKey).compact();


        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    // verify and get claims using public key

    private static Claims verifyToken(String token, PublicKey publicKey) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();

            System.out.println(claims.get("id"));
            System.out.println(claims.get("role"));

        } catch (Exception e) {

            claims = null;
        }
        return claims;
    }

    // Get RSA keys. Uses key size of 2048.
    private static Map<String, Object> getRSAKeys() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        Map<String, Object> keys = new HashMap<String, Object>();
        keys.put("private", privateKey);
        keys.put("public", publicKey);
        return keys;
    }
}

Maven Dependency

<dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.6.0</version>
</dependency>
Sign up to request clarification or add additional context in comments.

4 Comments

how can we use existing key, (already generated keys, stored in files)?
byte[] b1 = Base64.getDecoder().decode(privateKeyString); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey privateKey = kf.generatePrivate(spec);
This helped me a lot while trying to get the private key read from the external file such as application.yml in my case. Thanks @Manu
Use an existing key. read it from a file and then process validation.
15

Recent versions (since 3.0.0) of the auth0 java-jwt library supports RSA and ECDSA for signing JWT tokens using a public/private key pair.

Example of signing a JWT using java-jwt (based on the documentation).

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Map;

class JwtPKSigningExample {
    public static void main(String[] args) throws Exception {
        Map<String, Object> keys = generateRSAKeys();

        String token = null;
        try {
            RSAPrivateKey privateKey = (RSAPrivateKey) keys.get("private");
            Algorithm algorithm = Algorithm.RSA256(null, privateKey);
            token = JWT.create()
                    .withIssuer("pk-signing-example")
                    .sign(algorithm);
        } catch (JWTCreationException x) {
            throw x;
        }

        try {
            RSAPublicKey publicKey = (RSAPublicKey) keys.get("public");
            Algorithm algorithm = Algorithm.RSA256(publicKey, null);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("pk-signing-example")
                    .build();
            DecodedJWT jwt = verifier.verify(token);

            System.out.println(jwt.getToken());
        } catch (JWTVerificationException x) {
            throw x;
        }
    }

    private static Map<String, Object> generateRSAKeys() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);

        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        return Map.of("private", keyPair.getPrivate(), "public", keyPair.getPublic());
    }
}

Comments

2

That particular library doesn't support it. But you can check others for Java that do. See here: https://jwt.io/

Comments

1
    package com.java;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Enumeration;

import org.jose4j.json.internal.json_simple.parser.ParseException;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
import org.jose4j.keys.resolvers.VerificationKeyResolver;
import org.jose4j.lang.JoseException;

public class JWTSigningAndVerification {

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

        String jwt = generateJWT();
        validateJWTwithJWKS(jwt);
    }

    private static String generateJWT() throws FileNotFoundException, KeyStoreException, IOException,
            NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, JoseException {

        JwtClaims jwt_claims = new JwtClaims();

        jwt_claims.setSubject("sub");
        jwt_claims.setIssuer("https://domain");
        jwt_claims.setIssuedAtToNow();
        jwt_claims.setExpirationTimeMinutesInTheFuture(1000000);
        jwt_claims.setGeneratedJwtId();
        jwt_claims.setClaim("sid", "sessionid");
        jwt_claims.setClaim("email", "[email protected]");
        jwt_claims.setClaim("given_name", "first");
        jwt_claims.setClaim("family_name", "last");

        JsonWebSignature jws = new JsonWebSignature();
        jws.setPayload(jwt_claims.toJson());
        String KeyPassword = "p12-key-password";
        File file = new File("path-to-key.p12");
        InputStream stream = new FileInputStream(file);
        KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
        store.load(stream, KeyPassword.toCharArray());
        Enumeration e = store.aliases();
        String alias = (String) e.nextElement();
        PrivateKey key = (PrivateKey) store.getKey(alias, KeyPassword.toCharArray());
        jws.setKey(key);
        jws.setKeyIdHeaderValue("1");
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_PSS_USING_SHA512);
        jws.setHeader("typ", "JWT");

        String jwt = jws.getCompactSerialization();
        System.out.println(jwt);

        return jwt;
    }

    private static void validateJWTwithJWKS(String jwt) throws JoseException, FileNotFoundException, IOException,
            ParseException, InvalidJwtException, MalformedClaimException {

        JsonWebKeySet jsonWebKeySet = new JsonWebKeySet("json-jwks-escaped");
        VerificationKeyResolver verificationKeyResolver = new JwksVerificationKeyResolver(jsonWebKeySet.getJsonWebKeys());

        JwtConsumer jwtConsumer = new JwtConsumerBuilder().setVerificationKeyResolver(verificationKeyResolver).build();

        JwtClaims claims = jwtConsumer.processToClaims(jwt);
        System.out.println("sub:- " + claims.getSubject());
    }

}

1 Comment

This code block can be used to sign a JWT and verify it using JWKS using jose4j library, let me know if you face any issues using it.
1

If you have public key and private key pem files.

openssl genrsa -aes128 -passout pass:REPLACE_THIS -out privkey.pem 4096
openssl rsa -in privkey.pem -passin pass:REPLACE_THIS -pubout -out public_key.pem

In Java code, include below depedency in pom.xml:

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk18on</artifactId>
        <version>1.78</version>
    </dependency>

Credit to answer at link Github Gist

Create this class:

package com.zzz.xxx.util;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class PublicPrivateKey {
    // using bouncycastle to generate public key
    public static PublicKey getPublicKey() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        KeyFactory factory = KeyFactory.getInstance("RSA");
        File file = new File("E:\\PATH\\public_key.pem");
        try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)) {
            PemObject pemObject = pemReader.readPemObject();
            byte[] content = pemObject.getContent();
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
            PublicKey publicKey = factory.generatePublic(pubKeySpec);
            return publicKey;
        }
    }

    public static PrivateKey getPrivateKey() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        KeyFactory factory = KeyFactory.getInstance("RSA");
        File file = new File("E:\\PATH\\private_key.pem");
        try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)) {
            PemObject pemObject = pemReader.readPemObject();
            byte[] content = pemObject.getContent();
            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);
            PrivateKey privateKey = factory.generatePrivate(privateKeySpec);
            return privateKey;
        }
    }
}

And then use it where needed (example below):

return Jwts.builder()
        .setSubject(subject)
        .setIssuedAt(new Date())
        .setExpiration(expiryDate)
        //.signWith(SignatureAlgorithm.HS512, appProperties.getAuth().getTokenSecret())
        .signWith(privateKey, SignatureAlgorithm.RS512)
        .compact();

1 Comment

In case public key in pem format is available, one doesn't need bouncycastle dependecy at all - it suffices to just base64 decode the actual file content (of course w/o BEGIN and END lines) into a byte[]. Otherwise exactly what I needed! Thanks

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.