Skip to content

[WIP] JOSE support #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions cryptography-serialization/jose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2023-2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

import ckbuild.*

plugins {
id("ckbuild.multiplatform-library")
alias(libs.plugins.kotlin.plugin.serialization)
}

description = "cryptography-kotlin JOSE API"

kotlin {
jvmTarget()
jsTarget()
nativeTargets()
wasmTargets()

sourceSets.commonMain.dependencies {
api(projects.cryptographyCore)
implementation(libs.kotlinx.serialization.json)
}

sourceSets.commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2023-2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.serialization.jose

import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline

/**
* JSON Web Algorithms (JWA) as defined in RFC 7518.
*/

/**
* Digital Signature or MAC Algorithms for JWS (Section 3.1)
*/
@Serializable
@JvmInline
public value class JwsAlgorithm(public val value: String) {
public companion object {
/** HMAC using SHA-256 */
public val HS256: JwsAlgorithm = JwsAlgorithm("HS256")
/** HMAC using SHA-384 */
public val HS384: JwsAlgorithm = JwsAlgorithm("HS384")
/** HMAC using SHA-512 */
public val HS512: JwsAlgorithm = JwsAlgorithm("HS512")
/** RSASSA-PKCS1-v1_5 using SHA-256 */
public val RS256: JwsAlgorithm = JwsAlgorithm("RS256")
/** RSASSA-PKCS1-v1_5 using SHA-384 */
public val RS384: JwsAlgorithm = JwsAlgorithm("RS384")
/** RSASSA-PKCS1-v1_5 using SHA-512 */
public val RS512: JwsAlgorithm = JwsAlgorithm("RS512")
/** ECDSA using P-256 and SHA-256 */
public val ES256: JwsAlgorithm = JwsAlgorithm("ES256")
/** ECDSA using P-384 and SHA-384 */
public val ES384: JwsAlgorithm = JwsAlgorithm("ES384")
/** ECDSA using P-521 and SHA-512 */
public val ES512: JwsAlgorithm = JwsAlgorithm("ES512")
/** RSASSA-PSS using SHA-256 and MGF1 with SHA-256 */
public val PS256: JwsAlgorithm = JwsAlgorithm("PS256")
/** RSASSA-PSS using SHA-384 and MGF1 with SHA-384 */
public val PS384: JwsAlgorithm = JwsAlgorithm("PS384")
/** RSASSA-PSS using SHA-512 and MGF1 with SHA-512 */
public val PS512: JwsAlgorithm = JwsAlgorithm("PS512")
/** No digital signature or MAC performed */
public val NONE: JwsAlgorithm = JwsAlgorithm("none")
}
}

/**
* Key Management Algorithms for JWE (Section 4.1)
*/
@Serializable
@JvmInline
public value class JweKeyManagementAlgorithm(public val value: String) {
public companion object {
/** RSAES-PKCS1-v1_5 */
public val RSA1_5: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("RSA1_5")
/** RSAES OAEP using default parameters */
public val RSA_OAEP: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("RSA-OAEP")
/** RSAES OAEP using SHA-256 and MGF1 with SHA-256 */
public val RSA_OAEP_256: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("RSA-OAEP-256")
/** AES Key Wrap with default initial value using 128-bit key */
public val A128KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("A128KW")
/** AES Key Wrap with default initial value using 192-bit key */
public val A192KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("A192KW")
/** AES Key Wrap with default initial value using 256-bit key */
public val A256KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("A256KW")
/** Direct use of a shared symmetric key as the CEK */
public val DIR: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("dir")
/** Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF */
public val ECDH_ES: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("ECDH-ES")
/** ECDH Ephemeral Static key agreement using Concat KDF and CEK wrapped with AES Key Wrap using a 128-bit key */
public val ECDH_ES_A128KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("ECDH-ES+A128KW")
/** ECDH Ephemeral Static key agreement using Concat KDF and CEK wrapped with AES Key Wrap using a 192-bit key */
public val ECDH_ES_A192KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("ECDH-ES+A192KW")
/** ECDH Ephemeral Static key agreement using Concat KDF and CEK wrapped with AES Key Wrap using a 256-bit key */
public val ECDH_ES_A256KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("ECDH-ES+A256KW")
/** AES GCM key encryption with a 128-bit key */
public val A128GCMKW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("A128GCMKW")
/** AES GCM key encryption with a 192-bit key */
public val A192GCMKW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("A192GCMKW")
/** AES GCM key encryption with a 256-bit key */
public val A256GCMKW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("A256GCMKW")
/** PBES2 with HMAC SHA-256 and AES Key Wrap with 128-bit key */
public val PBES2_HS256_A128KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("PBES2-HS256+A128KW")
/** PBES2 with HMAC SHA-384 and AES Key Wrap with 192-bit key */
public val PBES2_HS384_A192KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("PBES2-HS384+A192KW")
/** PBES2 with HMAC SHA-512 and AES Key Wrap with 256-bit key */
public val PBES2_HS512_A256KW: JweKeyManagementAlgorithm = JweKeyManagementAlgorithm("PBES2-HS512+A256KW")
}
}

/**
* Content Encryption Algorithms for JWE (Section 5.1)
*/
@Serializable
@JvmInline
public value class JweContentEncryptionAlgorithm(public val value: String) {
public companion object {
/** AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm */
public val A128CBC_HS256: JweContentEncryptionAlgorithm = JweContentEncryptionAlgorithm("A128CBC-HS256")
/** AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm */
public val A192CBC_HS384: JweContentEncryptionAlgorithm = JweContentEncryptionAlgorithm("A192CBC-HS384")
/** AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm */
public val A256CBC_HS512: JweContentEncryptionAlgorithm = JweContentEncryptionAlgorithm("A256CBC-HS512")
/** AES GCM using 128-bit key */
public val A128GCM: JweContentEncryptionAlgorithm = JweContentEncryptionAlgorithm("A128GCM")
/** AES GCM using 192-bit key */
public val A192GCM: JweContentEncryptionAlgorithm = JweContentEncryptionAlgorithm("A192GCM")
/** AES GCM using 256-bit key */
public val A256GCM: JweContentEncryptionAlgorithm = JweContentEncryptionAlgorithm("A256GCM")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Copyright (c) 2023-2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.cryptography.serialization.jose

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlin.jvm.JvmInline

/**
* Key Types as defined in RFC 7518
*/
@Serializable
@JvmInline
public value class JwkKeyType(public val value: String) {
public companion object {
/** RSA Key Type */
public val RSA: JwkKeyType = JwkKeyType("RSA")
/** Elliptic Curve Key Type */
public val EC: JwkKeyType = JwkKeyType("EC")
/** Symmetric Key Type */
public val SYMMETRIC: JwkKeyType = JwkKeyType("oct")
}
}

/**
* Public Key Use values
*/
@Serializable
@JvmInline
public value class JwkKeyUse(public val value: String) {
public companion object {
/** Signature Use */
public val SIGNATURE: JwkKeyUse = JwkKeyUse("sig")
/** Encryption Use */
public val ENCRYPTION: JwkKeyUse = JwkKeyUse("enc")
}
}

/**
* Key Operations
*/
@Serializable
@JvmInline
public value class JwkKeyOperation(public val value: String) {
public companion object {
/** Sign Operation */
public val SIGN: JwkKeyOperation = JwkKeyOperation("sign")
/** Verify Operation */
public val VERIFY: JwkKeyOperation = JwkKeyOperation("verify")
/** Encrypt Operation */
public val ENCRYPT: JwkKeyOperation = JwkKeyOperation("encrypt")
/** Decrypt Operation */
public val DECRYPT: JwkKeyOperation = JwkKeyOperation("decrypt")
/** Wrap Key Operation */
public val WRAP_KEY: JwkKeyOperation = JwkKeyOperation("wrapKey")
/** Unwrap Key Operation */
public val UNWRAP_KEY: JwkKeyOperation = JwkKeyOperation("unwrapKey")
/** Derive Key Operation */
public val DERIVE_KEY: JwkKeyOperation = JwkKeyOperation("deriveKey")
/** Derive Bits Operation */
public val DERIVE_BITS: JwkKeyOperation = JwkKeyOperation("deriveBits")
}
}

/**
* Elliptic Curve identifiers
*/
@Serializable
@JvmInline
public value class JwkEllipticCurve(public val value: String) {
public companion object {
/** P-256 curve */
public val P256: JwkEllipticCurve = JwkEllipticCurve("P-256")
/** P-384 curve */
public val P384: JwkEllipticCurve = JwkEllipticCurve("P-384")
/** P-521 curve */
public val P521: JwkEllipticCurve = JwkEllipticCurve("P-521")
}
}

/**
* JSON Web Key (JWK) as defined in RFC 7517.
*
* A JWK is a JSON object that represents a cryptographic key.
*/
@Serializable
public data class JsonWebKey(
/** Key Type - identifies the cryptographic algorithm family used with the key */
@SerialName("kty")
val keyType: JwkKeyType,
/** Public Key Use - identifies the intended use of the public key */
@SerialName("use")
val keyUse: JwkKeyUse? = null,
/** Key Operations - identifies the operation(s) for which the key is intended to be used */
@SerialName("key_ops")
val keyOperations: List<JwkKeyOperation>? = null,
/** Algorithm - identifies the algorithm intended for use with the key */
@SerialName("alg")
val algorithm: JwsAlgorithm? = null,
/** Key ID - used to match a specific key among multiple keys */
@SerialName("kid")
val keyId: String? = null,
/** X.509 URL - URI that refers to a resource for an X.509 public key certificate or certificate chain */
@SerialName("x5u")
val x509Url: String? = null,
/** X.509 Certificate Chain - chain of one or more PKIX certificates */
@SerialName("x5c")
val x509CertificateChain: List<String>? = null,
/** X.509 Certificate SHA-1 Thumbprint */
@SerialName("x5t")
val x509CertificateSha1Thumbprint: String? = null,
/** X.509 Certificate SHA-256 Thumbprint */
@SerialName("x5t#S256")
val x509CertificateSha256Thumbprint: String? = null,

// RSA Key Parameters (RFC 7518 Section 6.3)
/** Modulus (for RSA keys) */
@SerialName("n")
val modulus: String? = null,
/** Exponent (for RSA keys) */
@SerialName("e")
val exponent: String? = null,
/** Private Exponent (for RSA private keys) or ECC Private Key (for EC private keys) */
@SerialName("d")
val privateKey: String? = null,
/** First Prime Factor (for RSA private keys) */
@SerialName("p")
val firstPrimeFactor: String? = null,
/** Second Prime Factor (for RSA private keys) */
@SerialName("q")
val secondPrimeFactor: String? = null,
/** First Factor CRT Exponent (for RSA private keys) */
@SerialName("dp")
val firstFactorCrtExponent: String? = null,
/** Second Factor CRT Exponent (for RSA private keys) */
@SerialName("dq")
val secondFactorCrtExponent: String? = null,
/** First CRT Coefficient (for RSA private keys) */
@SerialName("qi")
val firstCrtCoefficient: String? = null,

// Elliptic Curve Key Parameters (RFC 7518 Section 6.2)
/** Curve (for EC keys) */
@SerialName("crv")
val curve: JwkEllipticCurve? = null,
/** X Coordinate (for EC keys) */
@SerialName("x")
val xCoordinate: String? = null,
/** Y Coordinate (for EC keys) */
@SerialName("y")
val yCoordinate: String? = null,

// Symmetric Key Parameters (RFC 7518 Section 6.4)
/** Key Value (for symmetric keys) */
@SerialName("k")
val keyValue: String? = null,

/** Additional key-specific parameters */
val additionalParameters: Map<String, JsonElement> = emptyMap()
) {

/**
* Determines if this is a private key based on the presence of private key parameters.
*/
val isPrivateKey: Boolean
get() = when (keyType) {
JwkKeyType.RSA -> privateKey != null
JwkKeyType.EC -> privateKey != null
JwkKeyType.SYMMETRIC -> keyValue != null
else -> false
}

/**
* Determines if this is a public key (not private and has public key parameters).
*/
val isPublicKey: Boolean
get() = when (keyType) {
JwkKeyType.RSA -> modulus != null && exponent != null && privateKey == null
JwkKeyType.EC -> curve != null && xCoordinate != null && yCoordinate != null && privateKey == null
JwkKeyType.SYMMETRIC -> false // Symmetric keys are neither public nor private in the traditional sense
else -> false
}
}

/**
* JSON Web Key Set (JWK Set) as defined in RFC 7517.
*
* A JWK Set is a JSON object that represents a set of JWKs.
*/
@Serializable
public data class JsonWebKeySet(
/** Array of JWK values */
val keys: List<JsonWebKey>
) {
/**
* Finds a key by its Key ID (kid).
*/
public fun findByKeyId(keyId: String): JsonWebKey? = keys.find { it.keyId == keyId }

/**
* Finds keys by their intended use.
*/
public fun findByUse(keyUse: JwkKeyUse): List<JsonWebKey> = keys.filter { it.keyUse == keyUse }

/**
* Finds keys by their algorithm.
*/
public fun findByAlgorithm(algorithm: JwsAlgorithm): List<JsonWebKey> = keys.filter { it.algorithm == algorithm }

/**
* Finds keys by their key type.
*/
public fun findByKeyType(keyType: JwkKeyType): List<JsonWebKey> = keys.filter { it.keyType == keyType }

/**
* Finds all public keys in the set.
*/
public fun findPublicKeys(): List<JsonWebKey> = keys.filter { it.isPublicKey }

/**
* Finds all private keys in the set.
*/
public fun findPrivateKeys(): List<JsonWebKey> = keys.filter { it.isPrivateKey }
}
Loading
Loading