josekit

JOSE (Javascript Object Signing and Encryption: JWT, JWS, JWE, JWA, JWK) library based on OpenSSL for Rust.

Install

toml [dependencies] josekit = "0.7.2"

This library depends on OpenSSL 1.1.1 DLL. Read more about Crate openssl.

Build

sh sudo apt install build-essential pkg-config libssl-dev cd josekit-rs cargo build --release

Supported signing algorithms

Name Description Key Type
HS256 HMAC using SHA-256 oct (size: 32 bytes or more)
HS384 HMAC using SHA-384 oct (size: 48 bytes or more)
HS512 HMAC using SHA-512 oct (size: 64 bytes or more)
RS256 RSASSA-PKCS1-v1_5 using SHA-256 RSA (size: 1024 bits or more)
RS384 RSASSA-PKCS1-v1_5 using SHA-384
RS512 RSASSA-PKCS1-v1_5 using SHA-512
PS256 RSASSA-PSS using SHA-256 and MGF1 with SHA-256
PS384 RSASSA-PSS using SHA-384 and MGF1 with SHA-384
PS512 RSASSA-PSS using SHA-512 and MGF1 with SHA-512
ES256 ECDSA using P-256 and SHA-256 EC (curve: P-256)
ES384 ECDSA using P-384 and SHA-384 EC (curve: P-384)
ES512 ECDSA using P-521 and SHA-512 EC (curve: P-521)
ES256K ECDSA using secp256k1 curve and SHA-256 EC (curve: secp256k1)
EdDSA EdDSA signature algorithms OKP (curve: Ed25519 or Ed448)
none No digital signature or MAC performed -

Supported encryption algorithms

Name Description Key Type
dir Direct use of a shared symmetric key as the CEK oct (size: the CEK depended. See below)
  • A128CBC-HS256: 32 bytes
  • A192CBC-HS384: 40 bytes
  • A256CBC-HS512: 48 bytes
  • A128GCM: 16 bytes
  • A192GCM: 24 bytes
  • A256GCM: 32 bytes
ECDH-ES Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF EC (curve: P-256, P-384, P-521 or secp256k1)
OKP (curve: X25519 or X448)
ECDH-ES+A128KW ECDH-ES using Concat KDF and CEK wrapped with "A128KW"
ECDH-ES+A192KW ECDH-ES using Concat KDF and CEK wrapped with "A192KW"
ECDH-ES+A256KW ECDH-ES using Concat KDF and CEK wrapped with "A256KW"
A128KW AES Key Wrap with default initial value using 128-bit key oct (size: 16 bytes)
A192KW AES Key Wrap with default initial value using 192-bit key oct (size: 24 bytes)
A256KW AES Key Wrap with default initial value using 256-bit key oct (size: 32 bytes)
A128GCMKW Key wrapping with AES GCM using 128-bit key oct (size: 16 bytes)
A192GCMKW Key wrapping with AES GCM using 192-bit key oct (size: 24 bytes)
A256GCMKW Key wrapping with AES GCM using 256-bit key oct (size: 32 bytes)
PBES2-HS256+A128KW PBES2 with HMAC SHA-256 and "A128KW" wrapping oct (size: 1 bytes or more)
PBES2-HS384+A192KW PBES2 with HMAC SHA-384 and "A192KW" wrapping
PBES2-HS512+A256KW PBES2 with HMAC SHA-512 and "A256KW" wrapping
RSA1_5 RSAES-PKCS1-v1_5 RSA (size: 1024 bits or more)
RSA-OAEP RSAES OAEP using default parameters
RSA-OAEP-256 RSAES OAEP using SHA-256 and MGF1 with SHA-256
RSA-OAEP-384 RSAES OAEP using SHA-384 and MGF1 with SHA-384
RSA-OAEP-512 RSAES OAEP using SHA-512 and MGF1 with SHA-512

Supported key formats

Private Key

Algorithm JWK PEM DER
PKCS#8 Traditional PKCS#8 Raw
RSA OK OK OK OK OK
RSA-PSS OK OK OK OK OK
EC OK OK OK OK OK
ED OK OK OK OK -
ECX OK OK OK OK -

