Major cryptography blunder in Java enables “psychic paper” forgeries


Getty Images

Organizations using newer versions of Oracle’s Java framework woke up on Wednesday to a disquieting advisory: A critical vulnerability can make it easy for adversaries to forge TLS certificates and signatures, two-factor authentication messages, and authorization credentials generated by a range of widely used open standards.

The vulnerability, which Oracle patched on Tuesday, affects the company’s implementation of the Elliptic Curve Digital Signature Algorithm in Java versions 15 and above. ECDSA is an algorithm that uses the principles of elliptic curve cryptography to authenticate messages digitally. A key advantage of ECDSA is the smaller size of the keys it generates, compared to RSA or other crypto algorithms, making it ideal for use in standards including FIDO-based 2FA, the Security Assertion Markup Language, OpenID, and JSON.

Doctor Who and the psychic paper

Neil Madden, the researcher at security firm ForgeRock who discovered the vulnerability, likened it to the blank identity cards that make regular appearances in the sci-fi show Doctor Who. The psychic paper the cards are made of causes the person looking at it to see whatever the protagonist wants them to see.

“It turns out that some recent releases of Java were vulnerable to a similar kind of trick, in the implementation of widely-used ECDSA signatures,” Madden wrote. “If you are running one of the vulnerable versions then an attacker can easily forge some types of SSL certificates and handshakes (allowing interception and modification of communications), signed JWTs, SAML assertions or OIDC id tokens, and even WebAuthn authentication messages. All using the digital equivalent of a blank piece of paper.”

He continued:

“It’s hard to overstate the severity of this bug. If you are using ECDSA signatures for any of these security mechanisms, then an attacker can trivially and completely bypass them if your server is running any Java 15, 16, 17, or 18 version before the April 2022 Critical Patch Update (CPU). For context, almost all WebAuthn/FIDO devices in the real world (including Yubikeys use ECDSA signatures and many OIDC providers use ECDSA-signed JWTs.”

The bug, tracked as CVE-2022-21449, carries a severity rating of 7.5 out of a possible 10, but Madden said based on his assessment, he’d rate the severity at a perfect 10 “due to the wide range of impacts on different functionality in an access management context.” In its grimmest form, the bug could be exploited by someone outside a vulnerable network with no verification at all.

Other security experts also had strong reactions, with one declaring it “the crypto bug of the year.”

A mitigating factor is that Java versions 15 and above don’t appear to be as widely used as earlier versions. Data collected in February and March 2021 from security firm Snyk showed that Java 15, the latest version at that time, accounted for 12 percent of deployments. While Madden said that the specific ECDSA implementation flaw affected only Java 15 and higher, Oracle also listed versions 7, 8, and 11 as vulnerable. Madden said that the discrepancy may result from separate crypto bugs fixed in the earlier releases.

a/0 = valid signature

ECDSA signatures rely on a pseudo-random number, typically notated as K, that’s used to derive two additional numbers, R and S. To verify a signature as valid, a party must check the equation involving R and S, the signer’s public key, and a cryptographic hash of the message. When both sides of the equation are equal, the signature is valid.

In a writeup published Wednesday, security firm Sophos further explained the process:

S1. Select a cryptographically sound random integer K between 1 and N-1 inclusive.
S2. Compute R from K using Elliptic Curve multiplication.
S3. In the unlikely event that R is zero, go back to step 1 and start over.
S4. Compute S from K, R, the hash to be signed, and the private key.
S5. In the unlikely event that S is zero, go back to step 1 and start over.

For the process to work correctly, neither R nor S can ever be a zero. That’s because one side of the equation is R, and the other is multiplied by R and a value from S. If the values are both 0, the verification check translates to 0 = 0 X (other values from the private key and hash), which will be true regardless of the additional values. That means an adversary only needs to submit a blank signature to pass the verification check successfully.

Madden wrote:

Guess which check Java forgot?

That’s right. Java’s implementation of ECDSA signature verification didn’t check if R or S were zero, so you could produce a signature value in which they are both 0 (appropriately encoded) and Java would accept it as a valid signature for any message and for any public key. The digital equivalent of a blank ID card.

Below is an interactive JShell session Madden created that shows a vulnerable implementation accepting a blank signature as valid when verifying a message and public key:

| Welcome to JShell -- Version 17.0.1
| For an introduction type: /help intro
jshell> import java.security.*
jshell> var keys = KeyPairGenerator.getInstance("EC").generateKeyPair()
keys ==> java.security.KeyPair@626b2d4a
jshell> var blankSignature = new byte[64]
blankSignature ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }
jshell> var sig = Signature.getInstance("SHA256WithECDSAInP1363Format")
sig ==> Signature object: SHA256WithECDSAInP1363Format<not initialized>
jshell> sig.initVerify(keys.getPublic())
jshell> sig.update("Hello, World".getBytes())
jshell> sig.verify(blankSignature)
$8 ==> true
// Oops, that shouldn't have verified...

Organizations that are using any of the affected versions of Java to validate signatures should place a high priority on patching. It will also be important to monitor for advisories from app and product makers to see if any of their wares are made vulnerable. While the threat from CVE-2022-21449 appears limited to new Java versions, its severity is high enough to warrant vigilance.