josekit 0.8.1

JOSE (Javascript Object Signing and Encryption) library for Rust.
Documentation

josekit

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

Install

[dependencies]
josekit = "0.8.1"

This library depends on OpenSSL 1.1.1 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

Supported encryption algorithms

Supported key formats

Private Key

Public Key

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(())
}

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