Public Key

Algorithm JWK PEM DER
SPKI Traditional SPKI Raw
RSA OK OK OK OK OK
RSA-PSS OK OK OK OK OK
EC OK OK - OK -
ED OK OK - OK -
ECX OK OK - OK -

Usage

Signing a JWT by HMAC

HMAC is used to verify the integrity of a message by common secret key. Three algorithms are available for HMAC: HS256, HS384, and HS512.

You can use any bytes as the key. But the key length must be larger than or equal to the output hash size.

```rust use josekit::{JoseError, jws::{JwsHeader, HS256}, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> { let mut header = JwsHeader::new(); header.settokentype("JWT");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

let key = b"0123456789ABCDEF0123456789ABCDEF";

// Signing JWT
let signer = HS256.signer_from_bytes(key)?;
let jwt = jwt::encode_with_signer(&payload, &header, &signer)?;

// Verifing JWT
let verifier = HS256.verifier_from_bytes(key)?;
let (payload, header) = jwt::decode_with_verifier(&jwt, &verifier)?;

Ok(())

} ```

Signing a JWT by RSASSA

RSASSA is used to verify the integrity of a message by two keys: public and private. Three algorithms are available for RSASSA: RS256, RS384, and RS512.

You can generate the keys by executing openssl command.

```sh

Generate a new private key. Keygen bits must be 2048 or more.

openssl openssl genpkey -algorithm RSA -pkeyopt rsakeygenbits:2048 -out private.pem

Generate a public key from the private key.

openssl pkey -in private.pem -pubout -out public.pem ```

```rust use josekit::{JoseError, jws::{JwsHeader, RS256}, jwt::{self, JwtPayload}};

const PRIVATEKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/RSA2048bitprivate.pem"); const PUBLICKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/RSA2048bitpublic.pem");

fn main() -> Result<(), JoseError> { let mut header = JwsHeader::new(); header.settokentype("JWT");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

// Signing JWT
let private_key = std::fs::read(PRIVATE_KEY).unwrap();
let signer = RS256.signer_from_pem(&private_key)?;
let jwt = jwt::encode_with_signer(&payload, &header, &signer)?;

// Verifing JWT
let public_key = std::fs::read(PUBLIC_KEY).unwrap();
let verifier = RS256.verifier_from_pem(&public_key)?;
let (payload, header) = jwt::decode_with_verifier(&jwt, &verifier)?;

Ok(())

} ```

Signing a JWT by RSASSA-PSS

RSASSA-PSS is used to verify the integrity of a message by two keys: public and private.

The raw key format of RSASSA-PSS is the same as RSASSA. So you should use a PKCS#8 wrapped key. It contains some optional attributes.

Three algorithms are available for RSASSA-PSS: PS256, PS384, and PS512. You can generate the keys by executing openssl command.

```sh

Generate a new private key

for PS256

openssl genpkey -algorithm RSA-PSS -pkeyopt rsakeygenbits:2048 -pkeyopt rsapsskeygenmd:sha256 -pkeyopt rsapsskeygenmgf1md:sha256 -pkeyopt rsapsskeygensaltlen:32 -out private.pem

for PS384

openssl genpkey -algorithm RSA-PSS -pkeyopt rsakeygenbits:2048 -pkeyopt rsapsskeygenmd:sha384 -pkeyopt rsapsskeygenmgf1md:sha384 -pkeyopt rsapsskeygensaltlen:48 -out private.pem

for PS512

openssl genpkey -algorithm RSA-PSS -pkeyopt rsakeygenbits:2048 -pkeyopt rsapsskeygenmd:sha512 -pkeyopt rsapsskeygenmgf1md:sha512 -pkeyopt rsapsskeygensaltlen:64 -out private.pem

Generate a public key from the private key.

openssl pkey -in private.pem -pubout -out public.pem ```

