jwtrsajjwt

How to create a Java Key object from a base64 endoded strings for PS256 parsing with JJWT


I'm using JJWT to attempt to create and later verify JWT keys. It doesn't work when I try and convert the keys to Strings and vice versa - for storage for later use.

This example work fine:

    KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.PS256);
    Key publicKey = keyPair.getPublic();
    Key privateKey = keyPair.getPrivate();

    Claims claims = Jwts.claims();
    claims.setIssuedAt(new Date());

    String jws = Jwts.builder().setSubject("Joe").claim("Hello", "World").signWith(privateKey).compact();

    boolean result = Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(jws).getBody().getSubject().equals("Joe");

    System.out.println("Verified:" + result);
    return "";

However, when I convert the keys to Base64Encoded strings as such:

    String base64Public = Encoders.BASE64.encode(publicKey.getEncoded());
    String base64Private = Encoders.BASE64.encode(privateKey.getEncoded());

How do I load the public key (from a String) back into the algorithm and verify a JWS using the public key?

String base64Public = Encoders.BASE64.encode(publicKey.getEncoded());
boolean result2 = Jwts.parserBuilder().setSigningKey(base64Public).build().parseClaimsJws(jws).getBody().getSubject().equals("Joe");

fails with:

Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance.


Solution

  • The exported public key has the X.509/SPKI format and must be imported accordingly:

    import java.security.KeyFactory;
    import java.security.spec.X509EncodedKeySpec;
    import java.security.PublicKey;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.io.Encoders;
    import io.jsonwebtoken.io.Decoders;
    
    ...
    
    // Export
    String base64Public = Encoders.BASE64.encode(publicKey.getEncoded());
            
    // Import
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Decoders.BASE64.decode(base64Public));
    PublicKey publicKeyReloaded = (PublicKey)keyFactory.generatePublic(x509EncodedKeySpec);
    
    // Verification 
    boolean result = Jwts.parserBuilder().setSigningKey(publicKeyReloaded).build().parseClaimsJws(jws).getBody().getSubject().equals("Joe");
    System.out.println("Verified:" + result); // Verified:true
    

    Edit:

    The private key has PKCS#8 format and is imported as follows:

    import java.security.KeyFactory;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.PrivateKey;
    
    ...
    
    // Export
    String base64Private = Encoders.BASE64.encode(privateKey.getEncoded());
    
    // Import
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64Private));
    PrivateKey privateKeyReloaded = (PrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    
    // Sign
    String jws = Jwts.builder().setSubject("Joe").claim("Hello", "World").signWith(privateKeyReloaded).compact();
    System.out.println("JWS:" + jws); // e.g. JWS:eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJKb2UiLCJIZWxsbyI6IldvcmxkIn0.bk9EtxqRU3cfn7nMyn7MsDSKTlFUUwxjkWXVXqbpjVacEd6lEVG2jmkLSQ2oAoiA8fmKTlSXnULUKhv4XvDbvG2_BIx22JpceuYVdFhbvzkxv3EffPYrsYXftqws0vo-Wg05ubXk7qfeyIs9S-oq_Jf-5w_2oe6GLlcBqnNu-wLy8gAMiKNQPtuE7PmCT9ZEE7ALg_aGMBl2ttOEYN6bQcgxkbJLiS9pWm_RQbPsRCF34Q7alrETQPFltVJOPXd34aMPTaWSkYlyccj-0gVv8p5-BRpsGc3M9XaZWnwLm5CYzZ7tpfcd0BhKtkEO5mSFU7jo4P_T8BWCbEn2jYyzPA