Skip to content

Commit

Permalink
0.11.3 to master (#728)
Browse files Browse the repository at this point in the history
Merged 0.11.3 patch release into mainline development branch
  • Loading branch information
lhazlewood authored Apr 23, 2022
1 parent d91881f commit b784732
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 53 deletions.
51 changes: 50 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,61 @@
## Release Notes

### 0.11.3 (pending release)
### 0.11.4 (pending release)

This patch release:

* Adds additional handling for rare JSON parsing exceptions and wraps them in a `JwtException` to allow the application to handle these conditions as JWT concerns.
* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.12.6.1`.

### 0.11.3

This patch release adds security guards against an ECDSA bug in Java SE versions 15-15.0.6, 17-17.0.2, and 18
([CVE-2022-21449](https://nvd.nist.gov/vuln/detail/CVE-2022-21449)). This patch allows JJWT users using those JVM
versions to upgrade to JJWT 0.11.3, even if they are unable to upgrade their JVM to patched/fixed JVM version in a
timely manner. Note: if your application does not use these JVM versions, you are not exposed to the JVM vulnerability.

Note that the CVE is not a bug within JJWT itself - it is a bug within the above listed JVM versions, and the
JJWT 0.11.3 release adds additional precautions within JJWT in case an application team is not able to upgrade
their JVM in a timely manner.

However, even with these additional JJWT security guards, the root cause of the issue is the JVM, so it **strongly
recommended** to upgrade your JVM to version
15.0.7, 17.0.3, or 18.0.1 or later to ensure the bug does not surface elsewhere in your application code or any other
third party library in your application that may not contain similar security guards.

Issues included in this patch are listed in the [JJWT 0.11.3 milestone](https://github.com/jwtk/jjwt/milestone/24).

#### Backwards Compatibility Warning

In addition to additional protections against
[r or s values of zero in ECDSA signatures](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/), this
release also disables by default legacy DER-encoded signatures that might be included in an ECDSA-signed JWT.
(DER-encoded signatures are not supported by the JWT RFC specifications, so they are not frequently encountered.)

However, if you are using an application that needs to consume such legacy JWTs (either produced by a very
early version of JJWT, or a different JWT library), you may re-enable DER-encoded ECDSA signatures by setting the
`io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported` System property to the _exact_
`String` value `true`. For example:

```java
System.setProperty("io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported", "true");
```

*BUT BE CAREFUL*: **DO NOT** set this System property if your application may run on one of the vulnerable JVMs
noted above (Java SE versions 15-15.0.6, 17-17.0.2, and 18).

You may safely set this property to a `String` value of `true` on all other versions of the JVM if you need to
support these legacy JWTs, *otherwise it is best to ignore (not set) the property entirely*.

#### Credits

Thank you to [Neil Madden](https://neilmadden.blog), the security researcher that first discovered the JVM
vulnerability as covered in his [Psychic Signatures in Java](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/) blog post.

We'd also like to thank Toshiki Sasazaki, a member of [LINE Corporation](https://linecorp.com)'s Application Security
Team as the first person to report the concern directly to the JJWT team, as well as for working with us during testing
leading to our conclusions and subsequent 0.11.3 patch release.

### 0.11.2

This patch release:
Expand Down
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,18 +264,18 @@ If you're building a (non-Android) JDK project, you will want to define the foll
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<version>0.11.3</version>
<scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you are using JDK 10 or earlier and you also want to use
Expand All @@ -295,11 +295,11 @@ If you're building a (non-Android) JDK project, you will want to define the foll

```groovy
dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2',
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-jackson:0.11.2' // or 'io.jsonwebtoken:jjwt-gson:0.11.2' for gson
compile 'io.jsonwebtoken:jjwt-api:0.11.3'
runtime 'io.jsonwebtoken:jjwt-impl:0.11.3',
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-jackson:0.11.3' // or 'io.jsonwebtoken:jjwt-gson:0.11.3' for gson
}
```

Expand All @@ -315,9 +315,9 @@ Add the dependencies to your project:

```groovy
dependencies {
api 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.2') {
api 'io.jsonwebtoken:jjwt-api:0.11.3'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.3'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.3') {
exclude group: 'org.json', module: 'json' //provided by Android natively
}
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
Expand Down Expand Up @@ -1328,7 +1328,7 @@ scope which is the typical JJWT default). That is:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
<scope>compile</scope> <!-- Not runtime -->
</dependency>
```
Expand All @@ -1337,7 +1337,7 @@ scope which is the typical JJWT default). That is:
```groovy
dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.3'
}
```
Expand Down Expand Up @@ -1436,7 +1436,7 @@ scope which is the typical JJWT default). That is:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-gson</artifactId>
<version>0.11.2</version>
<version>0.11.3</version>
<scope>compile</scope> <!-- Not runtime -->
</dependency>
```
Expand All @@ -1445,7 +1445,7 @@ scope which is the typical JJWT default). That is:
```groovy
dependencies {
implementation 'io.jsonwebtoken:jjwt-gson:0.11.2'
implementation 'io.jsonwebtoken:jjwt-gson:0.11.3'
}
```
Expand Down
2 changes: 1 addition & 1 deletion api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion extensions/gson/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion extensions/jackson/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion extensions/orgjson/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion extensions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-root</artifactId>
<version>0.11.3-SNAPSHOT</version>
<version>0.11.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Strings;
import io.jsonwebtoken.security.SignatureException;