```rust use josekit::{JoseError, jws::{JwsHeader, PS256}, jwt::{self, JwtPayload}};

const PRIVATEKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/RSA-PSS2048bitSHA-256private.pem"); const PUBLICKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/RSA-PSS2048bitSHA-256public.pem");

fn main() -> Result<(), JoseError> { let mut header = JwsHeader::new(); header.settokentype("JWT");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

// Signing JWT
let private_key = std::fs::read(PRIVATE_KEY).unwrap();
let signer = PS256.signer_from_pem(&private_key)?;
let jwt = jwt::encode_with_signer(&payload, &header, &signer)?;

// Verifing JWT
let public_key = std::fs::read(PUBLIC_KEY).unwrap();
let verifier = PS256.verifier_from_pem(&public_key)?;
let (payload, header) = jwt::decode_with_verifier(&jwt, &verifier)?;

Ok(())

} ```

Signing a JWT by ECDSA

ECDSA is used to verify the integrity of a message by two keys: public and private. Four algorithms are available for ECDSA: ES256, ES384, ES512 and ES256K.

You can generate the keys by executing openssl command.

```sh

Generate a new private key

for ES256

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:P-256 -out private.pem

for ES384

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:P-384 -out private.pem

for ES512

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:P-521 -out private.pem

for ES256K

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:secp256k1 -out private.pem

Generate a public key from the private key.

openssl pkey -in private.pem -pubout -out public.pem ```

```rust use josekit::{JoseError, jws::{JwsHeader, ES256}, jwt::{self, JwtPayload}};

const PRIVATEKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/ECP-256private.pem"); const PUBLICKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/ECP-256public.pem");

fn main() -> Result<(), JoseError> { let mut header = JwsHeader::new(); header.settokentype("JWT");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

// Signing JWT
let private_key = std::fs::read(PRIVATE_KEY).unwrap();
let signer = ES256.signer_from_pem(&private_key)?;
let jwt = jwt::encode_with_signer(&payload, &header, &signer)?;

// Verifing JWT
let public_key = std::fs::read(PUBLIC_KEY).unwrap();
let verifier = ES256.verifier_from_pem(&public_key)?;
let (payload, header) = jwt::decode_with_verifier(&jwt, &verifier)?;

Ok(())

} ```

Signing a JWT by EdDSA

EdDSA is used to verify the integrity of a message by two keys: public and private. A algorithm is only available "EdDSA" for EdDSA. But it has two curve types: Ed25519, Ed448.

You can generate the keys by executing openssl command.

```sh

Generate a new private key

for Ed25519

openssl genpkey -algorithm ED25519 -out private.pem

for Ed448

openssl genpkey -algorithm ED448 -out private.pem

Generate a public key from the private key.

openssl pkey -in private.pem -pubout -out public.pem ```

```rust use josekit::{JoseError, jws::{JwsHeader, EdDSA}, jwt::{self, JwtPayload}};

const PRIVATEKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/ED25519private.pem"); const PUBLICKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/ED25519public.pem");

fn main() -> Result<(), JoseError> { let mut header = JwsHeader::new(); header.settokentype("JWT");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

// Signing JWT
let private_key = std::fs::read(PRIVATE_KEY).unwrap();
let signer = EdDSA.signer_from_pem(&private_key)?;
let jwt = jwt::encode_with_signer(&payload, &header, &signer)?;

// Verifing JWT
let public_key = std::fs::read(PUBLIC_KEY).unwrap();
let verifier = EdDSA.verifier_from_pem(&public_key)?;
let (payload, header) = jwt::decode_with_verifier(&jwt, &verifier)?;

Ok(())

} ```

Encrypting a JWT by a Direct method

A "Direct" method is used to encrypt a message by CEK (content encryption key). The algorithm name is "dir" only.

You can use any bytes as the key. But the length must be the same as the length of the CEK.

```rust use josekit::{JoseError, jwe::{JweHeader, Dir}, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> { let mut header = JweHeader::new(); header.settokentype("JWT"); header.setcontentencryption("A128CBC-HS256");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

let key = b"0123456789ABCDEF0123456789ABCDEF";

// Encrypting JWT
let encrypter = Dir.encrypter_from_bytes(key)?;
let jwt = jwt::encode_with_encrypter(&payload, &header, &encrypter)?;

// Decrypting JWT
let decrypter = Dir.decrypter_from_bytes(key)?;
let (payload, header) = jwt::decode_with_decrypter(&jwt, &decrypter)?;

Ok(())

} ```

