#jwt #jws #joes #jwe #jwa

josekit

JOSE (Javascript Object Signing and Encryption) library for Rust

4 releases (2 breaking)

new 0.4.1 Sep 15, 2020
0.4.0 Sep 13, 2020
0.3.0 Aug 18, 2020
0.2.0 Aug 12, 2020

#28 in Authentication

25 downloads per month

MIT/Apache

610KB
14K SLoC

josekit

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

Install

[dependencies]
josekit = "0.4.1"

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

Build

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: 256 bits or more)
HS384 HMAC using SHA-384 oct (size: 384 bits or more)
HS512 HMAC using SHA-512 oct (size: 512 bits 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: 128 bits)
A192KW AES Key Wrap with default initial value using 192-bit key oct (size: 192 bits)
A256KW AES Key Wrap with default initial value using 256-bit key oct (size: 256 bits)
A128GCMKW Key wrapping with AES GCM using 128-bit key oct (size: 128 bits)
A192GCMKW Key wrapping with AES GCM using 192-bit key oct (size: 192 bits)
A256GCMKW Key wrapping with AES GCM using 256-bit key oct (size: 256 bits)
PBES2-HS256+A128KW PBES2 with HMAC SHA-256 and "A128KW" wrapping oct (size: 1 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 -
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.

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

fn main() -> Result<(), JoseError> {
    let mut header = JwsHeader::new();
    header.set_token_type("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.

# Generate a new private key. Keygen bits must be 2048 or more.
openssl openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem

# Generate a public key from the private key.
openssl pkey -in private.pem -pubout -out public.pem
use josekit::{JoseError, jws::{JwsHeader, RS256}, jwt::{self, JwtPayload}};

const PRIVATE_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/RSA_2048bit_private.pem");
const PUBLIC_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/RSA_2048bit_public.pem");

fn main() -> Result<(), JoseError> {
    let mut header = JwsHeader::new();
    header.set_token_type("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.

# Generate a new private key

# for PS256
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_pss_keygen_md:sha256 -pkeyopt rsa_pss_keygen_mgf1_md:sha256 -pkeyopt rsa_pss_keygen_saltlen:32 -out private.pem

# for PS384
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_pss_keygen_md:sha384 -pkeyopt rsa_pss_keygen_mgf1_md:sha384 -pkeyopt rsa_pss_keygen_saltlen:48 -out private.pem

# for PS512
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_pss_keygen_md:sha512 -pkeyopt rsa_pss_keygen_mgf1_md:sha512 -pkeyopt rsa_pss_keygen_saltlen:64 -out private.pem

# Generate a public key from the private key.
openssl pkey -in private.pem -pubout -out public.pem
use josekit::{JoseError, jws::{JwsHeader, PS256}, jwt::{self, JwtPayload}};

const PRIVATE_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/RSA-PSS_2048bit_SHA-256_private.pem");
const PUBLIC_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/RSA-PSS_2048bit_SHA-256_public.pem");

fn main() -> Result<(), JoseError> {
    let mut header = JwsHeader::new();
    header.set_token_type("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.

# Generate a new private key

# for ES256
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem

# for ES384
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out private.pem

# for ES512
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-521 -out private.pem

# for ES256K
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:secp256k1 -out private.pem

# Generate a public key from the private key.
openssl pkey -in private.pem -pubout -out public.pem
use josekit::{JoseError, jws::{JwsHeader, ES256}, jwt::{self, JwtPayload}};

const PRIVATE_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/EC_P-256_private.pem");
const PUBLIC_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/EC_P-256_public.pem");

fn main() -> Result<(), JoseError> {
    let mut header = JwsHeader::new();
    header.set_token_type("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.

# 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
use josekit::{JoseError, jws::{JwsHeader, EdDSA}, jwt::{self, JwtPayload}};

const PRIVATE_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/ED25519_private.pem");
const PUBLIC_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/ED25519_public.pem");

fn main() -> Result<(), JoseError> {
    let mut header = JwsHeader::new();
    header.set_token_type("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.

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

fn main() -> Result<(), JoseError> {
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("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.

# Generate a new private key

# for P-256 EC key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private.pem

# for P-384 EC key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out private.pem

# for P-521 EC key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-521 -out private.pem

# for secp256k1 EC key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve: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
use josekit::{JoseError, jwe::{JweHeader, ECDH_ES}, jwt::{self, JwtPayload}};

const PRIVATE_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/EC_P-256_private.pem");
const PUBLIC_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/EC_P-256_public.pem");

fn main() -> Result<(), JoseError> {
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("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.

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

fn main() -> Result<(), JoseError> {
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("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.

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

fn main() -> Result<(), JoseError> {
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("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.

use josekit::{JoseError, jwe::{JweHeader, PBES2_HS256_A128KW}, jwt::{self, JwtPayload}};

fn main() -> Result<(), JoseError> {
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("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.

# Generate a new private key. Keygen bits must be 2048 or more.
openssl openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem

# Generate a public key from the private key.
openssl pkey -in private.pem -pubout -out public.pem
use josekit::{JoseError, jwe::{JweHeader, RSA_OAEP}, jwt::{self, JwtPayload}};

const PRIVATE_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/RSA_2048bit_private.pem");
const PUBLIC_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), 
    "/data/pem/RSA_2048bit_public.pem");

fn main() -> Result<(), JoseError> {
    let mut header = JweHeader::new();
    header.set_token_type("JWT");
    header.set_content_encryption("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

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

fn main() -> Result<(), JoseError> {
    let mut header = JwsHeader::new();
    header.set_token_type("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

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

fn main() -> Result<(), JoseError> {
    let mut validator = JwtPayloadValidator::new();
    // value based validation
    validator.set_issuer("http://example.com");
    validator.set_audience("user1");
    validator.set_jwt_id("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(())
}

ToDo

  • Supports multiple recipients jwe json serialization.

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

Dependencies

~6MB
~136K SLoC