Git Product home page Git Product logo

fusionauth-jwt's Introduction

FusionAuth JWT semver 2.0.0 compliant Tests

FusionAuth JWT is intended to be fast and easy to use. FusionAuth JWT has a single external dependency on Jackson, no Bouncy Castle, Apache Commons or Guava.

Security disclosures

If you find a vulnerability or other security related bug, please send a note to [email protected] before opening a GitHub issue. This will allow us to assess the disclosure and prepare a fix prior to a public disclosure.

We are very interested in compensating anyone that can identify a security related bug or vulnerability and properly disclose it to us.

Features

  • JWT signing using HMAC, RSA and Elliptic Curve support
    • HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512
  • JWT signing using RSA-PSS signatures
  • Modular crypto provider so you can drop in support for BC FIPS or other JCE security providers.
  • PEM decoding / encoding
    • Decode PEM files to PrivateKey or PublicKey
      • Decode private EC keys un-encapsulated in PKCS#8, returned PEM will be in PKCS#8 form.
      • Both public and private keys will be returned when encoded in the private PEM
    • Encode PrivateKey or PublicKey to PEM
  • JSON Web Key
    • Build JWK from Private Key
    • Build JWK from Public Key
    • Build JWK from PEM
    • Parse public keys from a JSON Web Key
    • Retrieve JWK from JWKS endpoints
  • Helpers
    • Generate RSA Key Pairs in 2048, 3072 or 4096 bit sizes
    • Generate EC Key Pairs in 256, 384 and 521 bit sizes
    • Generate x5t and x5t#256 values from X.509 Certificates
    • Generate JWK thumbprint using SHA-1 or SHA-256
    • Generate ideal HMAC secret lengths for SHA-256, SHA-384 and SHA-512
    • Generate the at_hash and c_hash claims for OpenID Connect

Get it

Maven

<dependency>
 <groupId>io.fusionauth</groupId>
 <artifactId>fusionauth-jwt</artifactId>
 <version>5.3.2</version>
</dependency>

Gradle

implementation 'io.fusionauth:fusionauth-jwt:5.3.2'

Gradle Kotlin

implementation("io.fusionauth:fusionauth-jwt:5.3.2")

Savant

dependency(id: "io.fusionauth:fusionauth-jwt:5.3.2")

For others see https://search.maven.org.

Example Code:

JWT Signing and Verifying

Sign and encode a JWT using HMAC

// Build an HMAC signer using a SHA-256 hash
Signer signer = HMACSigner.newSHA256Signer("too many secrets");

// Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)
JWT jwt = new JWT().setIssuer("www.acme.com")
                   .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
                   .setSubject("f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3")
                   .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60));
                       
// Sign and encode the JWT to a JSON string representation
String encodedJWT = JWT.getEncoder().encode(jwt, signer);

A higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.

// Build an HMAC signer using a SHA-384 hash
Signer signer384 = HMACSigner.newSHA384Signer("too many secrets");

// Build an HMAC signer using a SHA-512 hash
Signer signer512 = HMACSigner.newSHA512Signer("too many secrets");

Verify and decode a JWT using HMAC

// Build an HMC verifier using the same secret that was used to sign the JWT
Verifier verifier = HMACVerifier.newVerifier("too many secrets");

// Verify and decode the encoded string JWT to a rich object
JWT jwt = JWT.getDecoder().decode(encodedJWT, verifier);

// Assert the subject of the JWT is as expected
assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");

Sign and encode a JWT using RSA

// Build an RSA signer using a SHA-256 hash. A signer may also be built using the PrivateKey object.
Signer signer = RSASigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));

// Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)
JWT jwt = new JWT().setIssuer("www.acme.com")
                   .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
                   .setSubject("f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3")
                   .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60));
        
// Sign and encode the JWT to a JSON string representation
String encodedJWT = JWT.getEncoder().encode(jwt, signer);

A higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.

// Build an RSA signer using a SHA-384 hash
Signer signer = RSASigner.newSHA384Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));

// Build an RSA signer using a SHA5124 hash
Signer signer = RSASigner.newSHA512Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));

Verify and decode a JWT using RSA

// Build an RSA verifier using an RSA Public Key. A verifier may also be built using the PublicKey object.
Verifier verifier = RSAVerifier.newVerifier(Paths.get("public_key.pem"));