Encrypting a JWT by ECDH-ES

ECDH-ES is used to encrypt a message a message by random bytes as CEK (content encryption key) and the CEK is delivered safely by two keys: public and private. Four algorithms are available for ECDH-ES: ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW and ECDH-ES+A256KW.

The types of key are available both EC and ECX. The EC key has four curve types: P-256, P-384, P-521 and secp256k1. The ECX key has two curve types: X25519 and X448.

You can generate the keys by executing openssl command.

```sh

Generate a new private key

for P-256 EC key

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:P-256 -out private.pem

for P-384 EC key

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:P-384 -out private.pem

for P-521 EC key

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:P-521 -out private.pem

for secp256k1 EC key

openssl genpkey -algorithm EC -pkeyopt ecparamgencurve:secp256k1 -out private.pem

for X25519 ECX key

openssl genpkey -algorithm X25519 -out private.pem

for X448 ECX key

openssl genpkey -algorithm X448 -out private.pem

Generate a public key from the private key.

openssl pkey -in private.pem -pubout -out public.pem ```

```rust use josekit::{JoseError, jwe::{JweHeader, ECDH_ES}, jwt::{self, JwtPayload}};

const PRIVATEKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/ECP-256private.pem"); const PUBLICKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/ECP-256public.pem");

fn main() -> Result<(), JoseError> { let mut header = JweHeader::new(); header.settokentype("JWT"); header.setcontentencryption("A128CBC-HS256");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

// Encrypting JWT
let public_key = std::fs::read(PUBLIC_KEY).unwrap();
let encrypter = ECDH_ES.encrypter_from_pem(&public_key)?;
let jwt = jwt::encode_with_encrypter(&payload, &header, &encrypter)?;

// Decrypting JWT
let private_key = std::fs::read(PRIVATE_KEY).unwrap();
let decrypter = ECDH_ES.decrypter_from_pem(&private_key)?;
let (payload, header) = jwt::decode_with_decrypter(&jwt, &decrypter)?;

Ok(())

} ```

Encrypting a JWT by AESKW

AES is used to encrypt a message by random bytes as CEK (content encryption key) and the CEK is wrapping by common secret key. Three algorithms are available for AES: A128KW, A192KW and A256KW.

You can use any bytes as the key. But the length must be AES key size.

```rust use josekit::{JoseError, jwe::{JweHeader, A128KW}, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> { let mut header = JweHeader::new(); header.settokentype("JWT"); header.setcontentencryption("A128CBC-HS256");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

let key = b"0123456789ABCDEF";

// Encrypting JWT
let encrypter = A128KW.encrypter_from_bytes(key)?;
let jwt = jwt::encode_with_encrypter(&payload, &header, &encrypter)?;

// Decrypting JWT
let decrypter = A128KW.decrypter_from_bytes(key)?;
let (payload, header) = jwt::decode_with_decrypter(&jwt, &decrypter)?;
Ok(())

} ```

Encrypting a JWT by AES-GCM

AES-GCM is used to encrypt a message by random bytes as CEK (content encryption key) and the CEK is wrapping by common secret key. Three algorithms are available for AES-GCM: A128GCMKW, A192GCMKW and A256GCMKW.

You can use any bytes as the key. But the length must be AES key size.

```rust use josekit::{JoseError, jwe::{JweHeader, A128GCMKW}, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> { let mut header = JweHeader::new(); header.settokentype("JWT"); header.setcontentencryption("A128CBC-HS256");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

let key = b"0123456789ABCDEF";

// Encrypting JWT
let encrypter = A128GCMKW.encrypter_from_bytes(key)?;
let jwt = jwt::encode_with_encrypter(&payload, &header, &encrypter)?;

// Decrypting JWT
let decrypter = A128GCMKW.decrypter_from_bytes(key)?;
let (payload, header) = jwt::decode_with_decrypter(&jwt, &decrypter)?;
Ok(())

} ```

