Skip to content

Commit a5b2eb6

Browse files
e5lclaudeCopilot
authored
KTOR-9352 Handle EC key type when JWK algorithm is null (#5387)
* KTOR-9352 Add failing test for EC JWK with null algorithm Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * KTOR-9352 Handle EC key type when JWK algorithm is null Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Update ktor-server/ktor-server-plugins/ktor-server-auth-jwt/jvm/test/io/ktor/server/auth/jwt/JWTAuthTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * KTOR-9352 Detect EC curve when JWK algorithm is null Infer ECDSA algorithm from the EC key's curve size (P-256, P-384, P-521) instead of always defaulting to ECDSA256. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent a971317 commit a5b2eb6

File tree

2 files changed

+88
-1
lines changed
  • ktor-server/ktor-server-plugins/ktor-server-auth-jwt/jvm

2 files changed

+88
-1
lines changed

ktor-server/ktor-server-plugins/ktor-server-auth-jwt/jvm/src/io/ktor/server/auth/jwt/JWTUtils.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,18 @@ internal fun Jwk.makeAlgorithm(): Algorithm = when (algorithm) {
2525
"ES256" -> Algorithm.ECDSA256(publicKey as ECPublicKey, null)
2626
"ES384" -> Algorithm.ECDSA384(publicKey as ECPublicKey, null)
2727
"ES512" -> Algorithm.ECDSA512(publicKey as ECPublicKey, null)
28-
null -> Algorithm.RSA256(publicKey as RSAPublicKey, null)
28+
null -> when (type) {
29+
"EC" -> {
30+
val ecKey = publicKey as ECPublicKey
31+
when (ecKey.params.order.bitLength()) {
32+
in 249..258 -> Algorithm.ECDSA256(ecKey, null)
33+
in 379..388 -> Algorithm.ECDSA384(ecKey, null)
34+
in 518..527 -> Algorithm.ECDSA512(ecKey, null)
35+
else -> Algorithm.ECDSA256(ecKey, null)
36+
}
37+
}
38+
else -> Algorithm.RSA256(publicKey as RSAPublicKey, null)
39+
}
2940
else -> throw IllegalArgumentException("Unsupported algorithm $algorithm")
3041
}
3142

ktor-server/ktor-server-plugins/ktor-server-auth-jwt/jvm/test/io/ktor/server/auth/jwt/JWTAuthTest.kt

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,81 @@ class JWTAuthTest {
374374
verifier.verify(token)
375375
}
376376

377+
@Test
378+
fun `KTOR-9352 makeAlgorithm with EC key type and null algorithm`() {
379+
val ecKeyPair = KeyPairGenerator.getInstance("EC").apply {
380+
initialize(256, SecureRandom())
381+
}.generateKeyPair()
382+
val ecAlgorithm = Algorithm.ECDSA256(ecKeyPair.public as ECPublicKey, ecKeyPair.private as ECPrivateKey)
383+
384+
val token = JWT.create()
385+
.withAudience(audience)
386+
.withIssuer(issuer)
387+
.withKeyId(kid)
388+
.sign(ecAlgorithm)
389+
390+
val jwk = mockk<Jwk> {
391+
every { algorithm } returns null
392+
every { type } returns "EC"
393+
every { publicKey } returns ecKeyPair.public
394+
}
395+
val provider = mockk<JwkProvider> {
396+
every { this@mockk.get(kid) } returns jwk
397+
}
398+
399+
val resolvedAlgorithm = jwk.makeAlgorithm()
400+
val verifier = JWT.require(resolvedAlgorithm).withIssuer(issuer).build()
401+
verifier.verify(token)
402+
}
403+
404+
@Test
405+
fun `KTOR-9352 makeAlgorithm with EC P-384 key and null algorithm`() {
406+
val ecKeyPair = KeyPairGenerator.getInstance("EC").apply {
407+
initialize(java.security.spec.ECGenParameterSpec("secp384r1"))
408+
}.generateKeyPair()
409+
val ecAlgorithm = Algorithm.ECDSA384(ecKeyPair.public as ECPublicKey, ecKeyPair.private as ECPrivateKey)
410+
411+
val token = JWT.create()
412+
.withAudience(audience)
413+
.withIssuer(issuer)
414+
.withKeyId(kid)
415+
.sign(ecAlgorithm)
416+
417+
val jwk = mockk<Jwk> {
418+
every { algorithm } returns null
419+
every { type } returns "EC"
420+
every { publicKey } returns ecKeyPair.public
421+
}
422+
423+
val resolvedAlgorithm = jwk.makeAlgorithm()
424+
val verifier = JWT.require(resolvedAlgorithm).withIssuer(issuer).build()
425+
verifier.verify(token)
426+
}
427+
428+
@Test
429+
fun `KTOR-9352 makeAlgorithm with EC P-521 key and null algorithm`() {
430+
val ecKeyPair = KeyPairGenerator.getInstance("EC").apply {
431+
initialize(java.security.spec.ECGenParameterSpec("secp521r1"))
432+
}.generateKeyPair()
433+
val ecAlgorithm = Algorithm.ECDSA512(ecKeyPair.public as ECPublicKey, ecKeyPair.private as ECPrivateKey)
434+
435+
val token = JWT.create()
436+
.withAudience(audience)
437+
.withIssuer(issuer)
438+
.withKeyId(kid)
439+
.sign(ecAlgorithm)
440+
441+
val jwk = mockk<Jwk> {
442+
every { algorithm } returns null
443+
every { type } returns "EC"
444+
every { publicKey } returns ecKeyPair.public
445+
}
446+
447+
val resolvedAlgorithm = jwk.makeAlgorithm()
448+
val verifier = JWT.require(resolvedAlgorithm).withIssuer(issuer).build()
449+
verifier.verify(token)
450+
}
451+
377452
@Test
378453
fun testVerifyNotProvided() = testApplication {
379454
configureServer {
@@ -550,6 +625,7 @@ class JWTAuthTest {
550625
private fun getJwkProviderNullAlgorithmMock(): JwkProvider {
551626
val jwk = mockk<Jwk> {
552627
every { algorithm } returns null
628+
every { type } returns "RSA"
553629
every { publicKey } returns keyPair.public
554630
}
555631
return mockk {

0 commit comments

Comments
 (0)