mirror of
https://github.com/BitskiCo/jwk-rs
synced 2024-11-22 03:49:22 +00:00
Add key generation
This commit is contained in:
parent
fa22e01714
commit
ddb2613c4e
@ -14,7 +14,9 @@ bitflags = "1.2"
|
|||||||
derive_more = "0.99"
|
derive_more = "0.99"
|
||||||
jsonwebtoken = { version = "7.2", optional = true }
|
jsonwebtoken = { version = "7.2", optional = true }
|
||||||
num-bigint = { version = "0.2", optional = true }
|
num-bigint = { version = "0.2", optional = true }
|
||||||
|
p256 = { version = "0.3", optional = true }
|
||||||
paste = "0.1"
|
paste = "0.1"
|
||||||
|
rand = { version = "0.7", optional = true }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
syn = { version = "1.0", features = ["full"] } # required to parse const generics
|
syn = { version = "1.0", features = ["full"] } # required to parse const generics
|
||||||
@ -24,6 +26,7 @@ zeroize = { version = "1.1", features = ["zeroize_derive"] }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
convert = ["num-bigint", "yasna"]
|
convert = ["num-bigint", "yasna"]
|
||||||
|
generate = ["p256", "rand"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
jsonwebtoken = "7.2"
|
jsonwebtoken = "7.2"
|
||||||
|
@ -40,4 +40,6 @@ fn main() {
|
|||||||
|
|
||||||
* `convert` - enables `Key::{to_der, to_pem}`.
|
* `convert` - enables `Key::{to_der, to_pem}`.
|
||||||
This pulls in the [yasna](https://crates.io/crates/yasna) crate.
|
This pulls in the [yasna](https://crates.io/crates/yasna) crate.
|
||||||
|
* `generate` - enables `Key::{generate_p256, generate_symmetric}`.
|
||||||
|
This pulls in the [p256](https://crates.io/crates/p256) and [rand](https://crates.io/crates/rand) crates.
|
||||||
* `jsonwebtoken` - enables conversions to types in the [jsonwebtoken](https://crates.io/crates/jsonwebtoken) crate.
|
* `jsonwebtoken` - enables conversions to types in the [jsonwebtoken](https://crates.io/crates/jsonwebtoken) crate.
|
||||||
|
48
src/lib.rs
48
src/lib.rs
@ -200,9 +200,12 @@ impl Key {
|
|||||||
pkcs8::write_private(oids, |writer: &mut DERWriterSeq| {
|
pkcs8::write_private(oids, |writer: &mut DERWriterSeq| {
|
||||||
writer.next().write_i8(1); // version
|
writer.next().write_i8(1); // version
|
||||||
writer.next().write_bytes(private_point.as_slice());
|
writer.next().write_bytes(private_point.as_slice());
|
||||||
writer.next().write_tagged(Tag::context(0), |writer| {
|
// The following tagged value is optional. OpenSSL produces it,
|
||||||
writer.write_oid(&prime256v1_oid)
|
// but many tools, including jwt.io and `jsonwebtoken`, don't like it,
|
||||||
});
|
// so we don't include it.
|
||||||
|
// writer.next().write_tagged(Tag::context(0), |writer| {
|
||||||
|
// writer.write_oid(&prime256v1_oid)
|
||||||
|
// });
|
||||||
writer.next().write_tagged(Tag::context(1), write_public);
|
writer.next().write_tagged(Tag::context(1), write_public);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -286,6 +289,45 @@ impl Key {
|
|||||||
writeln!(&mut pem, "-----END {} KEY-----", key_ty).unwrap();
|
writeln!(&mut pem, "-----END {} KEY-----", key_ty).unwrap();
|
||||||
Ok(pem)
|
Ok(pem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a new symmetric key with the specified number of bits.
|
||||||
|
/// Best used with one of the HS algorithms (e.g., HS256).
|
||||||
|
#[cfg(feature = "generate")]
|
||||||
|
pub fn generate_symmetric(num_bits: usize) -> Self {
|
||||||
|
use rand::RngCore;
|
||||||
|
let mut bytes = Vec::with_capacity(num_bits / 8);
|
||||||
|
rand::thread_rng().fill_bytes(&mut bytes);
|
||||||
|
Self::Symmetric { key: bytes.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a new EC keypair using the prime256 curve.
|
||||||
|
/// Used with the ES256 algorithm.
|
||||||
|
#[cfg(feature = "generate")]
|
||||||
|
pub fn generate_p256() -> Self {
|
||||||
|
use p256::elliptic_curve::generic_array::GenericArray;
|
||||||
|
use rand::RngCore;
|
||||||
|
|
||||||
|
let mut sk_bytes = GenericArray::default();
|
||||||
|
rand::thread_rng().fill_bytes(&mut sk_bytes);
|
||||||
|
let sk = p256::SecretKey::new(sk_bytes);
|
||||||
|
let sk_scalar = p256::arithmetic::Scalar::from_secret(sk).unwrap();
|
||||||
|
|
||||||
|
let pk = p256::arithmetic::ProjectivePoint::generator() * &sk_scalar;
|
||||||
|
let pk_bytes = &pk
|
||||||
|
.to_affine()
|
||||||
|
.unwrap()
|
||||||
|
.to_uncompressed_pubkey()
|
||||||
|
.into_bytes()[1..];
|
||||||
|
let (x_bytes, y_bytes) = pk_bytes.split_at(32);
|
||||||
|
|
||||||
|
Self::EC {
|
||||||
|
curve: Curve::P256 {
|
||||||
|
d: Some(sk_scalar.to_bytes().into()),
|
||||||
|
x: ByteArray::try_from_slice(x_bytes).unwrap(),
|
||||||
|
y: ByteArray::try_from_slice(y_bytes).unwrap(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
80
src/tests.rs
80
src/tests.rs
@ -85,6 +85,32 @@ fn serialize_es256() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "generate")]
|
||||||
|
#[test]
|
||||||
|
fn generate_p256() {
|
||||||
|
extern crate jsonwebtoken as jwt;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct TokenClaims {}
|
||||||
|
|
||||||
|
let mut the_jwk = JsonWebKey::new(Key::generate_p256());
|
||||||
|
the_jwk.set_algorithm(JsonWebAlgorithm::ES256).unwrap();
|
||||||
|
|
||||||
|
let encoding_key = jwt::EncodingKey::from_ec_der(&the_jwk.key.to_der().unwrap());
|
||||||
|
let token = jwt::encode(
|
||||||
|
&jwt::Header::new(the_jwk.algorithm.unwrap().into()),
|
||||||
|
&TokenClaims {},
|
||||||
|
&encoding_key,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut validation = jwt::Validation::new(the_jwk.algorithm.unwrap().into());
|
||||||
|
validation.validate_exp = false;
|
||||||
|
let public_pem = the_jwk.key.to_public().unwrap().to_pem().unwrap();
|
||||||
|
let decoding_key = jwt::DecodingKey::from_ec_pem(public_pem.as_bytes()).unwrap();
|
||||||
|
jwt::decode::<TokenClaims>(&token, &decoding_key, &validation).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialize_hs256() {
|
fn deserialize_hs256() {
|
||||||
let jwk_str = r#"{
|
let jwk_str = r#"{
|
||||||
@ -136,66 +162,65 @@ fn deserialize_rs256() {
|
|||||||
public: RsaPublic {
|
public: RsaPublic {
|
||||||
e: PublicExponent,
|
e: PublicExponent,
|
||||||
n: vec![
|
n: vec![
|
||||||
149, 122, 70, 152, 26, 8, 198, 62, 122, 78, 154, 109, 2, 150, 154, 115,
|
164, 44, 219, 113, 223, 100, 142, 248, 57, 173, 241, 135, 116, 67, 22, 157,
|
||||||
165, 117, 247, 254, 214, 89, 186, 180, 107, 94, 26, 229, 65, 193, 6, 89,
|
122, 56, 247, 54, 193, 232, 82, 208, 250, 109, 1, 208, 27, 213, 167, 70,
|
||||||
28, 231, 131, 112, 33, 70, 182, 95, 138, 125, 57, 226, 182, 179, 46, 171,
|
168, 141, 85, 152, 107, 76, 110, 140, 47, 153, 63, 182, 97, 196, 28, 143,
|
||||||
45, 15, 245, 131, 80, 28, 75, 7, 236, 85, 76, 188, 48, 255, 145, 139
|
199, 39, 54, 61, 172, 240, 20, 146, 98, 246, 43, 217, 254, 8, 17, 195
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
private: Some(RsaPrivate {
|
private: Some(RsaPrivate {
|
||||||
d: vec![
|
d: vec![
|
||||||
94, 141, 21, 0, 123, 95, 87, 127, 7, 192, 150, 192, 35, 165, 254, 22, 238,
|
65, 218, 124, 107, 192, 223, 229, 57, 76, 105, 169, 104, 92, 10, 77, 23,
|
||||||
239, 187, 42, 16, 142, 123, 162, 74, 84, 33, 113, 40, 241, 159, 64, 89,
|
253, 222, 187, 203, 11, 28, 213, 155, 93, 216, 59, 209, 238, 88, 85, 48,
|
||||||
124, 179, 255, 12, 172, 93, 39, 96, 13, 129, 49, 149, 19, 76, 118, 227,
|
44, 209, 101, 31, 104, 135, 249, 115, 121, 253, 233, 26, 195, 12, 12, 230,
|
||||||
118, 7, 170, 21, 145, 28, 186, 27, 110, 96, 220, 157, 75, 140, 57
|
48, 76, 32, 42, 114, 123, 3, 83, 73, 244, 217, 115, 207, 134, 116, 1
|
||||||
]
|
]
|
||||||
.into(),
|
.into(),
|
||||||
p: Some(
|
p: Some(
|
||||||
vec![
|
vec![
|
||||||
252, 180, 162, 167, 154, 56, 121, 161, 159, 219, 155, 175, 195, 37, 42,
|
232, 4, 56, 200, 119, 159, 215, 182, 167, 254, 46, 75, 64, 241, 205,
|
||||||
246, 230, 209, 180, 167, 166, 172, 38, 168, 11, 27, 166, 162, 62, 183,
|
35, 28, 233, 31, 174, 113, 88, 228, 159, 254, 160, 129, 238, 175, 165,
|
||||||
2, 237
|
95, 35
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
),
|
),
|
||||||
q: Some(
|
q: Some(
|
||||||
vec![
|
vec![
|
||||||
151, 109, 34, 46, 152, 156, 17, 109, 238, 141, 173, 25, 131, 108, 79,
|
181, 37, 95, 165, 231, 194, 177, 253, 98, 90, 96, 44, 215, 54, 47, 197,
|
||||||
232, 56, 217, 107, 206, 155, 15, 130, 16, 223, 1, 87, 9, 194, 130, 127,
|
209, 44, 82, 43, 244, 84, 193, 46, 64, 27, 91, 78, 40, 227, 252, 225
|
||||||
87
|
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
),
|
),
|
||||||
dp: Some(
|
dp: Some(
|
||||||
vec![
|
vec![
|
||||||
250, 76, 172, 195, 23, 149, 18, 156, 140, 235, 7, 84, 219, 20, 104,
|
169, 89, 203, 136, 167, 168, 72, 111, 206, 151, 61, 123, 56, 96, 70,
|
||||||
110, 239, 135, 12, 201, 245, 227, 147, 210, 100, 86, 58, 1, 127, 222,
|
119, 134, 182, 178, 165, 69, 158, 184, 225, 255, 157, 112, 185, 164, 3,
|
||||||
227, 173
|
117, 57
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
),
|
),
|
||||||
dq: Some(
|
dq: Some(
|
||||||
vec![
|
vec![
|
||||||
58, 163, 22, 19, 121, 33, 38, 86, 173, 131, 203, 62, 15, 248, 71, 81,
|
24, 191, 196, 115, 172, 88, 131, 108, 245, 21, 23, 242, 200, 108, 148,
|
||||||
35, 130, 126, 14, 185, 88, 222, 2, 238, 120, 52, 94, 33, 38, 43, 109
|
214, 88, 31, 208, 18, 69, 77, 151, 31, 52, 143, 8, 72, 131, 121, 178,
|
||||||
|
193
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
),
|
),
|
||||||
qi: Some(
|
qi: Some(
|
||||||
vec![
|
vec![
|
||||||
218, 108, 192, 105, 42, 251, 35, 80, 247, 188, 59, 78, 133, 181, 138,
|
105, 216, 80, 28, 127, 8, 25, 113, 95, 44, 67, 39, 103, 155, 127, 77,
|
||||||
75, 223, 189, 16, 180, 71, 41, 176, 7, 207, 135, 97, 159, 128, 210, 8,
|
224, 169, 231, 56, 18, 193, 9, 45, 39, 105, 102, 202, 92, 84, 27, 67
|
||||||
26
|
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
algorithm: Some(JsonWebAlgorithm::RS256),
|
algorithm: None,
|
||||||
key_id: None,
|
key_id: None,
|
||||||
key_ops: KeyOps::empty(),
|
key_ops: KeyOps::empty(),
|
||||||
key_use: None,
|
key_use: Some(KeyUse::Encryption),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -273,10 +298,9 @@ fn p256_private_to_pem() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
jwk.key.to_pem().unwrap(),
|
jwk.key.to_pem().unwrap(),
|
||||||
"-----BEGIN PRIVATE KEY-----
|
"-----BEGIN PRIVATE KEY-----
|
||||||
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgZoKQ9j4dhIBlMRVr
|
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZoKQ9j4dhIBlMRVr
|
||||||
v+QG8P/T9sutv3/95eio9MtpgKigCgYIKoZIzj0DAQehRANCAARA4wea/3q1WUm/
|
v+QG8P/T9sutv3/95eio9MtpgKihRANCAARA4wea/3q1WUm/642qmucNIoiPkCIt
|
||||||
642qmucNIoiPkCItNcpGiZdidq/Q3U42GaB53LWrRBOjQqypl0HSST5zc2RF/JwZ
|
NcpGiZdidq/Q3U42GaB53LWrRBOjQqypl0HSST5zc2RF/JwZmXXtwGOJ
|
||||||
mXXtwGOJ
|
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user