Encrypting a JWT by PBES2-HMAC+AESKW

PBES2-HMAC+AES is used to encrypt a message by random bytes as CEK (content encryption key) and the CEK is wrapping by common secret key. Three algorithms are available for AES-GCM: PBES2-HS256+A128KW, PBES2-HS384+A192KW and PBES2-HS512+A256KW.

You can use any bytes as the key. But a password is recommended that the length is no shorter than AES key size and no longer than 128 octets.

```rust use josekit::{JoseError, jwe::{JweHeader, PBES2HS256A128KW}, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> { let mut header = JweHeader::new(); header.settokentype("JWT"); header.setcontentencryption("A128CBC-HS256");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

let key = b"01234567";

// Encrypting JWT
let encrypter = PBES2_HS256_A128KW.encrypter_from_bytes(key)?;
let jwt = jwt::encode_with_encrypter(&payload, &header, &encrypter)?;

// Decrypting JWT
let decrypter = PBES2_HS256_A128KW.decrypter_from_bytes(key)?;
let (payload, header) = jwt::decode_with_decrypter(&jwt, &decrypter)?;
Ok(())

} ```

Encrypting a JWT by RSAES

RSAES is used to encrypt a message a message by random bytes as CEK (content encryption key) and the CEK is delivered safely by two keys: public and private. Two algorithms are available for now: RSA1_5, RSA-OAEP.

You can generate the keys by executing openssl command.

```sh

Generate a new private key. Keygen bits must be 2048 or more.

openssl openssl genpkey -algorithm RSA -pkeyopt rsakeygenbits:2048 -out private.pem

Generate a public key from the private key.

openssl pkey -in private.pem -pubout -out public.pem ```

```rust use josekit::{JoseError, jwe::{JweHeader, RSA_OAEP}, jwt::{self, JwtPayload}};

const PRIVATEKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/RSA2048bitprivate.pem"); const PUBLICKEY: &str = concat!(env!("CARGOMANIFESTDIR"), "/data/pem/RSA2048bitpublic.pem");

fn main() -> Result<(), JoseError> { let mut header = JweHeader::new(); header.settokentype("JWT"); header.setcontentencryption("A128CBC-HS256");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

// Encrypting JWT
let public_key = std::fs::read(PUBLIC_KEY).unwrap();
let encrypter = RSA_OAEP.encrypter_from_pem(&public_key)?;
let jwt = jwt::encode_with_encrypter(&payload, &header, &encrypter)?;

// Decrypting JWT
let private_key = std::fs::read(PRIVATE_KEY).unwrap();
let decrypter = RSA_OAEP.decrypter_from_pem(&private_key)?;
let (payload, header) = jwt::decode_with_decrypter(&jwt, &decrypter)?;
Ok(())

} ```

Unsecured JWT

```rust use josekit::{JoseError, jws::JwsHeader, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> { let mut header = JwsHeader::new(); header.settokentype("JWT");

let mut payload = JwtPayload::new();
payload.set_subject("subject");

let jwt = jwt::encode_unsecured(&payload, &header)?;
let (payload, header) = jwt::decode_unsecured(&jwt)?;
Ok(())

} ```

Validate payload

```rust,should_panic use josekit::{JoseError, jwt::{JwtPayload, JwtPayloadValidator}}; use std::time::{Duration, SystemTime};

fn main() -> Result<(), JoseError> { let mut validator = JwtPayloadValidator::new(); // value based validation validator.setissuer("http://example.com"); validator.setaudience("user1"); validator.setjwtid("550e8400-e29b-41d4-a716-446655440000");

// time based validation: not_before <= base_time < expires_at
validator.set_base_time(SystemTime::now() + Duration::from_secs(30));

// issued time based validation: min_issued_time <= issued_time <= max_issued_time
validator.set_min_issued_time(SystemTime::now() - Duration::from_secs(48 * 60));
validator.set_max_issued_time(SystemTime::now() + Duration::from_secs(24 * 60));

let mut payload = JwtPayload::new();

validator.validate(&payload)?;

Ok(())

} ```

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

References