Skip to content

Commit

Permalink
fix: refactor jwk model structure
Browse files Browse the repository at this point in the history
  • Loading branch information
jcmelati committed Oct 20, 2024
1 parent d768513 commit 9f1d298
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.sphereon.oid.fed.openapi.models.SubordinateAdminDTO
import com.sphereon.oid.fed.openapi.models.SubordinateAdminJwkDto
import com.sphereon.oid.fed.openapi.models.SubordinateStatement
import com.sphereon.oid.fed.persistence.models.Subordinate
import com.sphereon.oid.fed.persistence.models.SubordinateJwk
import com.sphereon.oid.fed.services.SubordinateService
import com.sphereon.oid.fed.services.extensions.toSubordinateAdminDTO
import kotlinx.serialization.json.JsonObject
Expand Down Expand Up @@ -41,7 +40,7 @@ class SubordinateController {
@PathVariable accountUsername: String,
@PathVariable id: Int,
@RequestBody jwk: JsonObject
): SubordinateJwk {
): SubordinateAdminJwkDto {
return subordinateService.createSubordinateJwk(accountUsername, id, jwk)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.sphereon.oid.fed.kms.local.jwt.verify
import com.sphereon.oid.fed.openapi.models.JWTHeader
import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkAdminDTO
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject

Expand All @@ -22,7 +23,7 @@ class LocalKms {

database.insertKey(
keyId = jwk.kid!!,
key = aesEncryption.encrypt(Json.encodeToString(Jwk.serializer(), jwk))
key = aesEncryption.encrypt(Json.encodeToString(JwkWithPrivateKey.serializer(), jwk))
)

return jwk.toJwkAdminDto()
Expand All @@ -31,7 +32,7 @@ class LocalKms {
fun sign(header: JWTHeader, payload: JsonObject, keyId: String): String {
val jwk = database.getKey(keyId)

val jwkObject: Jwk = Json.decodeFromString(aesEncryption.decrypt(jwk.key))
val jwkObject: JwkWithPrivateKey = Json.decodeFromString(aesEncryption.decrypt(jwk.key))

val mHeader = header.copy(alg = jwkObject.alg, kid = jwkObject.kid!!)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.sphereon.oid.fed.kms.local.extensions

import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkAdminDTO
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey

fun Jwk.toJwkAdminDto(): JwkAdminDTO = JwkAdminDTO(
fun JwkWithPrivateKey.toJwkAdminDto(): JwkAdminDTO = JwkAdminDTO(
kid = this.kid,
use = this.use,
crv = this.crv,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.sphereon.oid.fed.kms.local.jwk

import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey

expect fun generateKeyPair(): Jwk
expect fun generateKeyPair(): JwkWithPrivateKey
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package com.sphereon.oid.fed.kms.local.jwt

import com.sphereon.oid.fed.openapi.models.JWTHeader
import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey
import kotlinx.serialization.json.JsonObject

expect fun sign(payload: JsonObject, header: JWTHeader, key: Jwk): String
expect fun sign(payload: JsonObject, header: JWTHeader, key: JwkWithPrivateKey): String
expect fun verify(jwt: String, key: Jwk): Boolean
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import com.nimbusds.jose.Algorithm
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jose.jwk.gen.ECKeyGenerator
import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey
import java.util.*


actual fun generateKeyPair(): Jwk {
actual fun generateKeyPair(): JwkWithPrivateKey {
try {
val ecKey: ECKey = ECKeyGenerator(Curve.P_256)
.keyIDFromThumbprint(true)
.algorithm(Algorithm("ES256"))
.issueTime(Date())
.generate()

return Jwk(
return JwkWithPrivateKey(
d = ecKey.d.toString(),
alg = ecKey.algorithm.name,
crv = ecKey.curve.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.sphereon.oid.fed.kms.local.jwt

import com.nimbusds.jose.*
import com.nimbusds.jose.JOSEObjectType
import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.JWSHeader
import com.nimbusds.jose.JWSSigner
import com.nimbusds.jose.JWSVerifier
import com.nimbusds.jose.crypto.ECDSASigner
import com.nimbusds.jose.crypto.ECDSAVerifier
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
import com.sphereon.oid.fed.openapi.models.JWTHeader
import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject

actual fun sign(
payload: JsonObject, header: JWTHeader, key: Jwk
payload: JsonObject, header: JWTHeader, key: JwkWithPrivateKey
): String {
val jwkJsonString = Json.encodeToString(key)
val ecJWK = ECKey.parse(jwkJsonString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import com.nimbusds.jose.Algorithm
import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.gen.ECKeyGenerator
import com.sphereon.oid.fed.openapi.models.BaseEntityStatementJwks
import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement
import com.sphereon.oid.fed.openapi.models.EntityJwks
import com.sphereon.oid.fed.openapi.models.JWTHeader
import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.JwkWithPrivateKey
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.encodeToJsonElement
Expand All @@ -21,13 +22,13 @@ class JoseJwtTest {
val key = ECKeyGenerator(Curve.P_256).keyID("key1").algorithm(Algorithm("ES256")).generate()
val jwk = key.toString()
val entityStatement = EntityConfigurationStatement(
iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = BaseEntityStatementJwks()
iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = EntityJwks()
)
val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject
val signature = sign(
payload,
JWTHeader(alg = JWSAlgorithm.ES256.toString(), typ = "JWT", kid = key.keyID),
Json.decodeFromString<Jwk>(jwk)
Json.decodeFromString<JwkWithPrivateKey>(jwk)
)
assertTrue { signature.startsWith("ey") }
}
Expand All @@ -37,14 +38,18 @@ class JoseJwtTest {
val key = ECKeyGenerator(Curve.P_256).keyID("key1").algorithm(Algorithm("ES256")).generate()
val jwk = key.toString()
val entityStatement = EntityConfigurationStatement(
iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = BaseEntityStatementJwks()
iss = "test", sub = "test", exp = 111111, iat = 111111, jwks = EntityJwks()
)
val payload: JsonObject = Json.encodeToJsonElement(entityStatement) as JsonObject
val signature = sign(
payload,
JWTHeader(alg = JWSAlgorithm.ES256.toString(), typ = "JWT", kid = key.keyID),
Json.decodeFromString<Jwk>(jwk)
Json.decodeFromString<JwkWithPrivateKey>(jwk)
)
assertTrue { verify(signature, Json.decodeFromString<Jwk>(jwk)) }
assertTrue {
verify(signature, Json {
ignoreUnknownKeys = true
}.decodeFromString<Jwk>(jwk))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1516,7 +1516,7 @@ paths:

components:
schemas:
BaseJwk:
Jwk:
type: object
x-tags:
- federation
Expand Down Expand Up @@ -1587,25 +1587,38 @@ components:
description: The SHA-1 thumbprint of the X.509 certificate.
example: dGhpcyBpcyBqdXN0IGEgdGh1bWJwcmludA
nullable: true
x5tS256: # Renamed to comply with OpenAPI restrictions
x5tS256:
type: string
description: The SHA-256 thumbprint of the X.509 certificate.
example: sM4KhEI1Y2Sb6-EVr6tJabmJuoP-ZE...
nullable: true

JwkDTO:
EntityJwkDTO:
allOf:
- $ref: '#/components/schemas/BaseJwk'
- $ref: '#/components/schemas/Jwk'
- type: object
x-tags:
- federation
properties:
revoked:
$ref: '#/components/schemas/JwkRevoked'
$ref: '#/components/schemas/EntityJwkRevoked'

Jwk:
EntityJwkRevoked:
type: object
x-tags:
- federation
required:
- revoked_at
properties:
revoked_at:
type: string
format: date-time
reason:
type: string

JwkWithPrivateKey:
allOf:
- $ref: '#/components/schemas/BaseJwk'
- $ref: '#/components/schemas/Jwk'
- type: object
x-tags:
- federation
Expand Down Expand Up @@ -1643,7 +1656,7 @@ components:

JwkAdminDTO:
allOf:
- $ref: '#/components/schemas/BaseJwk'
- $ref: '#/components/schemas/Jwk'
- type: object
x-tags:
- federation
Expand Down Expand Up @@ -1679,7 +1692,7 @@ components:
example: 2024-08-06T12:34:56Z
nullable: true

SubordinateAdminJwkDto:
SubordinateJwkDto:
type: object
x-tags:
- federation
Expand All @@ -1701,19 +1714,6 @@ components:
example: 2024-08-06T12:34:56Z
nullable: false

JwkRevoked:
type: object
x-tags:
- federation
required:
- revoked_at
properties:
revoked_at:
type: string
format: date-time
reason:
type: string

JWKS:
type: object
x-tags:
Expand All @@ -1722,7 +1722,7 @@ components:
keys:
type: array
items:
$ref: '#/components/schemas/JwkDTO'
$ref: '#/components/schemas/Jwk'

JWTHeader:
type: object
Expand Down Expand Up @@ -1791,20 +1791,24 @@ components:
format: date-time
description: The time the statement was issued.
jwks:
type:
object
properties:
keys:
type: array
items:
$ref: '#/components/schemas/Jwk'
$ref: '#/components/schemas/EntityJwks'
metadata:
additionalProperties: true
crit:
type: array
items:
type: string

EntityJwks:
type: object
x-tags:
- federation
properties:
keys:
type: array
items:
$ref: '#/components/schemas/Jwk'

EntityConfigurationStatement:
allOf:
- $ref: '#/components/schemas/BaseEntityStatement'
Expand Down Expand Up @@ -3373,7 +3377,7 @@ components:
keys:
type: array
items:
$ref: '#/components/schemas/JwkDTO'
$ref: '#/components/schemas/Jwk'

ResolveResponse:
type: object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.sphereon.oid.fed.common.builder

import com.sphereon.oid.fed.openapi.models.BaseEntityStatementJwks
import com.sphereon.oid.fed.openapi.models.EntityConfigurationStatement
import com.sphereon.oid.fed.openapi.models.EntityJwks
import com.sphereon.oid.fed.openapi.models.Jwk
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.JsonObject
Expand Down Expand Up @@ -33,8 +33,8 @@ class EntityConfigurationStatementBuilder {
}

@OptIn(ExperimentalSerializationApi::class)
private fun createJwks(jwks: List<Jwk>): BaseEntityStatementJwks {
return BaseEntityStatementJwks(jwks.toTypedArray())
private fun createJwks(jwks: List<Jwk>): EntityJwks {
return EntityJwks(jwks.toTypedArray())
}

fun build(): EntityConfigurationStatement {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package com.sphereon.oid.fed.common.builder

import com.sphereon.oid.fed.openapi.models.BaseEntityStatementJwks
import com.sphereon.oid.fed.openapi.models.EntityJwks
import com.sphereon.oid.fed.openapi.models.Jwk
import com.sphereon.oid.fed.openapi.models.SubordinateStatement
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject

class SubordinateStatementBuilder {
private var iss: String? = null
private var sub: String? = null
private var exp: Int? = null
private var iat: Int? = null
private var jwks: MutableList<Jwk> = mutableListOf();
private var jwks: MutableList<Jwk> = mutableListOf()
private var metadata: MutableMap<String, JsonObject> = mutableMapOf()
private var metadata_policy: MutableMap<String, JsonObject> = mutableMapOf()
private var metadata_policy_crit: MutableMap<String, JsonObject> = mutableMapOf()
Expand Down Expand Up @@ -50,22 +46,15 @@ class SubordinateStatementBuilder {
this.source_endpoint = sourceEndpoint
}

private fun createJwks(jwks: MutableList<Jwk>): BaseEntityStatementJwks {
val jsonArray: JsonArray =
Json.encodeToJsonElement(ListSerializer(Jwk.serializer()), jwks) as JsonArray

return buildJsonObject {
put("keys", jsonArray)
} as BaseEntityStatementJwks
}

fun build(): SubordinateStatement {
return SubordinateStatement(
iss = iss ?: throw IllegalArgumentException("iss must be provided"),
sub = sub ?: throw IllegalArgumentException("sub must be provided"),
exp = exp ?: throw IllegalArgumentException("exp must be provided"),
iat = iat ?: throw IllegalArgumentException("iat must be provided"),
jwks = createJwks(jwks),
jwks = EntityJwks(
propertyKeys = jwks.toTypedArray()
),
crit = if (crit.isNotEmpty()) crit.toTypedArray() else null,
metadata = JsonObject(metadata),
metadataPolicy = JsonObject(metadata_policy),
Expand Down
Loading

0 comments on commit 9f1d298

Please sign in to comment.