// Verify and decode the encoded string JWT to a rich object
JWT jwt = JWT.getDecoder().decode(encodedJWT, verifier);

// Assert the subject of the JWT is as expected
assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");

Sign and encode a JWT using EC

// Build an EC signer using a SHA-256 hash. A signer may also be built using the PrivateKey object.
Signer signer = ECSigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));

// Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp)
JWT jwt = new JWT().setIssuer("www.acme.com")
                   .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC))
                   .setSubject("f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3")
                   .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60));
        
// Sign and encode the JWT to a JSON string representation
String encodedJWT = JWT.getEncoder().encode(jwt, signer);

A higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.

// Build an EC signer using a SHA-384 hash
Signer signer = ECSigner.newSHA384Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));

// Build an EC signer using a SHA-512 hash
Signer signer = ECSigner.newSHA512Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));

Verify and decode a JWT using EC

// Build an EC verifier using an EC Public Key. A verifier may also be built using the PublicKey object.
Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem"));

// Verify and decode the encoded string JWT to a rich object
JWT jwt = JWT.getDecoder().decode(encodedJWT, verifier);

// Assert the subject of the JWT is as expected
assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");

Verify a JWT adjusting for Clock Skew

// Build an EC verifier using an EC Public Key
Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem"));

// Verify and decode the encoded string JWT to a rich object and allow up to 60 seconds
// of clock skew when asserting the 'exp' and 'nbf' claims if they exist.
JWT jwt = JWT.getDecoder().withClockSkew(60).decode(encodedJWT, verifier);

// Assert the subject of the JWT is as expected
assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");

Verify an expired JWT by going back in time

In a scenario where you may have a hard coded JWT in a test case that you wish to validate, you may use the time machine JWT decoder. Ideally you would not hard code JWTs in your tests and instead generate a new one each time so that the JWT would pass the expiration check. If this is not possible, this option is provided.

// Build an EC verifier using an EC Public Key
Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem"));

// Using the time machine decoder, you may adjust 'now' to any point in the past, or future.
// Note, this is only provided for testing, and should not be used in production.
ZonedDateTime thePast = ZonedDateTime.of(2019, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC) 
JWT jwt = JWT.getTimeMachineDecoder(thePast).decode(encodedJWT, verifier);

// Assert the subject of the JWT is as expected
assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");

Build a Signer, or a Verifier using a provided CryptoProvider

This pattern is available on the HMAC, RSA and EC verifier and signers.

// Build and EC signer using a BC Fips ready Crypto Provider
Signer signer = ECSigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))), new BCFIPSCryptoProvider());

// Build an EC verifier using a BC Fips ready Crypto Provider
Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem"), new BCFIPSCryptoProvider());

JSON Web Keys

Retrieve JSON Web Keys from a JWKS endpoint

// Retrieve JSON Web Keys using a known JWKS endpoint
// - You may optionally provide a HttpURLConnection to this method instead of a string if you want to build your own connection.
List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromJWKS("https://www.googleapis.com/oauth2/v3/certs");

// Retrieve JSON Web Keys using a well known OpenID Connect configuration endpoint
// - You may optionally provide a HttpURLConnection to this method instead of a string if you want to build your own connection.
List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromWellKnownConfiguration("https://accounts.google.com/.well-known/openid-configuration");

// Retrieve JSON Web Keys using an OpenID Connect issuer endpoint
List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromIssuer("https://accounts.google.com");

Convert a Public Key to JWK

JSONWebKey jwk = JSONWebKey.build(publicKey);
String json = jwk.toJSON();
{
  "e": "AQAB",
  "kty": "RSA",
  "n": "Auchby3lZKHbiAZrTkJh79hJvgC3W7STSS4y6UZEhhxx3m3W2hD8qCyw6BEyrciPpwou-vmeDN7qBSk2QKqTTjlg5Pkf8O4z8d9HAlBTUDg4p98qLFOF2EFWWTiFbQwAP2qODOIv9WCAM2rkXEPwGiF962XAoOwiSmldeDu7Uo5A-bnTi0z3oNu4qm_48kv90o9CMiELszE9jsfoH32WE71HDqhsRjVNddDJ81e5zxBN8UEmaR-gmWqa63laON2KANPugJP7PrYJ_PC9ilQfV3F1rDpqbvlFQkshohJ39VrVpEtSRmJ12nqTFuspXLApekOyic3J9jo6ZI7o3IdQmy3bpnJIT_U",
  "use": "sig"
}

