Git Product home page Git Product logo

josekit-rs's Introduction

josekit

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

Install

[dependencies]
josekit = "0.8.6"

This library depends on OpenSSL 1.1.1 or above 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: 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: 48 bytes
  • A256CBC-HS512: 64 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.

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 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 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

josekit-rs's People

Contributors

0xpr03 avatar arronwy avatar curtisleefulton avatar dodomorandi avatar guzzit avatar hidekatsu-izuno avatar jake-s6 avatar lambdalisue avatar michaelpaddon avatar naftulikay avatar puiterwijk avatar rongduan-zhu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

josekit-rs's Issues

JWS with x5c header parameter

Hi,

thanks for this great lib. Helps a lot for a project using JWS.

Have a question though, cannot figure it out, and that is how to use the x5c header parameter with JWS.

As far as I can see I get no help from the lib to parse out the header so that I can get a hold of the certificate(s) in the x5c parameter.

Now I do something like this:

pub fn deserialize(jws: &str) -> String {
    // Only support for rs256
    let alg = Rs256;

    //"Manually" decode header. Is there a way to do this in josekit?
    let header = decode_header(jws).unwrap();

    let header = JwsHeader::from_bytes(&header).unwrap();

    // Get signing certificate (first certificate)
    let cert = &header.x509_certificate_chain().unwrap()[0];

    // Get public key
    let key = get_public_key_from_der(cert);

    // Verify
    let verifier = alg.verifier_from_der(&key).unwrap();
    let (dst_payload, _dst_header) = jws::deserialize_compact(&jws, &verifier).unwrap();

    from_utf8(&dst_payload).unwrap().to_string()
}

But if I could use the deserialize_compact_with_selector function (https://docs.rs/josekit/0.8.1/josekit/jws/fn.deserialize_compact_with_selector.html) and use the closure to create the verifier, since in the closer I have the header, it would make life easier. This is not possible now though right? Since the verifier in the closer returns a reference to a JwsVerifier, it need to be owned somewhere before. Could it be modified (or a new function added) that returns a box?

Or is there a better way that I'm missing?

Thanks!

Only decode header

I would like to decode the header of a JWT to get the kid (i need the kid to get the JWK from redis).
Currently there is no way to get it because decode_unsecured don't want to parse JWT with signature part or alg header.
The best would be to create a trait for JwkSet and allow to customize how the JwkSet handle when and id is missing.

And thx for this lib, it's the only one able to manage JWK for ECDSA algorythms all other libs depend on ring which is only able to decode it from PEM or DER (and i didn't find a way to generate PEM from JWK).

[QUESTION] how to properly use decode_with_verifier_in_jwk_set

Hey,

I am new to rust and I am having trouble using the library. Could you help me?

I have a jwk_set, and I want to use it to decode a token. I am unsure what to put in the arg selector.

I am trying to use decode_with_verifier_in_jwk_set but having little success. ChatGpt suggested something like:


    let (payload, header) = decode_with_verifier_in_jwk_set(
        token,
        jwk_set,
        |jwk| -> Result<Option<&dyn JwsVerifier>, JoseError> {

            if jwk.algorithm().map_or(false, |alg| alg == "RS256") {

                let verifier = RS256.verifier_from_jwk(jwk)?;
                // Box the verifier to create a trait object and then convert the Box into a reference.
                let boxed: Box<dyn JwsVerifier> = Box::new(verifier);

                // Convert the Box into a trait object reference.
                let trait_object_ref: &dyn JwsVerifier = &*boxed;
                // Leak the Box to avoid it being dropped at the end of this scope.
                // This is safe because the trait object is essentially 'static within the context of this closure's execution.
                // However, it does mean the memory will not be reclaimed until the end of the program.
                let leaked: &dyn JwsVerifier = Box::leak(boxed);
                return Ok(Some(leaked));
            }

            Ok(None)

        }
    )
        .map_err(|e| e.to_string())?;

But seems weird and overly complex. I would appreciate any help.

Thank you

set_x509_certificate_chain (x5c) implementation is wrong

Issue: set_x509_certificate_chain() encodes input as base64url-encoded, not plain base64-encoded.