import java.security.Key;
import java.security.KeyPair;
Expand Down Expand Up @@ -238,6 +239,15 @@ public static byte[] transcodeSignatureToConcat(final byte[] derSignature, int o
* @throws JwtException If the ECDSA JWS signature format is invalid.
*/
public static byte[] transcodeSignatureToDER(byte[] jwsSignature) throws JwtException {
try {
return concatToDER(jwsSignature);
} catch (Exception e) { // CVE-2022-21449 guard
String msg = "Invalid ECDSA signature format.";
throw new SignatureException(msg, e);
}
}

private static byte[] concatToDER(byte[] jwsSignature) throws ArrayIndexOutOfBoundsException {

int rawLen = jwsSignature.length / 2;

Expand All @@ -247,6 +257,10 @@ public static byte[] transcodeSignatureToDER(byte[] jwsSignature) throws JwtExce
i--;
}

if (i == 0) { // r == 0, JVM bug CVE-2202-21449 guard:
throw new JwtException("Invalid ECDSA Signature format");
}

int j = i;

if (jwsSignature[rawLen - i] < 0) {
Expand All @@ -273,7 +287,7 @@ public static byte[] transcodeSignatureToDER(byte[] jwsSignature) throws JwtExce

int offset;

final byte derSignature[];
final byte[] derSignature;

if (len < 128) {
derSignature = new byte[2 + 2 + j + 2 + l];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public class EllipticCurveSignatureValidator extends EllipticCurveProvider imple
private static final String EC_PUBLIC_KEY_REQD_MSG =
"Elliptic Curve signature validation requires an ECPublicKey instance.";

private static final String DER_ENCODING_SYS_PROPERTY_NAME =
"io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported";

public EllipticCurveSignatureValidator(SignatureAlgorithm alg, Key key) {
super(alg, key);
Assert.isTrue(key instanceof ECPublicKey, EC_PUBLIC_KEY_REQD_MSG);
Expand All @@ -41,14 +44,20 @@ public boolean isValid(byte[] data, byte[] signature) {
PublicKey publicKey = (PublicKey) key;
try {
int expectedSize = getSignatureByteArrayLength(alg);
/**
*
* If the expected size is not valid for JOSE, fall back to ASN.1 DER signature.
* This fallback is for backwards compatibility ONLY (to support tokens generated by previous versions of jjwt)
* and backwards compatibility will possibly be removed in a future version of this library.
*
* **/
byte[] derSignature = expectedSize != signature.length && signature[0] == 0x30 ? signature : EllipticCurveProvider.transcodeSignatureToDER(signature);
/*
* If the expected size is not valid for JOSE, fall back to ASN.1 DER signature IFF the application
* is configured to do so. This fallback is for backwards compatibility ONLY (to support tokens
* generated by early versions of jjwt) and backwards compatibility will be removed in a future
* version of this library. This fallback is only enabled if the system property is set to 'true' due to
* the risk of CVE-2022-21449 attacks on early JVM versions 15, 17 and 18.
*/
//TODO: remove for 1.0 (DER-encoding support is not in the JWT RFCs)
byte[] derSignature;
if (expectedSize != signature.length && signature[0] == 0x30 && "true".equalsIgnoreCase(System.getProperty(DER_ENCODING_SYS_PROPERTY_NAME))) {
derSignature = signature;
} else {
derSignature = EllipticCurveProvider.transcodeSignatureToDER(signature);
}
return doVerify(sig, publicKey, data, derSignature);
} catch (Exception e) {
String msg = "Unable to verify Elliptic Curve signature using configured ECPublicKey. " + e.getMessage();
Expand Down
Loading

0 comments on commit b784732

Please sign in to comment.