fusionauth / fusionauth-jwt Goto Github PK
View Code? Open in Web Editor NEWA simple to use Java 8 JWT Library. Verify, Sign, Encode, Decode all day.
Home Page: https://fusionauth.io
License: Apache License 2.0
A simple to use Java 8 JWT Library. Verify, Sign, Encode, Decode all day.
Home Page: https://fusionauth.io
License: Apache License 2.0
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.
How many people would I break if I moved this to Java >= 14?
Feel free to comment on this thread.
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}\""
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.
hi, please tell me are these objects thread-safe or not.
For example Gson instead Jackson.
Move json to another maven module and allow to add it as separate jar.
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.
Had a friend review the repo and they had some suggestions on improving the README to be friendlier new folks.
Suggestions in this issue:
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
.
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.
Add support EDDSA
https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-4
https://en.wikipedia.org/wiki/EdDSA
Please doublecheck this is a valid issue. According to JWS §4.1.1 "alg" Header Parameter MUST be present and MUST be understood and processed by implementations.
In my opinion that means that if "alg" is not "none", then signature must be present and verified. Attached
JwtTest.java.txt demonstrates the problem.
Will Grant Negotiation and Authorization Protocol (GNAP) working code be made available soon?
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.
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 😄
like this:
{
"e": "AQAB",
"kid": "uncK0PWuznliRSr2pB4vtgcdLKYBnTngUV70GPc0Yxc",
"kty": "RSA",
"n": "..."
}
It can been gen by under python code
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)
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 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
.
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!
}
}
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!
}
}
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?
Fix padding on the EC signature when decoding r
and s
from the DER encoded value
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.
Make the timeout value for URL connections configurable, not hardcoded.
The timeouts for connecting to endpoints are hardcoded in AbstractHttpHelper.buildURLConnection(). They are too short for our environments.
properties or constructor based timeout options
using our own impl for JWK, but we'd rather not because we like fusionauth
this is especially problematic when working with various dev environments
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.
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
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.
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.
Add support for PS256, PS384, PS512
Additional context:
https://bitbucket.org/b_c/jose4j/issues/129/rsassa-pss-support-in-java-11
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?
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:
- Let the Message be the octets of the UTF-8 representation of the JWT Claims Set.
RFC8414 defines OAuth 2.0 Authorization Server Metadata which is the OAuth2-specific implementation of OpenID Connect Discovery.
Would you be interested in having support for this, similar to io.fusionauth.jwks.JSONWebKeySetHelper#retrieveKeysFromIssuer
?
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?
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.
io.fusionauth:fusionauth-jwt:4.0.1 uses:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
but this dependency has several security vulnerabilities:
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-14060
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-14061
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-14062
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-14195
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-24616
https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-24750
Version 2.12.1 is now available: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.12.1 and it includes the fixes.
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.
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.
In HMACVerifier.java Arrays.equals
is used to check whether the signature is valid or not. RFC 7518 states:
The comparison of the computed HMAC value to the JWS Signature value MUST be done in a constant-time manner to thwart timing attacks.
As far as i know, Arrays.equals
is not constant-time.
Please add a module descriptor.
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.
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-----
Hi, do you have an existing benchmark of JWT token decoding? If not this might be interesting.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.