ulrikstrid/ocaml-jose

OCaml

55

210 commits

updated Sep 23, 2026

See the code

README

JOSE - JavaScript Object Signing and Encryption

JOSE implementation in OCaml.

Goals

This package aims to implement the JOSE specification. The main usecase for JOSE is probably JWT signing and verification via JWKs.

Installation

Install jose using opam:

opam install jose

Examples

Make sure to initialize the random number generator (required by mirage-crypto) before performing cryptographic operations:

let () = Mirage_crypto_rng_unix.use_default ()

JWS (JSON Web Signature)

Sign and verify arbitrary payload with a key (symmetric oct or asymmetric RSA, EC, OKP):

(* 1. Create or load a key *)
let jwk = Jose.Jwk.make_oct "a-secret-key-that-is-at-least-32-bytes"

(* 2. Sign a payload *)
let jws = Jose.Jws.sign ~payload:"Hello, JWS!" jwk |> Result.get_ok

(* 3. Serialize to compact representation ("<header>.<payload>.<signature>") *)
let token = Jose.Jws.to_string jws

(* 4. Parse and validate signature *)
let parsed_jws = Jose.Jws.of_string token |> Result.get_ok
let validated_jws = Jose.Jws.validate ~jwk parsed_jws |> Result.get_ok
let payload = validated_jws.payload (* "Hello, JWS!" *)

JWE (JSON Web Encryption)

Encrypt and decrypt plaintext payloads:

