Skip to content
Connect2id

Frequently Asked Questions

Why are JWS ECDSA signatures with high-S values accepted?

EDSA signatures in JWS, as defined in RFC 7518, section 3.4, are inherently malleable. For a valid signature (R, S), the value (R, n − S) is also a valid signature over the same data, where n is the order of the elliptic curve.

This follows from the referenced ECDSA verification procedure in FIPS 186-5, section 6.4.2. The procedure ensures correctness and key security, not canonicalisation of the signature.

As a result, the same JWS signing input can produce multiple distinct, valid signature values. Because of that JWS serialisations are to be regarded as messages, not as canonical identifiers.

Having said that, there is a feature ticket for an option to enforce low-S on ECDSA validation. This can be used by applications where signature uniqueness matters.

I appended an extra char to the BASE64URL signature and the JWS validation still passes, this must be a bug!

According to RFC 7515, the signature validation is performed at the byte level, not by comparing the BASE64URL encodings.

Appending an extra character to a BASE64URL string will not necessarily modify the underlying bytes as returned by the BASE64 decoder.

Example JWS RSA signature:

pWOUF4Gkgqy9Il7KYfIWpS99m1K17-LdMDukwD6LqKol3RUZYc3XF0HzJ6F97iXFcK76FHgESDlpm3-
44AlA1zfUDclx6aHKXfJPA-0yB5fPGocJf7kURMh6tCdTkdbi

Decoded to bytes, in hex:

a5 63 94 17 81 a4 82 ac bd 22 5e ca 61 f2 16 a5 2f 7d 9b 52 b5 ef e2 dd 30 3b
a4 c0 3e 8b a8 aa 25 dd 15 19 61 cd d7 17 41 f3 27 a1 7d ee 25 c5 70 ae fa 14
78 04 48 39 69 9b 7f b8 e0 09 40 d7 37 d4 0d c9 71 e9 a1 ca 5d f2 4f 03 ed 32
07 97 cf 1a 87 09 7f b9 14 44 c8 7a b4 27 53 91 d6 e2

Let’s add the a character at the end of the JWS signature:

pWOUF4Gkgqy9Il7KYfIWpS99m1K17-LdMDukwD6LqKol3RUZYc3XF0HzJ6F97iXFcK76FHgESDlpm3-
44AlA1zfUDclx6aHKXfJPA-0yB5fPGocJf7kURMh6tCdTkdbia

The BASE64 decoder returns the same bytes as before:

a5 63 94 17 81 a4 82 ac bd 22 5e ca 61 f2 16 a5 2f 7d 9b 52 b5 ef e2 dd 30 3b
a4 c0 3e 8b a8 aa 25 dd 15 19 61 cd d7 17 41 f3 27 a1 7d ee 25 c5 70 ae fa 14
78 04 48 39 69 9b 7f b8 e0 09 40 d7 37 d4 0d c9 71 e9 a1 ca 5d f2 4f 03 ed 32
07 97 cf 1a 87 09 7f b9 14 44 c8 7a b4 27 53 91 d6 e2

This is because the extra character at the end contributes only six bits, which are insufficient to form a complete byte, so they are discarded by the library BASE64 decoder.

A survey of commonly available BASE64 implementations in Java shows two typical behaviours when encountering an incomplete trailing byte:

  • Discard the incomplete trailing bits and return the decoded bytes.
  • Reject the input by throwing an exception.

Both approaches ensure that only complete bytes are returned from the decoding process.

The Nimbus JOSE+JWT library uses a BASE64URL codec with branch-reduced (timing-hardened) digit conversion routines, but it is not strictly constant-time overall. The timing-hardened routines aim to reduce the risk of side-channel leaks when processing sensitive material (for example, private keys), where data-dependent branching could otherwise leak information via timing differences. The decoder ignores non-alphabet characters and discards any incomplete trailing bits that do not form a full byte. This behaviour is permitted by RFC 4648, which allows decoders to be either strict or lenient when processing input.

Illegal key size exception

Are you getting an java.security.InvalidKeyException with an Illegal key size message?

The standard Java edition is subject to a cryptographic export restriction that limits AES key sizes to 128 bits (and other keys to a similar strength). Attempting to use longer keys will result in the above exception.

To enable longer keys you need to add a couple of policy files provided by Oracle to your Java installation.

Unsupported algorithm / cipher / etc exception

The Nimbus JOSE+JWT library uses Java’s pluggable architecture (JCA) to perform most underlying crypto operations for JWS and JWE.

A java.security.NoSuchAlgorithmException indicates that your Java runtime is missing support for the JWS or JWE algorithms that you want to use.

com.nimbusds.jose.JOSEException: Couldn't create AES/GCM/NoPadding cipher: Cannot find any provider supporting AES/GCM/NoPadding

	at com.nimbusds.jose.crypto.AESGCM.encrypt(AESGCM.java:93)
	at com.nimbusds.jose.crypto.ContentCryptoProvider.encrypt(ContentCryptoProvider.java:169)
	at com.nimbusds.jose.crypto.DirectEncrypter.encrypt(DirectEncrypter.java:118)
	at com.nimbusds.jose.JWEObject.encrypt(JWEObject.java:353)
    ...
Caused by: java.security.NoSuchAlgorithmException: Cannot find any provider supporting AES/GCM/NoPadding
	at javax.crypto.Cipher.getInstance(Cipher.java:529)
	at com.nimbusds.jose.crypto.AESGCM.encrypt(AESGCM.java:85)
	... 34 more

In that case follow the instructions to add a JCA provider for the missing algorithms.

RemoteJWKSet times out

Increase the timeouts (in milliseconds) from their default values, zero implies no timeout.

