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