diff --git a/README.md b/README.md index ca53cae..bb3d9c5 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Complete examples are available in the examples directory: a basic one and one w In terms of imports and structs: ```rust use serde::{Serialize, Deserialize}; -use jsonwebtoken::{encode, decode, Header, Algorithm, Validation}; +use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey}; /// Our claims struct, it needs to derive `Serialize` and/or `Deserialize` #[derive(Debug, Serialize, Deserialize)] @@ -53,7 +53,7 @@ struct Claims { The default algorithm is HS256, which uses a shared secret. ```rust -let token = encode(&Header::default(), &my_claims, "secret".as_ref())?; +let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; ``` #### Custom headers & changing algorithm @@ -63,7 +63,7 @@ If you want to set the `kid` parameter or change the algorithm for example: ```rust let mut header = Header::new(Algorithm::HS512); header.kid = Some("blabla".to_owned()); -let token = encode(&header, &my_claims, "secret".as_ref())?; +let token = encode(&header, &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; ``` Look at `examples/custom_header.rs` for a full working example. @@ -71,7 +71,7 @@ Look at `examples/custom_header.rs` for a full working example. ```rust // HS256 -let token = encode(&Header::default(), &my_claims, "secret".as_ref())?; +let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; // RSA let token = encode(&Header::new(Algorithm::RS256), &my_claims, include_str!("privkey.pem"))?; ``` diff --git a/examples/custom_chrono.rs b/examples/custom_chrono.rs index e0b1185..facab56 100644 --- a/examples/custom_chrono.rs +++ b/examples/custom_chrono.rs @@ -1,5 +1,5 @@ use chrono::prelude::*; -use jsonwebtoken::{Header, Validation}; +use jsonwebtoken::{EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; const SECRET: &str = "some-secret"; @@ -51,8 +51,9 @@ mod jwt_numeric_date { let claims = Claims { sub: sub.clone(), iat, exp }; - let token = encode(&Header::default(), &claims, SECRET.as_ref()) - .expect("Failed to encode claims"); + let token = + encode(&Header::default(), &claims, &EncodingKey::from_secret(SECRET.as_ref())) + .expect("Failed to encode claims"); assert_eq!(&token, EXPECTED_TOKEN); @@ -82,7 +83,11 @@ fn main() -> Result<(), Box> { let claims = Claims { sub: sub.clone(), iat, exp }; - let token = jsonwebtoken::encode(&Header::default(), &claims, SECRET.as_ref())?; + let token = jsonwebtoken::encode( + &Header::default(), + &claims, + &EncodingKey::from_secret(SECRET.as_ref()), + )?; println!("serialized token: {}", &token); diff --git a/examples/custom_header.rs b/examples/custom_header.rs index f298c8d..adaf89c 100644 --- a/examples/custom_header.rs +++ b/examples/custom_header.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use jsonwebtoken::errors::ErrorKind; -use jsonwebtoken::{decode, encode, Algorithm, Header, Validation}; +use jsonwebtoken::{decode, encode, Algorithm, EncodingKey, Header, Validation}; #[derive(Debug, Serialize, Deserialize)] struct Claims { @@ -19,7 +19,7 @@ fn main() { header.kid = Some("signing_key".to_owned()); header.alg = Algorithm::HS512; - let token = match encode(&header, &my_claims, key) { + let token = match encode(&header, &my_claims, &EncodingKey::from_secret(key)) { Ok(t) => t, Err(_) => panic!(), // in practice you would return the error }; diff --git a/examples/validation.rs b/examples/validation.rs index 6c22d3f..88f9da3 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -1,5 +1,5 @@ use jsonwebtoken::errors::ErrorKind; -use jsonwebtoken::{decode, encode, Header, Validation}; +use jsonwebtoken::{decode, encode, EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] @@ -10,10 +10,10 @@ struct Claims { } fn main() { + let key = b"secret"; let my_claims = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned(), exp: 10000000000 }; - let key = b"secret"; - let token = match encode(&Header::default(), &my_claims, key) { + let token = match encode(&Header::default(), &my_claims, &EncodingKey::from_secret(key)) { Ok(t) => t, Err(_) => panic!(), // in practice you would return the error }; diff --git a/src/crypto/ecdsa.rs b/src/crypto/ecdsa.rs index 25099a7..afa0f79 100644 --- a/src/crypto/ecdsa.rs +++ b/src/crypto/ecdsa.rs @@ -2,7 +2,6 @@ use ring::{rand, signature}; use crate::algorithms::Algorithm; use crate::errors::Result; -use crate::pem::decoder::PemEncodedKey; use crate::serialization::b64_encode; /// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs. @@ -26,13 +25,13 @@ pub(crate) fn alg_to_ec_signing(alg: Algorithm) -> &'static signature::EcdsaSign } /// The actual ECDSA signing + encoding +/// The key needs to be in PKCS8 format pub fn sign( alg: &'static signature::EcdsaSigningAlgorithm, key: &[u8], message: &str, ) -> Result { - let pem_key = PemEncodedKey::new(key)?; - let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, pem_key.as_ec_private_key()?)?; + let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, key)?; let rng = rand::SystemRandom::new(); let out = signing_key.sign(&rng, message.as_bytes())?; Ok(b64_encode(out.as_ref())) diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 98dc440..7390286 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -2,6 +2,7 @@ use ring::constant_time::verify_slices_are_equal; use ring::{hmac, signature}; use crate::algorithms::Algorithm; +use crate::encoding::EncodingKey; use crate::errors::Result; use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_decode, b64_encode}; @@ -20,16 +21,14 @@ pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &str) -> Resu /// the base64 url safe encoded of the result. /// /// If you just want to encode a JWT, use `encode` instead. -/// -/// `key` is the secret for HMAC and a pem encoded string otherwise -pub fn sign(message: &str, key: &[u8], algorithm: Algorithm) -> Result { +pub fn sign(message: &str, key: &EncodingKey, algorithm: Algorithm) -> Result { match algorithm { - Algorithm::HS256 => sign_hmac(hmac::HMAC_SHA256, key, message), - Algorithm::HS384 => sign_hmac(hmac::HMAC_SHA384, key, message), - Algorithm::HS512 => sign_hmac(hmac::HMAC_SHA512, key, message), + Algorithm::HS256 => sign_hmac(hmac::HMAC_SHA256, key.inner(), message), + Algorithm::HS384 => sign_hmac(hmac::HMAC_SHA384, key.inner(), message), + Algorithm::HS512 => sign_hmac(hmac::HMAC_SHA512, key.inner(), message), Algorithm::ES256 | Algorithm::ES384 => { - ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key, message) + ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key.inner(), message) } Algorithm::RS256 @@ -37,7 +36,7 @@ pub fn sign(message: &str, key: &[u8], algorithm: Algorithm) -> Result { | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 - | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key, message), + | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key.inner(), message), } } @@ -69,7 +68,7 @@ pub fn verify(signature: &str, message: &str, key: &[u8], algorithm: Algorithm) match algorithm { Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { // we just re-sign the message with the key and compare if they are equal - let signed = sign(message, key, algorithm)?; + let signed = sign(message, &EncodingKey::from_secret(key), algorithm)?; Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok()) } Algorithm::ES256 | Algorithm::ES384 => { diff --git a/src/crypto/rsa.rs b/src/crypto/rsa.rs index 89da359..ec69da5 100644 --- a/src/crypto/rsa.rs +++ b/src/crypto/rsa.rs @@ -3,7 +3,6 @@ use simple_asn1::BigUint; use crate::algorithms::Algorithm; use crate::errors::{ErrorKind, Result}; -use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_decode, b64_encode}; /// Only used internally when validating RSA, to map from our enum to the Ring param structs. @@ -33,15 +32,14 @@ pub(crate) fn alg_to_rsa_signing(alg: Algorithm) -> &'static dyn signature::RsaE } /// The actual RSA signing + encoding +/// The key needs to be in PKCS8 format /// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html pub(crate) fn sign( alg: &'static dyn signature::RsaEncoding, key: &[u8], message: &str, ) -> Result { - let pem_key = PemEncodedKey::new(key)?; - let key_pair = signature::RsaKeyPair::from_der(pem_key.as_rsa_key()?) - .map_err(|_| ErrorKind::InvalidRsaKey)?; + let key_pair = signature::RsaKeyPair::from_der(key).map_err(|_| ErrorKind::InvalidRsaKey)?; let mut signature = vec![0; key_pair.public_modulus_len()]; let rng = rand::SystemRandom::new(); diff --git a/src/encoding.rs b/src/encoding.rs new file mode 100644 index 0000000..8df4776 --- /dev/null +++ b/src/encoding.rs @@ -0,0 +1,79 @@ +use std::borrow::Cow; + +use serde::ser::Serialize; + +use crate::crypto; +use crate::errors::Result; +use crate::header::Header; +use crate::pem::decoder::PemEncodedKey; +use crate::serialization::b64_encode_part; + +/// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. +#[derive(Debug, Clone, PartialEq)] +pub struct EncodingKey<'a> { + content: Cow<'a, [u8]>, +} + +impl<'a> EncodingKey<'a> { + /// If you're using HMAC, use that. + pub fn from_secret(secret: &'a [u8]) -> Self { + EncodingKey { content: Cow::Borrowed(secret) } + } + + /// If you are loading a RSA key from a .pem file + /// This errors if the key is not a valid RSA key + pub fn from_rsa_pem(key: &'a [u8]) -> Result { + let pem_key = PemEncodedKey::new(key)?; + let content = pem_key.as_rsa_key()?; + Ok(EncodingKey { content: Cow::Owned(content.to_vec()) }) + } + + /// If you are loading a ECDSA key from a .pem file + /// This errors if the key is not a valid private EC key + pub fn from_ec_pem(key: &'a [u8]) -> Result { + let pem_key = PemEncodedKey::new(key)?; + let content = pem_key.as_ec_private_key()?; + Ok(EncodingKey { content: Cow::Owned(content.to_vec()) }) + } + + /// If you know what you're doing and have the DER-encoded key, for RSA or ECDSA + pub fn from_der(der: &'a [u8]) -> Self { + EncodingKey { content: Cow::Borrowed(der) } + } + + /// Access the key, normal users do not need to use that. + pub fn inner(&'a self) -> &'a [u8] { + &self.content + } +} + +/// Encode the header and claims given and sign the payload using the algorithm from the header and the key. +/// If the algorithm given is RSA or EC, the key needs to be in the PEM format. +/// +/// ```rust +/// use serde::{Deserialize, Serialize}; +/// use jsonwebtoken::{encode, Algorithm, Header, EncodingKey}; +/// +/// #[derive(Debug, Serialize, Deserialize)] +/// struct Claims { +/// sub: String, +/// company: String +/// } +/// +/// let my_claims = Claims { +/// sub: "b@b.com".to_owned(), +/// company: "ACME".to_owned() +/// }; +/// +/// // my_claims is a struct that implements Serialize +/// // This will create a JWT using HS256 as algorithm +/// let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref())).unwrap(); +/// ``` +pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> Result { + let encoded_header = b64_encode_part(&header)?; + let encoded_claims = b64_encode_part(&claims)?; + let message = [encoded_header.as_ref(), encoded_claims.as_ref()].join("."); + let signature = crypto::sign(&*message, key, header.alg)?; + + Ok([message, signature].join(".")) +} diff --git a/src/lib.rs b/src/lib.rs index 77d34ca..7b3d7b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ mod algorithms; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; mod decoding; +mod encoding; /// All the errors that can be encountered while encoding/decoding JWTs pub mod errors; mod header; @@ -18,41 +19,6 @@ pub use algorithms::Algorithm; pub use decoding::{ dangerous_unsafe_decode, decode, decode_header, decode_rsa_components, TokenData, }; +pub use encoding::{encode, EncodingKey}; pub use header::Header; pub use validation::Validation; - -use serde::ser::Serialize; - -use crate::errors::Result; -use crate::serialization::b64_encode_part; - -/// Encode the header and claims given and sign the payload using the algorithm from the header and the key. -/// If the algorithm given is RSA or EC, the key needs to be in the PEM format. -/// -/// ```rust -/// use serde::{Deserialize, Serialize}; -/// use jsonwebtoken::{encode, Algorithm, Header}; -/// -/// #[derive(Debug, Serialize, Deserialize)] -/// struct Claims { -/// sub: String, -/// company: String -/// } -/// -/// let my_claims = Claims { -/// sub: "b@b.com".to_owned(), -/// company: "ACME".to_owned() -/// }; -/// -/// // my_claims is a struct that implements Serialize -/// // This will create a JWT using HS256 as algorithm -/// let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap(); -/// ``` -pub fn encode(header: &Header, claims: &T, key: &[u8]) -> Result { - let encoded_header = b64_encode_part(&header)?; - let encoded_claims = b64_encode_part(&claims)?; - let message = [encoded_header.as_ref(), encoded_claims.as_ref()].join("."); - let signature = crypto::sign(&*message, key, header.alg)?; - - Ok([message, signature].join(".")) -} diff --git a/src/validation.rs b/src/validation.rs index b8106ef..b6805aa 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -159,7 +159,7 @@ pub fn validate(claims: &Map, options: &Validation) -> Result<()> return Err(new_error(ErrorKind::InvalidAudience)); } } - _ => return Err(new_error(ErrorKind::InvalidAudience)) + _ => return Err(new_error(ErrorKind::InvalidAudience)), }; } else { return Err(new_error(ErrorKind::InvalidAudience)); @@ -447,17 +447,17 @@ mod tests { #[test] fn aud_use_validation_struct() { let mut claims = Map::new(); - claims.insert("aud".to_string(), to_value("my-googleclientid1234.apps.googleusercontent.com").unwrap()); + claims.insert( + "aud".to_string(), + to_value("my-googleclientid1234.apps.googleusercontent.com").unwrap(), + ); let aud = "my-googleclientid1234.apps.googleusercontent.com".to_string(); let mut aud_hashset = std::collections::HashSet::new(); aud_hashset.insert(aud); - let validation = Validation { - aud: Some(aud_hashset), - validate_exp: false, - ..Validation::default() - }; + let validation = + Validation { aud: Some(aud_hashset), validate_exp: false, ..Validation::default() }; let res = validate(&claims, &validation); println!("{:?}", res); assert!(res.is_ok()); diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index d90242e..2ccec50 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -1,7 +1,7 @@ use chrono::Utc; use jsonwebtoken::{ crypto::{sign, verify}, - decode, encode, Algorithm, Header, Validation, + decode, encode, Algorithm, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; @@ -26,7 +26,8 @@ pub struct Claims { fn round_trip_sign_verification_pem() { let privkey = include_bytes!("private_ecdsa_key.pem"); let pubkey = include_bytes!("public_ecdsa_key.pem"); - let encrypted = sign("hello world", privkey, Algorithm::ES256).unwrap(); + let encrypted = + sign("hello world", &EncodingKey::from_ec_pem(privkey).unwrap(), Algorithm::ES256).unwrap(); let is_valid = verify(&encrypted, "hello world", pubkey, Algorithm::ES256).unwrap(); assert!(is_valid); } @@ -40,7 +41,12 @@ fn round_trip_claim() { company: "ACME".to_string(), exp: Utc::now().timestamp() + 10000, }; - let token = encode(&Header::new(Algorithm::ES256), &my_claims, privkey).unwrap(); + let token = encode( + &Header::new(Algorithm::ES256), + &my_claims, + &EncodingKey::from_ec_pem(privkey).unwrap(), + ) + .unwrap(); let token_data = decode::(&token, pubkey, &Validation::new(Algorithm::ES256)).unwrap(); assert_eq!(my_claims, token_data.claims); } @@ -56,7 +62,12 @@ fn roundtrip_with_jwtio_example() { company: "ACME".to_string(), exp: Utc::now().timestamp() + 10000, }; - let token = encode(&Header::new(Algorithm::ES384), &my_claims, privkey).unwrap(); + let token = encode( + &Header::new(Algorithm::ES384), + &my_claims, + &EncodingKey::from_ec_pem(privkey).unwrap(), + ) + .unwrap(); let token_data = decode::(&token, pubkey, &Validation::new(Algorithm::ES384)).unwrap(); assert_eq!(my_claims, token_data.claims); } diff --git a/tests/hmac.rs b/tests/hmac.rs index 98e2d56..d77f8bc 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -1,7 +1,8 @@ use chrono::Utc; use jsonwebtoken::{ crypto::{sign, verify}, - dangerous_unsafe_decode, decode, decode_header, encode, Algorithm, Header, Validation, + dangerous_unsafe_decode, decode, decode_header, encode, Algorithm, EncodingKey, Header, + Validation, }; use serde::{Deserialize, Serialize}; @@ -14,7 +15,8 @@ pub struct Claims { #[test] fn sign_hs256() { - let result = sign("hello world", b"secret", Algorithm::HS256).unwrap(); + let result = + sign("hello world", &EncodingKey::from_secret(b"secret"), Algorithm::HS256).unwrap(); let expected = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; assert_eq!(result, expected); } @@ -35,7 +37,7 @@ fn encode_with_custom_header() { }; let mut header = Header::default(); header.kid = Some("kid".to_string()); - let token = encode(&header, &my_claims, b"secret").unwrap(); + let token = encode(&header, &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); let token_data = decode::(&token, b"secret", &Validation::default()).unwrap(); assert_eq!(my_claims, token_data.claims); assert_eq!("kid", token_data.header.kid.unwrap()); @@ -48,7 +50,8 @@ fn round_trip_claim() { company: "ACME".to_string(), exp: Utc::now().timestamp() + 10000, }; - let token = encode(&Header::default(), &my_claims, b"secret").unwrap(); + let token = + encode(&Header::default(), &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); let token_data = decode::(&token, b"secret", &Validation::default()).unwrap(); assert_eq!(my_claims, token_data.claims); assert!(token_data.header.kid.is_none()); diff --git a/tests/rsa/mod.rs b/tests/rsa/mod.rs index 9b3301e..814c645 100644 --- a/tests/rsa/mod.rs +++ b/tests/rsa/mod.rs @@ -1,7 +1,7 @@ use chrono::Utc; use jsonwebtoken::{ crypto::{sign, verify}, - decode, decode_rsa_components, encode, Algorithm, Header, Validation, + decode, decode_rsa_components, encode, Algorithm, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; @@ -27,7 +27,8 @@ fn round_trip_sign_verification_pem_pkcs1() { let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); for &alg in RSA_ALGORITHMS { - let encrypted = sign("hello world", privkey_pem, alg).unwrap(); + let encrypted = + sign("hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); let is_valid = verify(&encrypted, "hello world", pubkey_pem, alg).unwrap(); assert!(is_valid); } @@ -39,7 +40,8 @@ fn round_trip_sign_verification_pem_pkcs8() { let pubkey_pem = include_bytes!("public_rsa_key_pkcs8.pem"); for &alg in RSA_ALGORITHMS { - let encrypted = sign("hello world", privkey_pem, alg).unwrap(); + let encrypted = + sign("hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); let is_valid = verify(&encrypted, "hello world", pubkey_pem, alg).unwrap(); assert!(is_valid); } @@ -55,7 +57,9 @@ fn round_trip_claim() { let privkey = include_bytes!("private_rsa_key_pkcs1.pem"); for &alg in RSA_ALGORITHMS { - let token = encode(&Header::new(alg), &my_claims, privkey).unwrap(); + let token = + encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey).unwrap()) + .unwrap(); let token_data = decode::( &token, include_bytes!("public_rsa_key_pkcs1.pem"), @@ -78,18 +82,16 @@ fn rsa_modulus_exponent() { let n = "yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ"; let e = "AQAB"; - let encrypted = encode(&Header::new(Algorithm::RS256), &my_claims, privkey.as_ref()).unwrap(); + let encrypted = encode( + &Header::new(Algorithm::RS256), + &my_claims, + &EncodingKey::from_rsa_pem(privkey.as_ref()).unwrap(), + ) + .unwrap(); let res = decode_rsa_components::(&encrypted, n, e, &Validation::new(Algorithm::RS256)); assert!(res.is_ok()); } -#[test] -#[should_panic(expected = "InvalidKeyFormat")] -fn fails_with_non_pkcs8_key_format() { - let _encrypted = - sign("hello world", include_bytes!("private_rsa_key_pkcs1.pem"), Algorithm::ES256).unwrap(); -} - // https://jwt.io/ is often used for examples so ensure their example works with jsonwebtoken #[test] fn roundtrip_with_jwtio_example_jey() { @@ -103,7 +105,9 @@ fn roundtrip_with_jwtio_example_jey() { }; for &alg in RSA_ALGORITHMS { - let token = encode(&Header::new(alg), &my_claims, privkey_pem).unwrap(); + let token = + encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey_pem).unwrap()) + .unwrap(); let token_data = decode::(&token, pubkey_pem, &Validation::new(alg)).unwrap(); assert_eq!(my_claims, token_data.claims); }