System.out.println("Default connect timeout: " + RemoteJWKSet.DEFAULT_HTTP_CONNECT_TIMEOUT + "ms");
System.out.println("Default read timeout: " + RemoteJWKSet.DEFAULT_HTTP_READ_TIMEOUT + "ms");

int connectTimeoutMs = 1500;
int readTimeoutMs = 1500;
new RemoteJWKSet(url, new DefaultResourceRetriever(connectTimeoutMs, readTimeoutMs));

How to include the key ID (kid) in the JOSE header?

The key ID is an arbitrary string used to identify the key used in a JWS or JWE. An OpenID Connect server for instance uses an RSA public / private key pair to secure the issued identity tokens. The private RSA key is used to sign the JWS, while the public key (made available to clients for download) is used to verify the JWS signature.

It’s a good security practise to expire keys after a certain time, so a new RSA key pair is periodically generated and added under a new unique ID to the JWK set while retaining the previous keys.

Receiving a JWS object with a key ID for which the client has no previously cached public RSA key acts as a signal for the client to go and fetch the new key from the OpenID Connect server.

How to add a key ID to the JWS message:

String keyID = "001";

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256)
    .keyID(keyID)
    .build();

JWSObject jws = new JWSObject(header, new Payload("Hello world!"));

If you have a JWK:

RSAKey rsaJWK = RSAKey.parse(string);

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256)
    .keyID(rsaJWK.getKeyID())
    .build();

JWSObject jws = new JWSObject(header, new Payload("Hello world!"));

Note that only JWS and JWE objects support the key ID header parameter. Unsecured (plain) objects and JWTs don’t allow it.

To retrieve the key ID from a JWS or JWE object:

JWSObject jws = JWSObject.parse(string);
String keyID = jws.getHeader().getKeyID();

Is the Samsung Knox key store supported?

Yes, the TIMA key store is supported because it’s PKCS#11 compliant. Just use the TIMAKeyStore keyword when instantiating a KeyStore object for it:

// Load and init the key store
KeyStore timaKeyStore = KeyStore.getInstance("TIMAKeyStore");
timaKeyStore.load(null, null);

You can then use the KeyStore methods to retrieve the desired key for signing or encryption. You can also obtain a JWK set representation of all keys found in store:

JWKSet jwkSet = JWKSet.load(timaKeyStore, null);

How to limit the accepted JWS / JWE algorithms?

Note: This feature was removed in version 4.0. Use the new JOSE / JWT processing framework instead.

The JWSVerifier and JWEDecrypter interfaces provide a handy setAcceptedAlgorithms method for that.

Say a JWS verifier supports algorithms X, Y and Z. You however want the verifier to allow only JWS messages with algorithms X and Y only, and reject those with Z. That’s what the accepted algorithms setter does:

Set<JWSAlgorithm> acceptedAlgs = new HashSet<>();
acceptedAlgs.add(JWSAlgorithm.X);
acceptedAlgs.add(JWSAlgorithm.Y);

jwsVerifier.setAcceptedAlgorithms(acceptedAlgs);

How to check if a JWT string is signed or encrypted?

Use the generic com.nimbusds.jwt.JWTParser:

JWT jwt;

try {
	jwt = JWTParser.parse(jwtString);
} catch (ParseException e) {
	// Invalid JWT...
}

if (jwt instanceof SignedJWT) {
	// We have a JWS protected JWT
} else if (jwt instanceof EncryptedJWT) {
 	// We have a JWT protected JWT
} else {
	// We have an unsecured (plain) JWT
}

What is the difference between JCA and JCE?

The JCA acronym stands for Java Cryptography Architecture. It defines a pluggable framework for cryptography in Java in the java.security package and its descendants. The JCA APIs cover encryption, key generation and management, secure random number generation and certificates.

The JCE acronym stands for Java Cryptography Extension. It provides an official framework and JCA implementation, which classes are found in the javax.crypto package.

For more info check out Suresh Yadagiri’s article.

Will an upgrade / change to my BouncyCastle version break things?

No, it shouldn’t. The Nimbus JOSE+JWT library doesn’t call any BouncyCastle APIs directly. All crypto calls are instead routed via the standard Java crypto APIs, which then determines the appropriate implementation (builtin, BouncyCastle or other).

For more info: See plugging JCA algorithms.

My security tool complains about the JCIP annotations dependency

The JCIP dependency contains 4 helpful annotations to mark a class as @Immutable, @ThreadSafe, @NotThreadSafe or @GuardedBy. This tiny library has no executable code and being a simple, finite enumeration of annotations can be considered feature complete forever.

Still, security tools can be quite dumb and may raise a flag because the dependency hasn’t changed in say 5 years, while being totally oblivious to the fact that the JCIP library is just these four annotations and there’s nothing to “update” or “maintain” in it. Maybe the age of “AI” will make these security tools wiser. Maybe not.

I’m getting a block size exception on RSA encryption

If you’re getting an IllegalBlockSizeException this is an indication that the RSA encryption key is too short for the chosen JWE alg + enc combination, e.g. RSA-OAEP-256 with A256CBC-HS512 content encryption:

javax.crypto.IllegalBlockSizeException: Data must not be longer
than 62 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.nimbusds.jose.crypto.RSA_OAEP_256.encryptCEK(RSA_OAEP_256.java:73)
... 38 more

The solution is to switch to a longer RSA key. An RSA key size of 2048 bits will be sufficiently long for any of the standard JOSE algorithms with RSA based encryption.

To make debugging easier for developers version 4.31.1 of the library updated the JOSEException code to return a more informative message:

RSA block size exception: The RSA key is too short, try a longer one