Skip to content

Commit

Permalink
Merge pull request #14 from alexrudy:deserialize-jwk
Browse files Browse the repository at this point in the history
Add DeserializeJWK support
  • Loading branch information
alexrudy authored Nov 29, 2023
2 parents e2ff65e + c82a9eb commit 7eb9cc0
Show file tree
Hide file tree
Showing 15 changed files with 654 additions and 308 deletions.
37 changes: 7 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ use jaws::JWTFormat;
// signing and verification status.
use jaws::Token;

use jaws::key::DeserializeJWK;
// The unverified token state, used like `Token<.., Unverified<..>, ..>`.
// It is generic over the type of the custom header parameters.
use jaws::token::Unverified;
Expand Down Expand Up @@ -157,15 +158,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// this will derive the JWK field in the header from the signing key.
token.header_mut().key().derived();

println!("=== {} ===", "Initial JWT");
println!("=== Initial JWT ===");

// Initially the JWT has no defined signature:
println!("{}", token.formatted());

// Sign the token with the algorithm, and print the result.
let signed = token.sign::<_, rsa::pkcs1v15::Signature>(&alg).unwrap();

println!("=== {} ===", "Signed JWT");
println!("=== Signed JWT ===");

println!("JWT:");
println!("{}", signed.formatted());
Expand All @@ -183,7 +184,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let token: Token<Claims<serde_json::Value>, Unverified<()>, Compact> =
signed.rendered().unwrap().parse().unwrap();

println!("=== {} ===", "Parsed JWT");
println!("=== Parsed JWT ===");

// Unverified tokens can be printed for debugging, but there is deliberately
// no access to the payload, only to the header fields.
Expand All @@ -193,10 +194,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// We can use the JWK to verify that the token is signed with the correct key.
let hdr = token.header();
let jwk = hdr.key().unwrap();
let key = rsa_jwk_reader::rsa_pub(&serde_json::to_value(jwk).unwrap());
let key = rsa::RsaPublicKey::from_jwk(jwk).unwrap();

assert_eq!(&key, alg.verifying_key().as_ref());
println!("=== {} === ", "Verification");
println!("=== Verification === ");

// let alg: rsa::pkcs1v15::VerifyingKey<Sha256> = rsa::pkcs1v15::VerifyingKey::new(key);
let alg: rsa::pkcs1v15::VerifyingKey<Sha256> = alg.verifying_key();
Expand All @@ -207,7 +208,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.verify::<_, jaws::algorithms::SignatureBytes>(&alg)
.unwrap();

