jwks-rsa-java
jwks-rsa-java copied to clipboard
EC key invalid decoding
Signum supposed to be used after base64 decoding: https://github.com/auth0/jwks-rsa-java/blob/master/src/main/java/com/auth0/jwk/Jwk.java#L196-L197
Sample: ECPoint ecPoint = new ECPoint(new BigInteger(1, Base64.getUrlDecoder().decode(stringValue("x"))), new BigInteger(1, Base64.getUrlDecoder().decode(stringValue("y"))));
Similar approach is already used in lines 183-184 for RSA Algorithm.
Issue causes invalid pubKey decoding resulting in signature validation failure.
Hi @pstojek, can you provide a sample so that we can reproduce this?
I am attaching sample with wiremock test simulating jwks server written in Kotlin - Failure rate is 3/4 (when signum of x/y matters). Solution provided in first comment fixes the issue. If we use that key later e.g. jwt token validation then we will receive invalid signature, because of improper EC pub key decoding.
import com.auth0.jwk.JwkProviderBuilder
import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.ok
import com.github.tomakehurst.wiremock.client.WireMock.stubFor
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jose.jwk.KeyUse
import com.nimbusds.jose.jwk.gen.ECKeyGenerator
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.net.URL
import java.security.interfaces.ECPublicKey
import java.time.Duration
import java.util.UUID
@WireMockTest
internal class JwksTest {
@Test
fun `should pull same public key as on jwks server`(wmRuntimeInfo: WireMockRuntimeInfo) {
// PART 1: Stab JWKS server response with com.nimbusds.jose.jwk library:
val keyId = UUID.randomUUID().toString()
val serverJwk: ECKey = ECKeyGenerator(Curve.P_384)
.keyUse(KeyUse.SIGNATURE)
.keyID(keyId)
.generate()
stubFor(
get("/.well-known/jwks.json").willReturn(
ok("{\"keys\":[${serverJwk.toPublicJWK().toJSONString()}]}")
)
)
val serverPublicKey = serverJwk.toPublicJWK().toECPublicKey()
// PART 2: client side using com.auth0.jwk.JwkProviderBuilder
val jwkProvider = JwkProviderBuilder(
URL(wmRuntimeInfo.httpBaseUrl + "/.well-known/jwks.json")
)
.cached(5, Duration.ofHours(6))
.timeouts(1000, 1000)
.rateLimited(false)
.build()
val pulledECPublicKey = jwkProvider.get(keyId).publicKey as ECPublicKey
// Success rate 1/2 for following condition:
assertEquals(serverPublicKey.w.affineX, pulledECPublicKey.w.affineX)
// Success rate 1/2 for following condition:
assertEquals(serverPublicKey.w.affineY, pulledECPublicKey.w.affineY)
}
}
I'm not certain if I had exactly the same issue, but my EC JWK -> public token was failing with something like a "coordinates out of range" error.
Anyways I switched to https://github.com/fusionauth/fusionauth-jwt to grab the public key. Their logic is really quite similar to the logic in this library so not exactly sure what the difference is.
Thanks for raising this and providing a test case! @pstojek and @salami if you're able to try out #153 and verify it resolves the issue that would be great! 👍
Yes, it fixes the issue. Thanks