Per RFC 7515, 4.1.6 (https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6) each string entry in the x5c header parameter is a "base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.2008] PKIX certificate value."

This is a one-line fix in jws_header.rs

Likewise, x509_certificate_chain() has the same issue in reverse.

Of course, if you're using josekit for both encoding and decoding, you'll never notice :)

x509_certificate_chain must be encoded in STANDARD, not URL_SAFE_NO_PAD or STANDARD_NOPAD

x509_certificate_chain is encoded STANDARD_NOPAD or STANDARD_NOPAD. But this behavior violates the specification.

https://www.rfc-editor.org/rfc/rfc7515#page-11

[4.1.6](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.6).  "x5c" (X.509 Certificate Chain) Header Parameter

...
Each string in the array is a
   base64-encoded ([Section 4 of [RFC4648]](https://www.rfc-editor.org/rfc/rfc4648#section-4) -- not base64url-encoded) DER
   [[ITU.X690.2008](https://www.rfc-editor.org/rfc/rfc7515#ref-ITU.X690.2008)] PKIX certificate value.

Support the `ECMR` operation for JWK exchange

To quote the docs on jose-jwk-exc: https://manpages.ubuntu.com/manpages/bionic/man1/jose-jwk-exc.1.html

The ECMR algorithm has three modes of operation. Where the local key has a private key (the "d" property), it performs exactly like ECDH. If the local key does not have a private key and the remote key does have a private key, elliptic curve addition is performed on the two values. Otherwise, if neither the local key nor the remote key have a private key, the remote key is subtracted from the local key using elliptic curve subtraction. When using ECMR, be sure to validate the content of your inputs to avoid triggering the incorrect operation!

Decoding/Verifying with JWK Set or Verifier Selector

Background

I have written an example using josekit to just demonstrate the following:

  1. Generate public/private EdDSA/Curve25519 keypair.
  2. DIsplay them in PEM/JWK format.
  3. Generate header/claims.
  4. Generate signed token from header/claims using the private key.
  5. Verify that signed token using the public key.

I'm currently using josekit::jwt::decode_with_verifier to verify the signed token, but I'd like to use either josekit::jwt::decode_with_verifier_in_jwk_set or josekit::jwt::decode_with_verifier_selector so that I can verify with a list of different potential keys.

Goal

The exact use-case that I'm trying to support is similar to the way that Google provides their JWT signing keys (in PEM format here, in JWK format here) for use during OAuth 2 flows for serverside applications:

  1. On start, your application fetches the Google JWT signing keys from either:
  2. When Google's OAuth flow sends a application/x-www-form-urlencoded POST request including a JWT token, your application is responsible for verifying the returned token using their certificate list from above.

So far, I've been using the jwt crate, but josekit supports much more, so I'm trying to migrate my application.

Question

I'll only be signing tokens with one key at a time, but I'd like to be verifying tokens with a list of public keys. I'm a bit confused by the APIs for decode_with_verifier_in_jwk_set, where the API looks like this:

pub fn decode_with_verifier_selector<'a, F>(
    input: impl AsRef<[u8]>,
    selector: F,
) -> Result<(JwtPayload, JwsHeader), JoseError>
where
    F: Fn(&JwsHeader) -> Result<Option<&'a dyn JwsVerifier>, JoseError>

as well as decode_with_verifier_selector, where the API looks like this:

pub fn decode_with_verifier_in_jwk_set<F>(
    input: impl AsRef<[u8]>,
    jwk_set: &JwkSet,
    selector: F,
) -> Result<(JwtPayload, JwsHeader), JoseError>
where
    F: Fn(&Jwk) -> Result<Option<&dyn JwsVerifier>, JoseError>

I can't seem to determine what the expected usage of these APIs are. I'm probably going to have a josekit::jwk::JwkSet of public keys, and I'd like to use the kid field in the JWT header to select the JWK key based on its key id.

How do I use these APIs to do this?

Allow deserialization of JwtPayload

Hello, I'd like to propose adding a method for deserialization of the JwtPayload.

Background: I want to use josekit to verify the basic claims (exp,aud,sub,iss) and then deserialize multiple (other) claims that I expect to exist, using them as part of the API.
Doing that is currently very code intense and a lot of error handling:

let name: String = payload.claim("name")
        .ok_or(AuthError::MissingClaim("name"))
        .and_then(|v|josekit::Value::deserialize(v).map_err(AuthError::from))?;
let delete_after: Option<u32> = match payload.claim("delete_after").map(Deserialize::deserialize) {
         None => None,
         Some(Ok(i)) => Some(i),
         Some(Err(_)) => return Err(AuthError::InvalidClaim("delete_after")),
};

As the JwtPayload contains the inner parts of json::Value::Object, I'd like to do something like

#[derive(Deserialize)]
struct ExpectedClaims {
    name: String,
    should_timeout: Option<u32>,
    ...
}
// handle invalid/missing claims at once
let my_claims: ExpectedClaims = payload.deserialize()?;

The less API intrusive way would be providing a into_inner(self) -> Map<String,Value> using the into_inner(self) -> Map<String,Value> function. I just found the function ๐Ÿ˜…. But I think this could be expanded by directly returning json::Value::Object.

let claims: josekit::Map<String,josekit::Value>  = payload.into();
let val = serde_json::Value::Object(claims);
let my_claims: ExpectedClaims = serde_json::from_value(val)?;

Any thoughts ? Am I missing something here ?
If you would like to, I can open a PR for one of them.

[QUESTION] How can I cast JoseHeader to JwsHeader?

Hello,

So I use the decode_header function but I want the more specific JwsHeader instead of Box<dyn JoseHeader> to make use of the member functions to call algorithm or x509_certificate_sha1_thumbprint functions. How can I do it?

let header = josekit::jwt::decode_header(jws)?;

An equivalent function to node-jose's createEncrypt

Hi. First of all, thanks for josekit.

I'm trying to port my javascript code to rust using josekit-rs. Specifically the following code:

let JWK_KEY = {
    "kty":  ("RSA"),
    "e":  ("AQAB"),
    "kid":  ("5fe64480-444f-4d4a-af27-aa2a750239af"),
    "n":  ("3NxCce6lBGfYCDCMejT1mc9Y0YmGx-S6herOLq6DQw1hyULsKsdOLWM47hAfxL4tTx1Y-lE082xyuKg96a9zXhoGiEvwXYFJAMxtQUcbxvTLYy2rtgclVok5UkOIE1IoYPnZ8EqTDk7YcWwxfRPGw-o1Kpuq7cB5Eymvoiq7x566PSPDFkZZGBdvlhNNK1qgUyEtpAxI9_6zfiKHVjmJjzAGh9UoiKiWAEIFUwB-SayhekZmDvM1h2OwOeinIA19R4dwloP1HtdRyuTLKxfppixgMDiWi6EO_xsY5FJwsO0Ak7ydLu3avI8_ZtwZOCYDzH7xz9ISecWkctiPHDHMBw"),
}
JWE.createEncrypt({format: "compact"}, JWK_KEY).update(payload).final()

I've used the following rust

    let mut payload = JwtPayload::new();
    let JWK_KEY: Map<String, serde_json::Value> = (THE_ABOVE_JSON).as_object().unwrap().clone();
    payload.set_subject(...);
    let mut header = JweHeader::new();
    header.set_content_encryption("A128CBC-HS256");
    let key = Jwk::from_map(JWK_KEY)?;
    let enc = josekit::jwe::RSA_OAEP.encrypter_from_jwk(&key)?;
    let jwt = jwt::encode_with_encrypter(&payload, &header, &enc)?;

The final encoded jwt s are not equal, but the JWK_KEY and payload are indeed equal

Allow to enable openssl vendored feature to cross compile

I have a project that needs to be able to compile to multiple targets on the same machine, but it fails when compiling to a non-local platform target because openssl depends on the local libssl library. In order to complete the compilation to multiple targets on the same host machine for this target, we need to enable the vendored feature of openssl, so that it will compile openssl from source at the right time (openssl-src).

openssl without vendored feature

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.