9个版本
0.4.0 | 2021年4月29日 |
---|---|
0.3.8 | 2020年8月28日 |
0.3.6 | 2020年7月25日 |
0.3.4 | 2020年6月29日 |
#2346 in 密码学
71KB
1.5K SLoC
Rust中紧凑的JWT实现 [存档]
被 jwt-simple 取代。
lib.rs
:
以类型安全和安全的加密原语为重点的简约JSON Web令牌(JWT)实现。
设计选择
- JWT签名算法(即提供JWT完整性的加密算法)通过
Algorithm
特质表达,它使用完全类型化的密钥和签名。 - JWT头部由
Header
结构体表示。值得注意的是,Header
不暴露alg
字段。相反,alg
在创建令牌时自动填充,并在验证过程中与预期值进行比较。(如果在验证JWT签名算法时不知道,那么你在做错事。)这消除了算法切换攻击的可能性。
其他特性
- 该包支持更紧凑的CBOR编码的声明。紧凑编码的JWT在头部设置
cty
字段(内容类型)为"CBOR"
。 - 该包支持使用Ed25519椭圆曲线的
EdDSA
算法,以及使用secp256k1椭圆曲线的ES256K
算法。
支持的算法
算法(们) | 特性 | 描述 |
---|---|---|
HS256 、HS384 、HS512 |
- | 使用纯Rust sha2 库 |
EdDSA (Ed25519) |
exonum-crypto |
libsodium 绑定。默认启用 |
EdDSA (Ed25519) |
ed25519-dalek |
纯Rust实现 |
EdDSA (Ed25519) |
ed25519-compact |
紧凑型纯Rust实现,WASM兼容 |
ES256K |
secp256k1 |
libsecp256k1 的绑定 |
RS* 、PS* (RSA) |
[rsa ] |
使用带有盲化的纯Rust [rsa ] 库 |
标准的ES*
算法尚未实现。主要原因(除了懒惰和相关加密后端不友好的API之外)是
ES*
算法中的椭圆曲线使用了一种可能包含后门的生成过程
EdDSA
和 ES256K
算法是非标准的。它们都使用椭圆曲线(Curve25519和secp256k1;这两个在加密社区中广泛使用,并且被认为被安全生成)。这些算法具有128位安全性,可以作为ES256
的替代品。
示例
基本的JWT生命周期
use chrono::{Duration, Utc};
use jwt_compact::{prelude::*, alg::{Hs256, Hs256Key}};
use serde::{Serialize, Deserialize};
use std::convert::TryFrom;
/// Custom claims encoded in the token.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct CustomClaims {
/// `sub` is a standard claim which denotes claim subject:
/// https://tools.ietf.org/html/rfc7519#section-4.1.2
#[serde(rename = "sub")]
subject: String,
}
// Create a symmetric HMAC key, which will be used both to create and verify tokens.
let key = Hs256Key::from(b"super_secret_key_donut_steel" as &[_]);
// Create a token.
let header = Header {
key_id: Some("my-key".to_owned()),
..Default::default()
};
let claims = Claims::new(CustomClaims { subject: "alice".to_owned() })
.set_duration_and_issuance(Duration::days(7))
.set_not_before(Utc::now() - Duration::hours(1));
let token_string = Hs256.token(header, &claims, &key)?;
println!("token: {}", token_string);
// Parse the token.
let token = UntrustedToken::try_from(token_string.as_str())?;
// Before verifying the token, we might find the key which has signed the token
// using the `Header.key_id` field.
assert_eq!(token.header().key_id, Some("my-key".to_owned()));
// Validate the token integrity.
let token: Token<CustomClaims> = Hs256.validate_integrity(&token, &key)?;
// Validate additional conditions.
token
.claims()
.validate_expiration(TimeOptions::default())?
.validate_maturity(TimeOptions::default())?;
// Now, we can extract information from the token (e.g., its subject).
let subject = &token.claims().custom.subject;
assert_eq!(subject, "alice");
紧凑型JWT
/// Custom claims encoded in the token.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct CustomClaims {
/// `sub` is a standard claim which denotes claim subject:
/// https://tools.ietf.org/html/rfc7519#section-4.1.2
/// The custom serializer we use allows to efficiently
/// encode the subject in CBOR.
#[serde(rename = "sub", with = "HexForm")]
subject: [u8; 32],
}
let key = Hs256Key::from(b"super_secret_key_donut_steel" as &[_]);
let claims = Claims::new(CustomClaims { subject: [111; 32] })
.set_duration_and_issuance(Duration::days(7));
let token = Hs256.token(Header::default(), &claims, &key)?;
println!("token: {}", token);
let compact_token = Hs256.compact_token(Header::default(), &claims, &key)?;
println!("compact token: {}", compact_token);
// The compact token should be ~40 chars shorter.
// Parse the compact token.
let token = UntrustedToken::try_from(compact_token.as_str())?;
let token: Token<CustomClaims> = Hs256.validate_integrity(&token, &key)?;
token.claims().validate_expiration(TimeOptions::default())?;
// Now, we can extract information from the token (e.g., its subject).
assert_eq!(token.claims().custom.subject, [111; 32]);
依赖项
~8-13MB
~225K SLoC