Extract the Public Key from a JWK

{
  "e": "AQAB",
  "kty": "RSA",
  "n": "Auchby3lZKHbiAZrTkJh79hJvgC3W7STSS4y6UZEhhxx3m3W2hD8qCyw6BEyrciPpwou-vmeDN7qBSk2QKqTTjlg5Pkf8O4z8d9HAlBTUDg4p98qLFOF2EFWWTiFbQwAP2qODOIv9WCAM2rkXEPwGiF962XAoOwiSmldeDu7Uo5A-bnTi0z3oNu4qm_48kv90o9CMiELszE9jsfoH32WE71HDqhsRjVNddDJ81e5zxBN8UEmaR-gmWqa63laON2KANPugJP7PrYJ_PC9ilQfV3F1rDpqbvlFQkshohJ39VrVpEtSRmJ12nqTFuspXLApekOyic3J9jo6ZI7o3IdQmy3bpnJIT_U",
  "use": "sig"
}
String json = { ... example above ... }
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
JSONWebKey jwk = Mapper.deserialize(bytes, JSONWebKey.class);
PublicKey publicKey = JSONWebKey.parse(jwk);

Convert a Private Key to JWK

JSONWebKey jwk = JSONWebKey.build(privateKey);
String json = jwk.toJSON();
{
  "p": "9dy6wUxA0eOHopUP-E5QjDzuW8rXdaQMR566oDJ1qL0iD0koQAB9X3hboB-2Rru0aATu6WDW-jd4mgtYnXO8ow",
  "kty": "RSA",
  "q": "6Nfc6c8meTRkVRAHCF24LB5GLfsjoMB0tOeEO9w9Ous1a4o-D24bAePMUImAp3woFoNDRfWtlNktOqLel5Pjew",
  "d": "C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0",
  "e": "AQAB",
  "use": "sig",
  "qi": "XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw",
  "dp": "32QGgDmjr9GX3N6p2wh1YWa_gMHmUSqUScLseUA_7eijeNYU70pCoCtAvVXzDYPhoJ3S4lQuIL2kI_tpMe8GFw",
  "dq": "21tJjqeN-k-mWhCwX2xTbpTSzsyy4uWMzUTy6aXxtUkTWY2yK70yClS-Df2MS70G0za0MPtjnUAAgSYhB7HWcw",
  "n": "359ZykLITko_McOOKAtpJRVkjS5itwZxzjQidW2X6tBEOYCH4LZbwfj8fGGvlUtzpyuwnYuIlNX8TvZLTenOk45pphXr5PMCMKi7YZgkhd6_t_oeHnXY-4bnDLF1r9OUFKwj6C-mFFM-woKc-62tuK6QJiuc-5bFfn9wRL15K1E"
}

Add a custom property to a JWK

JSONWebKey jwk = JSONWebKey.build(privateKey)
                           .add("boom", "goes the dynamite")
                           .add("more", "cowbell");
String json = jwk.toJSON();
{
  "alg" : "ES256",
  "boom" : "goes the dynamite",
  "crv" : "P-256",
  "kty" : "EC",
  "more" : "cowbell",
  "use" : "sig",
  "x" : "NIWpsIea0qzB22S0utDG8dGFYqEInv9C7ZgZuKtwjno",
  "y" : "iVFFtTgiInz_fjh-n1YqbibnUb2vtBZFs3wPpQw3mc0"
}

Building

Building with Maven

$ mvn install

Building with Savant

$ sb int

Note: If you do not yet have Savant build tool installed, use the following instructions.

mkdir ~/savant
cd ~/savant
wget http://savant.inversoft.org/org/savantbuild/savant-core/2.0.0-RC.6/savant-2.0.0-RC.6.tar.gz
tar xvfz savant-2.0.0-RC.6.tar.gz
ln -s ./savant-2.0.0-RC.6 current
export PATH=$PATH:~/savant/current/bin/

For more information, checkout savantbuild.org.

fusionauth-jwt's People

Contributors