(* 1. Create or load an encryption key *)
let jwk = Jose.Jwk.make_oct ~use:`Enc "a-secret-key-that-is-at-least-32-bytes"

(* 2. Create a JWE header with key management and content encryption algorithms *)
let header = Jose.Header.make_header ~alg:`Dir ~enc:`A256GCM jwk

(* 3. Encrypt the plaintext into compact representation *)
let jwe = Jose.Jwe.make ~header "Secret payload data" |> Result.get_ok
let encrypted_token = Jose.Jwe.encrypt ~jwk jwe |> Result.get_ok

(* 4. Decrypt using the key *)
let decrypted_jwe = Jose.Jwe.decrypt ~jwk encrypted_token |> Result.get_ok
let payload = decrypted_jwe.payload (* "Secret payload data" *)

JWT (JSON Web Token)

Create, sign, and validate tokens with claim and expiration verification:

(* 1. Create or load a key *)
let jwk = Jose.Jwk.make_oct "a-secret-key-that-is-at-least-32-bytes"

(* 2. Build payload with claims *)
let now = Ptime.of_float_s (Unix.time ()) |> Option.get
let exp =
  Ptime.add_span now (Ptime.Span.v (0, 3600L * 1_000_000_000_000L))
  |> Option.get

let payload =
  Jose.Jwt.empty_payload
  |> Jose.Jwt.add_claim "sub" (`String "user_123")
  |> Jose.Jwt.add_claim "exp" (`Int (Ptime.to_span exp |> Ptime.Span.to_int_s |> Option.get))

(* 3. Sign the token *)
let jwt = Jose.Jwt.sign ~payload jwk |> Result.get_ok
let token_string = Jose.Jwt.to_string jwt

(* 4. Parse and validate signature and expiration *)
let validated_jwt = Jose.Jwt.of_string ~jwk ~now token_string |> Result.get_ok
let user_id = Jose.Jwt.get_string_claim validated_jwt "sub" (* Some "user_123" *)

Algorithm Compatibility

The compatibility tables below are automatically extracted from the codebase using scripts/extract_compatibility.py.

JWS Digital Signature and MAC Algorithms (alg)

AlgorithmDescriptionRequirementRFC ReferenceSupported
HS256HMAC using SHA-256RequiredRFC 7518 §3.1Yes
HS384HMAC using SHA-384OptionalRFC 7518 §3.1No
HS512HMAC using SHA-512OptionalRFC 7518 §3.1No
RS256RSASSA-PKCS1-v1_5 using SHA-256RecommendedRFC 7518 §3.1Yes
RS384RSASSA-PKCS1-v1_5 using SHA-384OptionalRFC 7518 §3.1No
RS512RSASSA-PKCS1-v1_5 using SHA-512OptionalRFC 7518 §3.1No
ES256ECDSA using P-256 and SHA-256Recommended+RFC 7518 §3.1Yes
ES384ECDSA using P-384 and SHA-384OptionalRFC 7518 §3.1Yes
ES512ECDSA using P-521 and SHA-512OptionalRFC 7518 §3.1Yes
PS256RSASSA-PSS using SHA-256 and MGF1 with SHA-256OptionalRFC 7518 §3.1No
PS384RSASSA-PSS using SHA-384 and MGF1 with SHA-384OptionalRFC 7518 §3.1No
PS512RSASSA-PSS using SHA-512 and MGF1 with SHA-512OptionalRFC 7518 §3.1No
EdDSAEdDSA signature algorithm (deprecated by RFC 9864)OptionalRFC 8037 §3.1Yes
Ed25519Ed25519 signature algorithmOptionalRFC 9864 §3.1Yes
Ed448Ed448 signature algorithmOptionalRFC 9864 §3.1No
noneNo digital signature or MAC performedOptionalRFC 7518 §3.1Yes

JWE Key Management Algorithms (alg)

AlgorithmKey Management AlgorithmRequirementRFC ReferenceSupported
RSA1_5RSAES-PKCS1-v1_5Recommended-RFC 7518 §4.1Yes
RSA-OAEPRSAES OAEP using default parametersRecommended+RFC 7518 §4.1Yes
RSA-OAEP-256RSAES OAEP using SHA-256 and MGF1 with SHA-256OptionalRFC 7518 §4.1No
A128KWAES Key Wrap using 128-bit keyRecommendedRFC 7518 §4.1, RFC 3394Yes
A192KWAES Key Wrap using 192-bit keyOptionalRFC 7518 §4.1, RFC 3394No
A256KWAES Key Wrap using 256-bit keyRecommendedRFC 7518 §4.1, RFC 3394Yes
dirDirect use of a shared symmetric keyRecommendedRFC 7518 §4.1Yes
ECDH-ESElliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDFRecommended+RFC 7518 §4.1, §4.6Yes
ECDH-ES+A128KWECDH-ES using Concat KDF and CEK wrapped with "A128KW"RecommendedRFC 7518 §4.1, §4.6Yes
ECDH-ES+A192KWECDH-ES using Concat KDF and CEK wrapped with "A192KW"OptionalRFC 7518 §4.1, §4.6No
ECDH-ES+A256KWECDH-ES using Concat KDF and CEK wrapped with "A256KW"RecommendedRFC 7518 §4.1, §4.6No
A128GCMKWKey wrapping with AES GCM using 128-bit keyOptionalRFC 7518 §4.1No
A192GCMKWKey wrapping with AES GCM using 192-bit keyOptionalRFC 7518 §4.1No
A256GCMKWKey wrapping with AES GCM using 256-bit keyOptionalRFC 7518 §4.1No
PBES2-HS256+A128KWPBES2 with HMAC SHA-256 and "A128KW" wrappingOptionalRFC 7518 §4.1No
PBES2-HS384+A192KWPBES2 with HMAC SHA-384 and "A192KW" wrappingOptionalRFC 7518 §4.1No
PBES2-HS512+A256KWPBES2 with HMAC SHA-512 and "A256KW" wrappingOptionalRFC 7518 §4.1No

JWE Content Encryption Algorithms (enc)

AlgorithmContent Encryption AlgorithmRequirementRFC ReferenceSupported
A128CBC-HS256AES_128_CBC_HMAC_SHA_256 authenticated encryptionRequiredRFC 7518 §5.1, §5.2.3Yes
A192CBC-HS384AES_192_CBC_HMAC_SHA_384 authenticated encryptionOptionalRFC 7518 §5.1, §5.2.4No
A256CBC-HS512AES_256_CBC_HMAC_SHA_512 authenticated encryptionRequiredRFC 7518 §5.1, §5.2.5Yes
A128GCMAES GCM using 128-bit keyRecommendedRFC 7518 §5.1, §5.3Yes
A192GCMAES GCM using 192-bit keyOptionalRFC 7518 §5.1, §5.3No
A256GCMAES GCM using 256-bit keyRecommendedRFC 7518 §5.1, §5.3Yes

JSON Web Key Types (kty)

Key Type (kty)DescriptionRequirementRFC ReferenceSupported
ECElliptic CurveRecommended+RFC 7518 §6.1Yes
RSARSARequiredRFC 7518 §6.1Yes
octOctet sequence (used to represent symmetric keys)RequiredRFC 7518 §6.1Yes
OKPOctet Key PairOptionalRFC 8037 §2Yes

To update or check the compatibility tables:

# Update README.md in-place
python3 scripts/extract_compatibility.py --update-readme

# Check if README.md is in sync (e.g. in CI)
python3 scripts/extract_compatibility.py --check

pre 1.0.0

Expect breaking changes on minor releases but patch should not be breaking.

I want to get feedback on both the API and implementation. Issues and PRs are more than welcome.

crypto
jose
jwk
jwt
oidc

Contributors

ulrikstrid

191 commits

anmonteiro

16 commits

hannesm

1 commits

phongphan

1 commits

ulrikstrid/ocaml-jose

OCaml

55

210 commits

updated Sep 23, 2026

See the code

README

JOSE - JavaScript Object Signing and Encryption

JOSE implementation in OCaml.

Goals

This package aims to implement the JOSE specification. The main usecase for JOSE is probably JWT signing and verification via JWKs.

Installation

Install jose using opam:

opam install jose

Examples

Make sure to initialize the random number generator (required by mirage-crypto) before performing cryptographic operations:

let () = Mirage_crypto_rng_unix.use_default ()

JWS (JSON Web Signature)

Sign and verify arbitrary payload with a key (symmetric oct or asymmetric RSA, EC, OKP):

(* 1. Create or load a key *)
let jwk = Jose.Jwk.make_oct "a-secret-key-that-is-at-least-32-bytes"

(* 2. Sign a payload *)
let jws = Jose.Jws.sign ~payload:"Hello, JWS!" jwk |> Result.get_ok

(* 3. Serialize to compact representation ("<header>.<payload>.<signature>") *)
let token = Jose.Jws.to_string jws

(* 4. Parse and validate signature *)
let parsed_jws = Jose.Jws.of_string token |> Result.get_ok
let validated_jws = Jose.Jws.validate ~jwk parsed_jws |> Result.get_ok
let payload = validated_jws.payload (* "Hello, JWS!" *)

JWE (JSON Web Encryption)

Encrypt and decrypt plaintext payloads:

(* 1. Create or load an encryption key *)
let jwk = Jose.Jwk.make_oct ~use:`Enc "a-secret-key-that-is-at-least-32-bytes"

(* 2. Create a JWE header with key management and content encryption algorithms *)
let header = Jose.Header.make_header ~alg:`Dir ~enc:`A256GCM jwk

(* 3. Encrypt the plaintext into compact representation *)
let jwe = Jose.Jwe.make ~header "Secret payload data" |> Result.get_ok
let encrypted_token = Jose.Jwe.encrypt ~jwk jwe |> Result.get_ok

(* 4. Decrypt using the key *)
let decrypted_jwe = Jose.Jwe.decrypt ~jwk encrypted_token |> Result.get_ok
let payload = decrypted_jwe.payload (* "Secret payload data" *)

JWT (JSON Web Token)

Create, sign, and validate tokens with claim and expiration verification:

(* 1. Create or load a key *)
let jwk = Jose.Jwk.make_oct "a-secret-key-that-is-at-least-32-bytes"

(* 2. Build payload with claims *)
let now = Ptime.of_float_s (Unix.time ()) |> Option.get
let exp =
  Ptime.add_span now (Ptime.Span.v (0, 3600L * 1_000_000_000_000L))
  |> Option.get

let payload =
  Jose.Jwt.empty_payload
  |> Jose.Jwt.add_claim "sub" (`String "user_123")
  |> Jose.Jwt.add_claim "exp" (`Int (Ptime.to_span exp |> Ptime.Span.to_int_s |> Option.get))

(* 3. Sign the token *)
let jwt = Jose.Jwt.sign ~payload jwk |> Result.get_ok
let token_string = Jose.Jwt.to_string jwt

(* 4. Parse and validate signature and expiration *)
let validated_jwt = Jose.Jwt.of_string ~jwk ~now token_string |> Result.get_ok
let user_id = Jose.Jwt.get_string_claim validated_jwt "sub" (* Some "user_123" *)

Algorithm Compatibility

The compatibility tables below are automatically extracted from the codebase using scripts/extract_compatibility.py.

JWS Digital Signature and MAC Algorithms (alg)

AlgorithmDescriptionRequirementRFC ReferenceSupported
HS256HMAC using SHA-256RequiredRFC 7518 §3.1Yes
HS384HMAC using SHA-384OptionalRFC 7518 §3.1No
HS512HMAC using SHA-512OptionalRFC 7518 §3.1No
RS256RSASSA-PKCS1-v1_5 using SHA-256RecommendedRFC 7518 §3.1Yes
RS384RSASSA-PKCS1-v1_5 using SHA-384OptionalRFC 7518 §3.1No
RS512RSASSA-PKCS1-v1_5 using SHA-512OptionalRFC 7518 §3.1No
ES256ECDSA using P-256 and SHA-256Recommended+RFC 7518 §3.1Yes
ES384ECDSA using P-384 and SHA-384OptionalRFC 7518 §3.1Yes
ES512ECDSA using P-521 and SHA-512OptionalRFC 7518 §3.1Yes
PS256RSASSA-PSS using SHA-256 and MGF1 with SHA-256OptionalRFC 7518 §3.1No
PS384RSASSA-PSS using SHA-384 and MGF1 with SHA-384OptionalRFC 7518 §3.1No
PS512RSASSA-PSS using SHA-512 and MGF1 with SHA-512OptionalRFC 7518 §3.1No
EdDSAEdDSA signature algorithm (deprecated by RFC 9864)OptionalRFC 8037 §3.1Yes
Ed25519Ed25519 signature algorithmOptionalRFC 9864 §3.1Yes
Ed448Ed448 signature algorithmOptionalRFC 9864 §3.1No
noneNo digital signature or MAC performedOptionalRFC 7518 §3.1Yes

JWE Key Management Algorithms (alg)

AlgorithmKey Management AlgorithmRequirementRFC ReferenceSupported
RSA1_5RSAES-PKCS1-v1_5Recommended-RFC 7518 §4.1Yes
RSA-OAEPRSAES OAEP using default parametersRecommended+RFC 7518 §4.1Yes
RSA-OAEP-256RSAES OAEP using SHA-256 and MGF1 with SHA-256OptionalRFC 7518 §4.1No
A128KWAES Key Wrap using 128-bit keyRecommendedRFC 7518 §4.1, RFC 3394Yes
A192KWAES Key Wrap using 192-bit keyOptionalRFC 7518 §4.1, RFC 3394No
A256KWAES Key Wrap using 256-bit keyRecommendedRFC 7518 §4.1, RFC 3394Yes
dirDirect use of a shared symmetric keyRecommendedRFC 7518 §4.1Yes
ECDH-ESElliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDFRecommended+RFC 7518 §4.1, §4.6Yes
ECDH-ES+A128KWECDH-ES using Concat KDF and CEK wrapped with "A128KW"RecommendedRFC 7518 §4.1, §4.6Yes
ECDH-ES+A192KWECDH-ES using Concat KDF and CEK wrapped with "A192KW"OptionalRFC 7518 §4.1, §4.6No
ECDH-ES+A256KWECDH-ES using Concat KDF and CEK wrapped with "A256KW"RecommendedRFC 7518 §4.1, §4.6No
A128GCMKWKey wrapping with AES GCM using 128-bit keyOptionalRFC 7518 §4.1No
A192GCMKWKey wrapping with AES GCM using 192-bit keyOptionalRFC 7518 §4.1No
A256GCMKWKey wrapping with AES GCM using 256-bit keyOptionalRFC 7518 §4.1No
PBES2-HS256+A128KWPBES2 with HMAC SHA-256 and "A128KW" wrappingOptionalRFC 7518 §4.1No
PBES2-HS384+A192KWPBES2 with HMAC SHA-384 and "A192KW" wrappingOptionalRFC 7518 §4.1No
PBES2-HS512+A256KWPBES2 with HMAC SHA-512 and "A256KW" wrappingOptionalRFC 7518 §4.1No

JWE Content Encryption Algorithms (enc)

AlgorithmContent Encryption AlgorithmRequirementRFC ReferenceSupported
A128CBC-HS256AES_128_CBC_HMAC_SHA_256 authenticated encryptionRequiredRFC 7518 §5.1, §5.2.3Yes
A192CBC-HS384AES_192_CBC_HMAC_SHA_384 authenticated encryptionOptionalRFC 7518 §5.1, §5.2.4No
A256CBC-HS512AES_256_CBC_HMAC_SHA_512 authenticated encryptionRequiredRFC 7518 §5.1, §5.2.5Yes
A128GCMAES GCM using 128-bit keyRecommendedRFC 7518 §5.1, §5.3Yes
A192GCMAES GCM using 192-bit keyOptionalRFC 7518 §5.1, §5.3No
A256GCMAES GCM using 256-bit keyRecommendedRFC 7518 §5.1, §5.3Yes

JSON Web Key Types (kty)

Key Type (kty)DescriptionRequirementRFC ReferenceSupported
ECElliptic CurveRecommended+RFC 7518 §6.1Yes
RSARSARequiredRFC 7518 §6.1Yes
octOctet sequence (used to represent symmetric keys)RequiredRFC 7518 §6.1Yes
OKPOctet Key PairOptionalRFC 8037 §2Yes

To update or check the compatibility tables:

# Update README.md in-place
python3 scripts/extract_compatibility.py --update-readme

# Check if README.md is in sync (e.g. in CI)
python3 scripts/extract_compatibility.py --check

pre 1.0.0

Expect breaking changes on minor releases but patch should not be breaking.

I want to get feedback on both the API and implementation. Issues and PRs are more than welcome.

crypto
jose
jwk
jwt
oidc

Contributors

ulrikstrid

191 commits

anmonteiro

16 commits

hannesm

1 commits

phongphan

1 commits

Languages

OCaml

87.4%

Python

8.3%

Standard ML

3.0%