println!("=== {} ===", "Verified JWT");
println!("=== Verified JWT ===");
println!("JWT:");
println!("{}", verified.formatted());
println!(
Expand All @@ -218,30 +219,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

mod rsa_jwk_reader {
use base64ct::Encoding;

fn strip_whitespace(s: &str) -> String {
s.chars().filter(|c| !c.is_whitespace()).collect()
}

fn to_biguint(v: &serde_json::Value) -> Option<rsa::BigUint> {
let val = strip_whitespace(v.as_str()?);
Some(rsa::BigUint::from_bytes_be(
base64ct::Base64UrlUnpadded::decode_vec(&val)
.ok()?
.as_slice(),
))
}

pub(crate) fn rsa_pub(key: &serde_json::Value) -> rsa::RsaPublicKey {
let n = to_biguint(&key["n"]).expect("decode n");
let e = to_biguint(&key["e"]).expect("decode e");

rsa::RsaPublicKey::new(n, e).expect("valid key parameters")
}
}

```

## Philosophy
Expand Down
38 changes: 9 additions & 29 deletions examples/dyn-key.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use jaws::algorithms::SignatureBytes;
use jaws::algorithms::TokenSigner;
use jaws::algorithms::TokenVerifier;
use jaws::key::DeserializeJWK;
use jaws::key::SerializeJWK;
use jaws::key::SerializePublicJWK;
use jaws::token::Unverified;
use jaws::Compact;
use jaws::JWTFormat;
Expand All @@ -11,9 +13,12 @@ use rsa::pkcs8::DecodePrivateKey;
use serde_json::json;
use sha2::Sha256;

trait TokenSigningKey: TokenSigner<SignatureBytes> + SerializeJWK {}
trait TokenSigningKey: TokenSigner<SignatureBytes> + SerializePublicJWK {}

impl<T> TokenSigningKey for T where T: TokenSigner<SignatureBytes> + SerializeJWK {}
impl<T> TokenSigningKey for T where
T: TokenSigner<SignatureBytes> + SerializeJWK + SerializePublicJWK
{
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
// This key is from RFC 7515, Appendix A.2. Provide your own key instead!
Expand Down Expand Up @@ -80,9 +85,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// We can use the JWK to verify that the token is signed with the correct key.
let hdr = token.header();
let jwk = hdr.key().unwrap();
let key: rsa::pkcs1v15::VerifyingKey<Sha256> = rsa::pkcs1v15::VerifyingKey::new(
rsa_jwk_reader::rsa_pub(&serde_json::to_value(jwk).unwrap()),
);
let key: rsa::pkcs1v15::VerifyingKey<Sha256> =
rsa::pkcs1v15::VerifyingKey::new(rsa::RsaPublicKey::from_jwk(&jwk).unwrap());

println!("=== Verification === ");
// Check it against the verified key
Expand Down Expand Up @@ -122,27 +126,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

Ok(())
}

mod rsa_jwk_reader {
use base64ct::Encoding;

fn strip_whitespace(s: &str) -> String {
s.chars().filter(|c| !c.is_whitespace()).collect()
}

fn to_biguint(v: &serde_json::Value) -> Option<rsa::BigUint> {
let val = strip_whitespace(v.as_str()?);
Some(rsa::BigUint::from_bytes_be(
base64ct::Base64UrlUnpadded::decode_vec(&val)
.ok()?
.as_slice(),
))
}

pub(crate) fn rsa_pub(key: &serde_json::Value) -> rsa::RsaPublicKey {
let n = to_biguint(&key["n"]).expect("decode n");
let e = to_biguint(&key["e"]).expect("decode e");

rsa::RsaPublicKey::new(n, e).expect("valid key parameters")
}
}
27 changes: 2 additions & 25 deletions examples/rfc7515a2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use jaws::JWTFormat;
// signing and verification status.
use jaws::Token;

use jaws::key::DeserializeJWK;
// The unverified token state, used like `Token<.., Unverified<..>, ..>`.
// It is generic over the type of the custom header parameters.
use jaws::token::Unverified;
Expand Down Expand Up @@ -110,7 +111,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// We can use the JWK to verify that the token is signed with the correct key.
let hdr = token.header();
let jwk = hdr.key().unwrap();
let key = rsa_jwk_reader::rsa_pub(&serde_json::to_value(jwk).unwrap());
let key = rsa::RsaPublicKey::from_jwk(jwk).unwrap();

assert_eq!(&key, alg.verifying_key().as_ref());
println!("=== Verification === ");
Expand All @@ -134,27 +135,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

Ok(())
}

mod rsa_jwk_reader {
use base64ct::Encoding;

fn strip_whitespace(s: &str) -> String {
s.chars().filter(|c| !c.is_whitespace()).collect()
}

fn to_biguint(v: &serde_json::Value) -> Option<rsa::BigUint> {
let val = strip_whitespace(v.as_str()?);
Some(rsa::BigUint::from_bytes_be(
base64ct::Base64UrlUnpadded::decode_vec(&val)
.ok()?
.as_slice(),
))
}

pub(crate) fn rsa_pub(key: &serde_json::Value) -> rsa::RsaPublicKey {
let n = to_biguint(&key["n"]).expect("decode n");
let e = to_biguint(&key["e"]).expect("decode e");

rsa::RsaPublicKey::new(n, e).expect("valid key parameters")
}
}
44 changes: 4 additions & 40 deletions examples/save-key.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,11 @@
use std::io::Write;

use jaws::key::DeserializeJWK as _;
use rsa::{pkcs1::EncodeRsaPublicKey, pkcs8::EncodePrivateKey};
use serde_json::json;

mod reader {
use base64ct::Encoding;
use rsa::traits::PrivateKeyParts;

fn strip_whitespace(s: &str) -> String {
s.chars().filter(|c| !c.is_whitespace()).collect()
}

fn to_biguint(v: &serde_json::Value) -> Option<rsa::BigUint> {
let val = strip_whitespace(v.as_str()?);
Some(rsa::BigUint::from_bytes_be(
base64ct::Base64UrlUnpadded::decode_vec(&val)
.ok()?
.as_slice(),
))
}

pub(crate) fn rsa(key: &serde_json::Value) -> rsa::RsaPrivateKey {
let primes = vec![
to_biguint(&key["p"]).expect("p"),
to_biguint(&key["q"]).expect("q"),
];

let pkey = rsa::RsaPrivateKey::from_components(
to_biguint(&key["n"]).expect("n"),
to_biguint(&key["e"]).expect("e"),
to_biguint(&key["d"]).expect("d"),
primes,
)
.unwrap();

assert_eq!(&to_biguint(&key["dp"]).expect("dp"), pkey.dp().unwrap());
assert_eq!(&to_biguint(&key["dq"]).expect("dq"), pkey.dq().unwrap());

pkey
}
}

fn main() {
let pkey = reader::rsa(&json!( {"kty":"RSA",
let pkey = rsa::RsaPrivateKey::from_value(json!( {"kty":"RSA",
"n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx
HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs
D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH
Expand Down Expand Up @@ -72,7 +35,8 @@ fn main() {
y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU
W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"
}
));
))
.unwrap();

let pemdata = pkey.to_pkcs8_pem(Default::default()).unwrap();

Expand Down
Loading

0 comments on commit 7eb9cc0

Please sign in to comment.