#json #jwt #jose #jws #jwe

no-way

一个用于处理 JavaScript 对象签名和加密(JOSE)的库,包括 JSON Web 令牌(JWT)、JSON Web 签名(JWS)和 JSON Web 加密(JWE)

6 个版本 (3 个重大更新)

0.4.1 2022 年 10 月 9 日
0.4.0 2022 年 10 月 8 日
0.3.0 2022 年 10 月 7 日
0.2.0 2022 年 10 月 3 日
0.1.1 2022 年 10 月 1 日

#3#jwe

MIT 许可证

325KB
6K SLoC

No Way, Jose!

一个用于处理 JavaScript 对象签名和加密(JOSE)的库,包括

  • JSON Web 令牌(JWT)
  • JSON Web 签名(JWS)
  • JSON Web 加密(JWE)
  • JSON Web 算法(JWA)
  • JSON Web 密钥(JWK)

本库受到 lawliet89/biscuit 的启发,而后者又基于 Keats/rust-jwt.

配置

所有加密算法都在编译时选择。这降低了臭名昭著的 'none' 攻击的风险。

支持的功能

该软件包不支持所有功能,并且可能永远不会支持在各种 RFC 中描述的所有功能,包括一些算法和验证。

快速演示

JWTs

use no_way::{JWT, jwa, jws, jwk, ClaimsSet, RegisteredClaims};
use serde::{Serialize, Deserialize};

// Define our own private claims
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
struct PrivateClaims {
    company: String,
    department: String,
}

let signing_key = jwk::OctetKey::new("secret".to_string().into_bytes());

let expected_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.\
       eyJpc3MiOiJodHRwczovL3d3dy5hY21lLmNvbS8iLCJzdWIiOiJKb2huIERvZSIsImF1ZCI6Imh0dHBzOi8vYWNtZ\
       S1jdXN0b21lci5jb20vIiwibmJmIjoxMjM0LCJjb21wYW55IjoiQUNNRSIsImRlcGFydG1lbnQiOiJUb2lsZXQgQ2\
       xlYW5pbmcifQ.VFCl2un1Kc17odzOe2Ehf4DVrWddu3U4Ux3GFpOZHtc";

let expected_claims = ClaimsSet::<PrivateClaims> {
    registered: RegisteredClaims {
        issuer: Some("https://www.acme.com/".into()),
        subject: Some("John Doe".to_string()),
        audience: Some("https://acme-customer.com/".into()),
        not_before: Some(1234.try_into().unwrap()),
        ..Default::default()
    },
    private: PrivateClaims {
        department: "Toilet Cleaning".to_string(),
        company: "ACME".to_string(),
    },
};

let jwt = JWT::new(expected_claims.clone());

let token = jwt.encode::<jwa::sign::HS256>(&signing_key).unwrap().to_string();
assert_eq!(expected_token, token);
// Now, send `token` to your clients

// ... some time later, we get token back!

let encoded_token: jws::Encoded<ClaimsSet::<PrivateClaims>> = token.parse().unwrap();
let token = JWT::<_>::decode::<jwa::sign::HS256>(encoded_token, &signing_key).unwrap();
assert_eq!(token.payload, expected_claims);

JWEs

use no_way::{ClaimsSet, RegisteredClaims, JWT, JWE};
use no_way::jwk;
use no_way::jwe::Encrypted;
use no_way::jwa::{kma, cea, sign};
use serde::{Serialize, Deserialize};

// Define our own private claims
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
struct PrivateClaims {
    company: String,
    department: String,
}

// Craft our JWS
let expected_claims = ClaimsSet::<PrivateClaims> {
    registered: RegisteredClaims {
        issuer: Some("https://www.acme.com".into()),
        subject: Some("John Doe".into()),
        audience: Some("htts://acme-customer.com".into()),
        not_before: Some(1234.try_into().unwrap()),
        ..Default::default()
    },
    private: PrivateClaims {
        department: "Toilet Cleaning".to_string(),
        company: "ACME".to_string(),
    },
};

let expected_jwt = JWT::new(expected_claims.clone());

let signing_key = jwk::OctetKey::new("secret".to_string().into_bytes());
let jws = expected_jwt.encode::<sign::HS256>(&signing_key).unwrap();

// Encrypt the token

// You would usually have your own AES key for this, but we will use a zeroed key as an example
let key = jwk::OctetKey::new(vec![0; 256 / 8]);

/// We need to create a nonce for AES GCM encryption.
/// You must take care NOT to reuse the nonce.
/// You can simply treat the nonce as a 96 bit
/// counter that is incremented after every use
///
/// In this case, we're using a 64bit counter + a 32bit random prefix tag
fn generate_nonce() -> Vec<u8> {
    # use std::sync::atomic::{AtomicU64, Ordering};
    static NONCE: AtomicU64 = AtomicU64::new(0);
    // use some lazy random generation so each service has a separate tag
    static TAG: u32 = 0xDEADCAFE;

    // fetch and increment the nonce counter
    let nonce = NONCE.fetch_add(1, Ordering::Release);

    // collect the bytes together and return them
    let mut output = vec![0; 96/8];
    output[0..32/8].copy_from_slice(&TAG.to_be_bytes());
    output[32/8..].copy_from_slice(&nonce.to_be_bytes());
    output
}
let nonce = generate_nonce();

// Construct the JWE
let jwe = JWE::new(jws.clone());

// Encrypt
let encrypted_jwe = jwe.encrypt::<
    cea::A256GCM,   // encrypt the contents with AES256 GCM
    kma::A256GCMKW, // perform key wrapping with AES256 GCM
>(&key, nonce).unwrap();

let token = encrypted_jwe.to_string();

// Now, send `token` to your clients

// ... some time later, we get token back!
let token: Encrypted<kma::A256GCMKW> = token.parse().unwrap();

// Decrypt
let decrypted_jwe = token.decrypt::<_, cea::A256GCM>(&key).unwrap();

assert_eq!(jws, decrypted_jwe.payload);

依赖关系

~8.5MB
~169K SLoC