bblackwo avatar jamietanna avatar ju57u5 avatar luankevinferreira avatar mdemille avatar robotdan avatar rsatrio avatar skjolber avatar spwitt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fusionauth-jwt's Issues

Overriding "configureMessageConverters" in spring

When I override configureMessageConverters in Spring:

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

        ObjectMapper mapper = new ObjectMapper();

        mapper.registerModule(new Hibernate5Module());
        mapper.registerModule(new JavaTimeModule());
        
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        converters.add(new MappingJackson2HttpMessageConverter(mapper));

    }

The Verifier throws this exception:

io.fusionauth.jwt.InvalidJWTException: The encoded JWT is not properly Base64 encoded.

Feature: Elliptic Curve support

Overview

The JSON Web Algorithms (JWA) RFC (https://tools.ietf.org/html/rfc7518) specifies several cryptography standards, one of the optional is Elliptic Curve support.

In section 3.1. "alg" (Algorithm) Header Parameter Values for JWS - the following algorithms are recommended for implementation.

   | ES256        | ECDSA using P-256 and SHA-256  | Recommended+       |
   | ES384        | ECDSA using P-384 and SHA-384  | Optional           |
   | ES512        | ECDSA using P-521 and SHA-512  | Optional           |

Add support for these algorithms by adding a new Verifier and Signer impl in a new library called fusionauth-jwt-elliptic.

Example Verifier Impl Stub

public class EllipticVerifier implements Verifier {

  private final byte[] secret;

  private EllipticVerifier(String secret) {
    Objects.requireNonNull(secret);
    this.secret = secret.getBytes(StandardCharsets.UTF_8);
  }

  public static EllipticVerifier newVerifier(String secret) {
    return new EllipticVerifier(secret);
  }

  @Override
  public boolean canVerify(Algorithm algorithm) {
    switch (algorithm) {
      case ES256:
      case ES384:
      case ES512:
        return true;
      default:
        return false;
    }
  }

  @Override
  public void verify(Algorithm algorithm, byte[] payload, byte[] signature) {
    Objects.requireNonNull(algorithm);
    Objects.requireNonNull(payload);
    Objects.requireNonNull(signature);

    // Make me work!
  }
}

Example Signer Impl Stub

public class EllipticSigner implements Signer {

  private final Algorithm algorithm;

  private byte[] secret;

  private Elliptic(Algorithm algorithm, String secret) {
    this.algorithm = algorithm;
    this.secret = secret.getBytes(StandardCharsets.UTF_8);
  }

  public static EllipticSigner newSHA256Signer(String secret) {
    return new HMACSigner(Algorithm.ES256, secret);
  }

  public static EllipticSigner newSHA384Signer(String secret) {
    return new EllipticSigner(Algorithm.ES384, secret);
  }

  public static EllipticSigner newSHA512Signer(String secret) {
    return new EllipticSigner(Algorithm.ES512, secret);
  }

  @Override
  public Algorithm getAlgorithm() {
    return algorithm;
  }

  @Override
  public byte[] sign(String message) {
    Objects.requireNonNull(algorithm);
    Objects.requireNonNull(secret);

    // Make me work!
  }
}

It is not possible to get the claims and the JWT parameters without verify

A verifier is mandatory to get the access to the JWT fields, but if i'm in the client side and i want read informations from JWT is not possibile decode the payload without verification:

byte[] payload = Base64.getUrlDecoder().decode( authResult.getAccessToken().split( "\\." )[ 1 ] );
JWT jwt = Mapper.deserialize( payload, JWT.class );

some helpers could be useful.

Add x5c and verify public key against x5c when extracting a public key from a JSON Web Key

Issue

Inbound thread from Nightwatch Cybersecurity Research

Nightwatch Cybersecurity Research <research@....>
It looks like the "x5c" field is not being used, while the RFC
dictates that "x5c" needs to verify the raw key materials:
https://tools.ietf.org/html/rfc7517#section-4.7

Nightwatch Cybersecurity Research <research@....>
Hi,

If the JWKS endpoint is compromised or the JWKS data is modified while
in transit by an MITM attacker (when the URL is non-HTTPS). Such an
attacker can choose to modify the public key information only and
leave the certificate alone, so an application verifying the
certificate will miss the fact that the public key doesn't match.

There is some discussion on the IETF list ot this affect:
https://mailarchive.ietf.org/arch/msg/jose/f_jo8sfQN6TqzkpmzgzzMb3_kEw/

Thanks

Solution

Add the x5c to the JSON Web Key when parsing a certificate, and when extracting a public key from a JSON Web key, attempt to verify the public key against the x5c if possible.

Solution notes

From what I can gather, a consumer of the JWKs may just use the certificate directly if available in the x5c parameter and not trust or parse the public key components.

However, if we also verify the response has not been modified by verifying the key components such as e, and n for RSA keys, or the x and y coordinates for an EC key, we should be able to detect when the public key represented by the JSON Web key does not match the x5c parameter.

With all that said, it seems to me if you are concerned about the thread of MITM when consuming JWKS, you should only accept JWKS from a TLS endpoint which has HSTS enabled. Or, only accept a JWK if it is published with the x5c and ignore the public key components in the JWK response.

If you were to be accepting a JSON Web Key from an endpoint that had been compromised by a MITM, it would seem to me the easier way to mess with someone is to remove the x5c parameter AND modify the public key components so it could not be verified. I do not know why you'd modify the public key components and NOT remove the x5c.

In any case, it doesn't hurt to perform some additional validation against the JSON Web Key to ensure the public key components match up with the certificate if found in the x5c parameter if we think it may help security some fashion.

Support JWT Fingerprints

RFC7638 JSON Web Key (JWK) Thumbprint describes a standard method to generate the kid field for a given JSON Web Key.

This is commonly required, and given we can generate a JWK from a PEM / PublicKey / PrivateKey, we should be able to use this to generate a kid.

If you're interested in this feature, I'd be happy to submit a PR πŸ˜„

Create a RSASigner.newSHA256Signer which supports PrivateKey instance

We're using the AndroidKeyStore which protects the private exponent (by design for improved security) so it's impossible to call .getEncoded() or retrieve the private key's ASN1 for the PEM.

Your implementation is assuming this is possible, but by doing so, will only support private keys which expose the private exponent which isn't always a good idea.

Ideally rather than hard-coding the pem decoding in your RSASigner constructor, you should be accepting PrivateKey from elsewhere too and allow these to be managed by the respective KeyStore.

How to gen a jwk with kid?

like this:

{
    "e": "AQAB",
    "kid": "uncK0PWuznliRSr2pB4vtgcdLKYBnTngUV70GPc0Yxc",
    "kty": "RSA",
    "n": "..."
}

It can been gen by under python code

need install jwcrypto: pip install jwcrypto

from jwcrypto.jwk import JWK
from pathlib import Path

private_key = Path("./rsa-private-key.pem").read_bytes()
jwk = JWK.from_pem(private_key)
jwk_bytes = jwk.public().export()
print(jwk_bytes)

Best way to pull out "kid" to pick verifier?

I have a feeling I might be doing this wrong.

How do decode a JWT by using its "kid"?

Right now it looks like I use JWTUtils#decodeHeader to get the "kid" before using the Decoder. Then from that I can get the correct PublicKey (jwks) to pass to the decoder.

Or I can make a custom Verifier that again will probably call JWTUtils#decodeHeader.

It seems like this should be kind of built in.

Accept leeway while checking expiration

In some use cases it would be beneficial to allow for a small leeway when checking whether a token has expired, to account for clock differences in client and server machines.

Simple&dirty way could be to overload decode method.

Nicer way would be to extend Verifier with optional leeway parameter in seconds and to use this parameter in the Decoder and to move expiration responsibility to Decoder.

io.fusionauth:fusionauth-jwt:4.0.1 has security vulnerabilities

Wrong module descriptor

Jackson can't deserialize some classes because module doesn't open packages to jackson databind module.

io.fusionauth.jwks.JSONWebKeySetHelper$JSONWebKeySetResponse
Jackson can't access this class.

add-opens fixes this error. Should i make PR?

Embedding JWK does not yield an interoperable result

Our agent has a concept of 'untrusted payloads' this is to allow diagnostics and error reports when the licensing bootstrapping is failing. Untrusted payloads are treated as potentially suspicious, stored separately but allow us to receive important health info should something go wrong in the early stages of setting up an agent.

Untrusted JWT payloads work by embedding the public key into the JWK (hence the name), like so:

The server-side code for receiving this is written in Golang and uses the square/go-jose library. This works perfectly for .NET clients and Golang clients, but I can't get it to work with your JWT library :(

My implementation is below. I am overwriting the JWK header because there doesn't appear to be a way to set this at a higher level, but I think this is correct.

    @Override
    public String signUnTrusted(Key idKey, String mimeType, Map<String, ?> payload) {
        Signer signer = new FusionRSASigner(idKey.getPrivateKey());
        JWT builder = new JWT();
        JSONWebKey jwk = JSONWebKey.build(idKey.getPublicKey());
        for (String key : payload.keySet()) {
            builder = builder.addClaim(key, payload.get(key));
        }
        return JWT.getEncoder().encode(builder, signer, h -> {
            h.set("cty", "application/json");
            h.set("jwk", jwk.toJSON());
        });
    }

The error I am getting from the backend is related to the JSON JWK object not being in the expected format - therefore the resulting JSON is not compatible with what the server is expecting. Given other third-party libraries are working, I suspect your JSON serialisation may have producing something which isn't a valid JWK? Or this library is sensitive and intolerant to your version of the encoding?

unable to read untrusted report, failed to unmarshal JWK: json: cannot unmarshal string into Go value of type jose.rawJSONWebKey: "\"{\\n  \\\"e\\\" : \\\"AQAB\\\",\\n  \\\"kty\\\" : \\\"RSA\\\",\\n  \\\"n\\\" : \\\"qWUW1Psri7WQrVOjT8NG5vtEpwyZFFAcVIau7N6sals7E-ZQtIRfsDhnzsf8RlHeqc3WD9elNpYMoEbKwcKUIpmNbj0J1E5g_6-XXXX...XXXXX-QZzPO0jDmWxThgfpQKHRughcJUgkamkw\\\",\\n  \\\"use\\\" : \\\"sig\\\"\\n}\""

Need Ability to Extend `Header` class

I am using this library in support of the STIR/SHAKEN protocol in telecom. The RFC specifies that the Header of a JWT must have its keys in lexicographical order.

The JWTEncoder class manually builds a header object and then allows a Consumer<Header> to customize it. I would like to be able to subclass the Header class so that I have tighter/easier control over the fields in that class, and still have it used by the encoder.

"The JWT could not be de-serialized."

This is for a kotlin-based Android project.

I'm calling Mapper.deserialize(bytes, JSONWebKey::class.java) and it works completely fine on my dev build from android studio, but in a prod release it throws an InvalidJWTException error with the message "The JWT could not be de-serialized."

Any idea what the issue is? Do I need to add some proguard rules?

JWT signature validation can be bypassed in versions <= 1.3.0

Summary

The prime-jwt implementation allows that any not-signed JWT be decoded and, therefore, validated by JWTDecoder class, even when a Verifier object is provided. This issue affects versions <= 1.3.0.

For security reasons, I'm contacting the developers by email with the necessary technical details.

Description

When the JWT.getDecoder().decode(String, Verifier...) is called, the JWT signature will be ignored due to a lack of validation in JWTDecoder. A new condition should be added in this class to prevent that any encodedJWT without the signature part be decoded if exists at least 1 verifier object.

Configurable timeouts on UrlConnection

Configurable timeouts on UrlConnection

Make the timeout value for URL connections configurable, not hardcoded.

Problem

The timeouts for connecting to endpoints are hardcoded in AbstractHttpHelper.buildURLConnection(). They are too short for our environments.

Solution

properties or constructor based timeout options

Alternatives/workarounds

using our own impl for JWK, but we'd rather not because we like fusionauth

Additional context

this is especially problematic when working with various dev environments

How to vote

Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.

The key/message are converted into bytes in platform-dependant way

I think that the line HMACSigner:41 should better be changed to

this.secret = secret.getBytes(StandardCharsets.US_ASCII);

because otherwise the same string key can interpreted differently on systems configured with different system charset (e.g. UTF-8 and UTF-16), and because of that these systems are not longer interoperable (even though they share the "same" key).

On the same reason the message would not be decoded correctly. I think for message one can use StandardCharsets.UTF_8.

See also JSON Web Token Specification Β§7, item 2:

  1. Let the Message be the octets of the UTF-8 representation of the JWT Claims Set.

2047 vs. 2048

Using this public key (see below), I get this error:
Key length of [2047] is less than the required key length of 2048 bits
with this java snippet of code:
RSAVerifier verifier = RSAVerifier.newVerifier(publicKeyContent);
Can we have an option or parameter to allow 2047 as well as 2048? Or a parameter to turn off this key length check?
See this article
https://randomoracle.wordpress.com/2019/12/04/off-by-one-the-curious-case-of-2047-bit-rsa-keys/

-----BEGIN PUBLIC KEY-----
MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBxbF2xqMaW05S4+qgaWUya
6e2QfXt7hNRFW/z7PlygU5D4lol6dfCiTEkgCHCiuYU7T7tmzNhqlMxKf8cj0XSo
UDvhmAfB9+pLx5hVsqHQlAJA4f5/q3oj7/bT6exfK6xsDlSAlAuxMy/gwVx8Zcbw
zxjFcK6S4o75Lr1zK40MfGKFOcbaNs/ma7F59R5ttXU0Y1gTnup2DZx5Z9TudWsB
jJoAhXV4dZN8uGeneD/2raLbKHWT1lCWzCwSwTSvMefRLwxCxfX+eXA0Vle9zPT8
P8xr2QXOJ7u4VPYjwrQdCpPbPdOENiIBhb0dwU7hrjGjRDZ3O2z1x7VbC7B5oX5b
AgMBAAE=
-----END PUBLIC KEY-----

Decode expired JWT throws Exception

I want to verify an old expired JWT and use the JWT object for some processing.
But currently decode throw an JWTExpiredException when I try to decode old JWT string.
My use case is just to verify the JWT string and get back the JWT object without throwing an exception.

Extend HMACSigner to accept byte[] as secret input

Hi, there is no api which allows creating HMACSigner from byte[], even though HMAC signer constructor will convert String to byte[].
As well, it is incompatible with HMACVerifier which accepts byte[] as input.
So now, to create HMACSigner from byte[] , I need to redefine the whole class.

Incorrectly using alg instead of kty to parse a public key from a JWK

One of our fantastic users pointed out to us:

I am using the fusionauth-jwt Java library (3.5.0) for a project and have run into an issue with the JSONWebKeyParser. It appears that the parse method of that class, which takes a JSONWebKey object and returns a PublicKey object is relying on the presence of the β€˜alg’ parameter of the key. However, this parameter is an optional parameter according to the spec (https://tools.ietf.org/html/rfc7517). I believe that the code for this class could be rewritten to rely on the β€˜kty’ parameter instead, which is a required parameter of a key.

This looks to be correct, and we should be using the kty parameter to extract the public components from the JWK.

Android 7 - Base64 NoClassDefFoundError

I guess that is an issue with the Java version not being as modern as it should but when I try to use it in my Android App (for Android 7, API Level 24) the app crashes.

The call:

JWT.getDecoder().decode(token, verifier)

is trying the to execute the following line:

Header header = Mapper.deserialize(base64Decode(parts[0]), Header.class);

Which fails because the base64 decoder is not fund. On newer Android Versions this works so I assume the library is just not compatible. Any specifications regarding that? I couldn't find anything. Or am I just doing it wrong?

Support Function<String,Verifier> for kid mapping

Hi. Great library!

As we were discussing in #46 on mapping kid to key it would be great if you supported a Function<String, Verifier> instead of a Map<String, Verifier>.

 public JWT decode(String encodedJWT, Function<String, Verifier> verifiers)

For now we have a hack:

         Function<String, @Nullable PublicKey> keyFunction ; //snip
         var verifiers = new AbstractMap<String, @Nullable Verifier>() {

            @Override
            public @Nullable Verifier get(
                    @Nullable Object key) {
                var k = keyFunction.apply((String) key);
                if (k == null)
                    return null;
                return RSAVerifier.newVerifier(k);
            }

            @Override
            public boolean isEmpty() {
                return false;
            }

            @Override
            public Set<Entry<String, Verifier>> entrySet() {
                throw new UnsupportedOperationException();
            }

        };

Obviously this isn't ideal particularly if the original decode method that takes Map calls other things besides isEmpty.

Because we have the hack it isn't urgent.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.