completely rewrited

This commit is contained in:
OKAMURA, Yasunobu 2021-01-10 00:34:31 +09:00
parent 2e633283aa
commit 4d5fd2b852
16 changed files with 840 additions and 632 deletions

View file

@ -11,15 +11,21 @@ repository = "https://github.com/informationsea/jsonwebkey-rs"
keywords = ["jsonwebkey", "jsonwebtoken"]
categories = ["authentication"]
[features]
default = []
full = ["jsonwebtoken", "pem_support"]
pem_support = ["simple_asn1", "pem"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pem = "0.8"
simple_asn1 = "0.4"
num-bigint = "0.2"
pem = { version = "0.8", optional = true }
jsonwebtoken = { version = "^7.2", optional = true }
simple_asn1 = { version = "^0.5.1", optional = true }
num-bigint = "^0.3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
base64-url = "1"
base64 = "^0.13"
lazy_static = "1"
anyhow = "1"
thiserror = "1"
thiserror = "1"

View file

@ -0,0 +1,170 @@
use super::*;
use lazy_static::lazy_static;
use num_bigint::ToBigInt;
use simple_asn1::{oid, ASN1Block, BigUint, OID};
lazy_static! {
static ref RSA_OID: simple_asn1::OID = oid!(1, 2, 840, 113549, 1, 1, 1);
}
pub trait ToPem {
fn to_der(&self) -> Result<Vec<u8>, Error>;
fn to_pem(&self) -> Result<String, Error> {
let der = self.to_der()?;
let pem = pem::Pem {
tag: "PUBLIC KEY".to_string(),
contents: der,
};
Ok(pem::encode(&pem))
}
}
impl ToPem for RSAPublicKey {
fn to_der(&self) -> Result<Vec<u8>, Error> {
let pubkey_asn1 = ASN1Block::Sequence(
0,
vec![
ASN1Block::Integer(0, self.n.big_uint.to_bigint().unwrap()),
ASN1Block::Integer(0, self.e.big_uint.to_bigint().unwrap()),
],
);
let pubkey_der = simple_asn1::to_der(&pubkey_asn1)?;
let asn1 = ASN1Block::Sequence(
0,
vec![
ASN1Block::Sequence(
0,
vec![
ASN1Block::ObjectIdentifier(0, RSA_OID.clone()),
ASN1Block::Null(0),
],
),
ASN1Block::BitString(0, pubkey_der.len() * 8, pubkey_der),
],
);
Ok(simple_asn1::to_der(&asn1)?)
}
}
pub trait FromPem: Sized {
fn from_pem<T: AsRef<[u8]>>(pem: T) -> Result<Self, Error> {
let data = pem::parse(pem)?;
Self::from_der(&data.contents)
}
fn from_der(der: &[u8]) -> Result<Self, Error>;
}
impl FromPem for RSAPublicKey {
fn from_der(der: &[u8]) -> Result<Self, Error> {
let ans1_block_vec = simple_asn1::from_der(der)?;
if ans1_block_vec.len() != 1 {
return Err(Error::PubKeyParse(
"Invalid number of sequence: length of 1ans1_block_vec is not 1",
));
}
let ans1_seq = if let ASN1Block::Sequence(_, d) = &ans1_block_vec[0] {
d
} else {
return Err(Error::PubKeyParse("Invalid format: 2"));
};
if ans1_seq.len() != 2 {
return Err(Error::PubKeyParse("Invalid number of sequence: 3"));
}
//println!("pubkey der: {:?}", ans1_seq);
let oid_seq = if let ASN1Block::Sequence(_, s) = &ans1_seq[0] {
s
} else {
return Err(Error::PubKeyParse("Invalid format: 3"));
};
let oid = if let ASN1Block::ObjectIdentifier(_, o) = &oid_seq[0] {
o
} else {
return Err(Error::PubKeyParse("Invalid format: 4"));
};
if oid != *RSA_OID {
return Err(Error::PubKeyParse("Invalid format: 5"));
}
let bit_string = if let ASN1Block::BitString(_, _, s) = &ans1_seq[1] {
s
} else {
return Err(Error::PubKeyParse("Invalid format: 6"));
};
let parsed_der = simple_asn1::from_der(&bit_string).map_err(Error::ANS1DecodeError)?;
if parsed_der.len() != 1 {
return Err(Error::PubKeyParse("Invalid format: 7"));
}
let pubkey_seq = if let ASN1Block::Sequence(_, s) = &parsed_der[0] {
s
} else {
return Err(Error::PubKeyParse("Invalid format: 8"));
};
if pubkey_seq.len() != 2 {
return Err(Error::PubKeyParse("Invalid format: 9"));
}
let n = if let ASN1Block::Integer(_, n) = &pubkey_seq[0] {
n
} else {
return Err(Error::PubKeyParse("Invalid format: 10"));
};
let e = if let ASN1Block::Integer(_, e) = &pubkey_seq[1] {
e
} else {
return Err(Error::PubKeyParse("Invalid format: 11"));
};
let rsa_pub_key = RSAPublicKey {
generic: Generic {
kty: KeyType::Rsa,
use_: None,
key_ops: None,
alg: None,
kid: None,
x5u: None,
x5c: None,
x5t: None,
x5t_s256: None,
},
e: e.to_biguint().unwrap().into(),
n: n.to_biguint().unwrap().into(),
};
Ok(rsa_pub_key)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_convert_to_pem1() {
let jwk: RSAPublicKey =
serde_json::from_str(include_str!("../testfiles/test1.json")).unwrap();
let pem = jwk.to_pem().unwrap();
let pem_expected = include_str!("../testfiles/test1.pem");
assert_eq!(pem, pem_expected);
}
#[test]
fn test_convert_to_pem2() {
let jwk: RSAPublicKey =
serde_json::from_str(include_str!("../testfiles/test2.json")).unwrap();
let pem = jwk.to_pem().unwrap();
let pem_expected = include_str!("../testfiles/test2.pem");
assert_eq!(pem, pem_expected);
}
#[test]
fn test_convert_from_pem1() {
let pem = include_str!("../testfiles/test1.pem");
let mut jwk = RSAPublicKey::from_pem(pem).unwrap();
jwk.generic.kid = Some("fe41cf0f-7901-489f-9d4e-1437d6c1aa1f".to_string());
let jwk_expected: RSAPublicKey =
serde_json::from_str(include_str!("../testfiles/test1.json")).unwrap();
assert_eq!(jwk, jwk_expected);
}
}

View file

@ -0,0 +1,12 @@
use super::*;
use jsonwebtoken::DecodingKey;
pub trait ToDecodingKey {
fn to_decoding_key(&'_ self) -> DecodingKey<'_>;
}
impl ToDecodingKey for RSAPublicKey {
fn to_decoding_key(&'_ self) -> DecodingKey<'_> {
DecodingKey::from_rsa_components(&self.n.base64, &self.e.base64)
}
}

View file

@ -1,351 +1,495 @@
//! # jsonwebkey-convert
//! Convert an RSA public key between Json Web Key and DER/PEM format.
//! Handle Json Web Key without nightly rust compiler.
//!
//! ## Load JSON Web Key Set
//!
//! ```
//! use jsonwebkey_convert::JsonWebKeySet;
//! # use jsonwebkey_convert::Error;
//!
//! # fn main() -> Result<(), Error> {
//! # let jwks_str = include_str!("../testfiles/example-public-key.json");
//! let jwks: JsonWebKeySet = jwks_str.parse()?;
//! # Ok(())
//! # }
//! ```
//!
//!
//! ## Convert PEM to JWK
//! `pem_support` feature is required.
//!
//! ```
//! use jsonwebkey_convert::*;
//! use jsonwebkey_convert::der::FromPem;
//!
//! # fn main() -> Result<(), JWKConvertError> {
//! # let pem_data = include_bytes!("../testfiles/test1.pem");
//! let pem_rsa = load_pem(&pem_data[..])?;
//! let jwk_data = RSAJWK {
//! kid: Some("3f5fbba0-06c4-467c-8d5e-e935a71437b0".to_string()),
//! jwk_use: Some("sig".to_string()),
//! pubkey: pem_rsa
//! };
//! let jwk_byte_vec = jwk_data.to_jwk()?;
//! # fn main() -> Result<(), Error> {
//! # let pem_data = include_str!("../testfiles/test1.pem");
//! let rsa_jwk = RSAPublicKey::from_pem(pem_data)?;
//! let jwk_byte_vec = serde_json::to_string(&rsa_jwk);
//! # Ok(())
//! # }
//! ```
//!
//! ## Convert JWK to PEM
//! `pem_support` feature is required.
//!
//! ```
//! use jsonwebkey_convert::*;
//! use jsonwebkey_convert::der::ToPem;
//!
//! # fn main() -> Result<(), JWKConvertError> {
//! # let jwk_byte_vec = include_bytes!("../testfiles/test1.json");
//! let jwk_data = load_jwk(&jwk_byte_vec[..])?;
//! let rsa_pubkey = jwk_data.pubkey;
//! let pem_string = rsa_pubkey.to_pem()?;
//! # fn main() -> Result<(), Error> {
//! # let jwk_data = include_str!("../testfiles/test1.json");
//! let rsa_jwk: RSAPublicKey = jwk_data.parse()?;
//! let pem_data = rsa_jwk.to_pem()?;
//! # Ok(())
//! # }
//! ```
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use simple_asn1::{oid, ASN1Block, BigInt, BigUint, OID};
use std::convert::TryInto;
/// DER and PEM format support
#[cfg(feature = "simple_asn1")]
pub mod der;
/// Json Web Token support
#[cfg(feature = "jsonwebtoken")]
pub mod jwt;
use num_bigint::BigUint;
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum JWKConvertError {
#[error("JWK Parse Error: {0}")]
JWKParseError(&'static str),
pub enum Error {
#[error("Public Key Parse Error: {0}")]
PubKeyParse(&'static str),
#[cfg(feature = "simple_asn1")]
#[error(transparent)]
ANS1DecodeError(#[from] simple_asn1::ASN1DecodeErr),
#[cfg(feature = "simple_asn1")]
#[error(transparent)]
ANS1EncodeError(#[from] simple_asn1::ASN1EncodeErr),
#[error(transparent)]
#[cfg(feature = "pem")]
PEMParseError(#[from] pem::PemError),
#[error(transparent)]
Base64UrlError(#[from] base64_url::base64::DecodeError),
Base64UrlError(#[from] base64::DecodeError),
#[error(transparent)]
JSONParseError(#[from] serde_json::Error),
}
lazy_static! {
static ref RSA_OID: simple_asn1::OID = oid!(1, 2, 840, 113549, 1, 1, 1);
/// A set of Json Web Keys
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonWebKeySet {
pub keys: Vec<JsonWebKey>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct RSAPubKeyJWK {
#[serde(skip_serializing_if = "Option::is_none")]
kid: Option<String>,
kty: String,
#[serde(rename = "use")]
#[serde(skip_serializing_if = "Option::is_none")]
use_: Option<String>,
n: String,
e: String,
}
/// RSA Public Key with kid and use
#[derive(Debug, PartialEq)]
pub struct RSAJWK {
pub kid: Option<String>,
pub jwk_use: Option<String>,
pub pubkey: RSAPubKey,
}
impl Into<RSAPubKey> for RSAJWK {
fn into(self) -> RSAPubKey {
self.pubkey
impl JsonWebKeySet {
pub fn from_bytes(data: &[u8]) -> Result<JsonWebKeySet, Error> {
Ok(serde_json::from_slice(data)?)
}
}
/// RSA Public Key
#[derive(Debug, PartialEq)]
pub struct RSAPubKey {
pub n: BigInt,
pub e: BigInt,
impl FromStr for JsonWebKeySet {
type Err = Error;
fn from_str(data: &str) -> Result<JsonWebKeySet, Error> {
Ok(serde_json::from_str(data)?)
}
}
impl Into<RSAJWK> for RSAPubKey {
fn into(self) -> RSAJWK {
RSAJWK {
kid: None,
jwk_use: None,
pubkey: self,
/// A JSON Web Key
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonWebKey {
ECPrivateKey {
#[serde(flatten)]
value: ECPrivateKey,
},
ECPublicKey {
#[serde(flatten)]
value: ECPublicKey,
},
RSAPrivateKey {
#[serde(flatten)]
value: Box<RSAPrivateKey>,
},
RSAPublicKey {
#[serde(flatten)]
value: RSAPublicKey,
},
SymmetricKey {
#[serde(flatten)]
value: SymmetricKey,
},
}
impl FromStr for JsonWebKey {
type Err = Error;
fn from_str(data: &str) -> Result<JsonWebKey, Error> {
Ok(serde_json::from_str(data)?)
}
}
impl JsonWebKey {
pub fn from_bytes(data: &[u8]) -> Result<JsonWebKey, Error> {
Ok(serde_json::from_slice(data)?)
}
pub fn ec_private_key(&self) -> Option<&ECPrivateKey> {
match self {
JsonWebKey::ECPrivateKey { value } => Some(value),
_ => None,
}
}
pub fn ec_public_key(&self) -> Option<&ECPublicKey> {
match self {
JsonWebKey::ECPublicKey { value } => Some(value),
_ => None,
}
}
pub fn rsa_private_key(&self) -> Option<&RSAPrivateKey> {
match self {
JsonWebKey::RSAPrivateKey { value } => Some(value),
_ => None,
}
}
pub fn rsa_public_key(&self) -> Option<&RSAPublicKey> {
match self {
JsonWebKey::RSAPublicKey { value } => Some(value),
_ => None,
}
}
pub fn symmetric_key(&self) -> Option<&SymmetricKey> {
match self {
JsonWebKey::SymmetricKey { value } => Some(value),
_ => None,
}
}
}
impl TryInto<RSAJWK> for RSAPubKeyJWK {
type Error = JWKConvertError;
fn try_into(self) -> Result<RSAJWK, Self::Error> {
if self.kty != "RSA" {
return Err(JWKConvertError::JWKParseError("Unspported type"));
}
let n = base64_url::decode(&self.n)?;
let e = base64_url::decode(&self.e)?;
Ok(RSAJWK {
kid: self.kid,
jwk_use: self.use_,
pubkey: RSAPubKey {
n: BigInt::from_bytes_be(num_bigint::Sign::Plus, &n),
e: BigInt::from_bytes_be(num_bigint::Sign::Plus, &e),
},
#[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)]
pub enum ECCurveParameter {
#[serde(rename = "P-256")]
P256,
#[serde(rename = "P-384")]
P384,
#[serde(rename = "P-521")]
P521,
}
/// BASE64 encoded big integer
#[derive(Debug, Clone, PartialEq)]
pub struct Base64BigUint {
big_uint: BigUint,
base64: String,
}
impl Base64BigUint {
pub fn to_base64url(&self) -> String {
base64::encode_config(&self.big_uint.to_bytes_be(), base64::URL_SAFE_NO_PAD)
}
pub fn from_base64url(value: &str) -> Result<Self, Error> {
Ok(Base64BigUint {
big_uint: BigUint::from_bytes_be(&base64::decode_config(
value,
base64::URL_SAFE_NO_PAD,
)?),
base64: value.to_string(),
})
}
}
impl RSAJWK {
pub fn to_jwk(&self) -> Result<String, JWKConvertError> {
let jwk = RSAPubKeyJWK {
kid: self.kid.clone(),
kty: "RSA".to_string(),
use_: self.jwk_use.clone(),
n: base64_url::encode(&self.pubkey.n.to_bytes_be().1),
e: base64_url::encode(&self.pubkey.e.to_bytes_be().1),
};
serde_json::to_string(&jwk).map_err(JWKConvertError::JSONParseError)
impl Into<BigUint> for Base64BigUint {
fn into(self) -> BigUint {
self.big_uint
}
}
impl RSAPubKey {
pub fn to_der(&self) -> Result<Vec<u8>, JWKConvertError> {
let pubkey_asn1 = ASN1Block::Sequence(
0,
vec![
ASN1Block::Integer(0, self.n.clone()),
ASN1Block::Integer(0, self.e.clone()),
],
);
let pubkey_der = simple_asn1::to_der(&pubkey_asn1)?;
let asn1 = ASN1Block::Sequence(
0,
vec![
ASN1Block::Sequence(
0,
vec![
ASN1Block::ObjectIdentifier(0, RSA_OID.clone()),
ASN1Block::Null(0),
],
),
ASN1Block::BitString(0, pubkey_der.len() * 8, pubkey_der),
],
);
Ok(simple_asn1::to_der(&asn1)?)
}
pub fn to_pem(&self) -> Result<String, JWKConvertError> {
let der = self.to_der()?;
let pem = pem::Pem {
tag: "PUBLIC KEY".to_string(),
contents: der,
};
Ok(pem::encode(&pem))
impl Into<Base64BigUint> for BigUint {
fn into(self) -> Base64BigUint {
Base64BigUint {
base64: base64::encode_config(self.to_bytes_be(), base64::URL_SAFE_NO_PAD),
big_uint: self,
}
}
}
/// Load a Json Web Key from bytes slice
pub fn load_jwk(data: &[u8]) -> Result<RSAJWK, JWKConvertError> {
let jwk: RSAPubKeyJWK = serde_json::from_slice(data)?;
Ok(jwk.try_into()?)
impl Serialize for Base64BigUint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_base64url().serialize(serializer)
}
}
/// Load an RSA public key from DER format
pub fn load_der(data: &[u8]) -> Result<RSAPubKey, JWKConvertError> {
let ans1_block_vec = simple_asn1::from_der(data)?;
if ans1_block_vec.len() != 1 {
return Err(JWKConvertError::PubKeyParse(
"Invalid number of sequence: 1",
));
impl<'de> Deserialize<'de> for Base64BigUint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Ok(Base64BigUint::from_base64url(&value).map_err(|e| {
D::Error::custom(format!("failed to decode BASE64 URL: {} : {}", value, e))
})?)
}
let ans1_seq = if let ASN1Block::Sequence(_, d) = &ans1_block_vec[0] {
d
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 2"));
};
if ans1_seq.len() != 2 {
return Err(JWKConvertError::PubKeyParse(
"Invalid number of sequence: 3",
));
}
//println!("pubkey der: {:?}", ans1_seq);
let oid_seq = if let ASN1Block::Sequence(_, s) = &ans1_seq[0] {
s
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 3"));
};
let oid = if let ASN1Block::ObjectIdentifier(_, o) = &oid_seq[0] {
o
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 4"));
};
if oid != *RSA_OID {
return Err(JWKConvertError::PubKeyParse("Invalid format: 5"));
}
let bit_string = if let ASN1Block::BitString(_, _, s) = &ans1_seq[1] {
s
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 6"));
};
let parsed_der =
simple_asn1::from_der(&bit_string).map_err(JWKConvertError::ANS1DecodeError)?;
if parsed_der.len() != 1 {
return Err(JWKConvertError::PubKeyParse("Invalid format: 7"));
}
let pubkey_seq = if let ASN1Block::Sequence(_, s) = &parsed_der[0] {
s
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 8"));
};
if pubkey_seq.len() != 2 {
return Err(JWKConvertError::PubKeyParse("Invalid format: 9"));
}
let n = if let ASN1Block::Integer(_, n) = &pubkey_seq[0] {
n
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 10"));
};
let e = if let ASN1Block::Integer(_, e) = &pubkey_seq[1] {
e
} else {
return Err(JWKConvertError::PubKeyParse("Invalid format: 11"));
};
let rsa_pub_key = RSAPubKey {
e: e.clone(),
n: n.clone(),
};
Ok(rsa_pub_key)
}
/// Load an RSA public key from PEM format
pub fn load_pem(data: &[u8]) -> Result<RSAPubKey, JWKConvertError> {
let data = pem::parse(data)?;
load_der(&data.contents)
// RFC 7518
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
//#[serde(deny_unknown_fields)]
pub struct ECPublicKey {
#[serde(flatten)]
pub generic: Generic,
pub crv: ECCurveParameter,
pub x: Base64BigUint,
#[serde(skip_serializing_if = "Option::is_none")]
pub y: Option<Base64BigUint>,
}
impl FromStr for ECPublicKey {
type Err = Error;
fn from_str(data: &str) -> Result<Self, Error> {
Ok(serde_json::from_str(data)?)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
//#[serde(deny_unknown_fields)]
pub struct ECPrivateKey {
#[serde(flatten)]
pub public_key: ECPublicKey,
pub d: Base64BigUint,
}
impl FromStr for ECPrivateKey {
type Err = Error;
fn from_str(data: &str) -> Result<Self, Error> {
Ok(serde_json::from_str(data)?)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
//#[serde(deny_unknown_fields)]
pub struct RSAPublicKey {
#[serde(flatten)]
pub generic: Generic,
pub n: Base64BigUint,
pub e: Base64BigUint,
}
impl FromStr for RSAPublicKey {
type Err = Error;
fn from_str(data: &str) -> Result<Self, Error> {
Ok(serde_json::from_str(data)?)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
//#[serde(deny_unknown_fields)]
pub struct RSAPrivateKey {
#[serde(flatten)]
pub public_key: RSAPublicKey,
pub d: Base64BigUint,
#[serde(flatten)]
pub optimizations: Option<RSAPrivateKeyOptimizations>,
pub oth: Option<Vec<RSAPrivateKeyOtherPrimesInfo>>,
}
impl FromStr for RSAPrivateKey {
type Err = Error;
fn from_str(data: &str) -> Result<Self, Error> {
Ok(serde_json::from_str(data)?)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
//#[serde(deny_unknown_fields)]
pub struct RSAPrivateKeyOptimizations {
pub p: Base64BigUint,
pub q: Base64BigUint,
pub dp: Base64BigUint,
pub dq: Base64BigUint,
pub qi: Base64BigUint,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
//#[serde(deny_unknown_fields)]
pub struct RSAPrivateKeyOtherPrimesInfo {
pub r: Base64BigUint,
pub d: Base64BigUint,
pub t: Base64BigUint,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SymmetricKey {
#[serde(flatten)]
pub generic: Generic,
pub k: String,
}
impl FromStr for SymmetricKey {
type Err = Error;
fn from_str(data: &str) -> Result<Self, Error> {
Ok(serde_json::from_str(data)?)
}
}
/// A type of JWK.
/// See RFC 7518 Section 6.1.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum KeyType {
#[serde(rename = "EC")]
EllipticCurve,
#[serde(rename = "RSA")]
Rsa,
#[serde(rename = "oct")]
OctetSequence,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum KeyUse {
#[serde(rename = "sig")]
Signature,
#[serde(rename = "enc")]
Encryption,
}
/// Generic parameters for JSON Web Key.
/// See RFC 7517, Section 4.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Generic {
// Generic parameters
pub kty: KeyType,
#[serde(skip_serializing_if = "Option::is_none", rename = "use")]
pub use_: Option<KeyUse>,
#[serde(skip_serializing_if = "Option::is_none")]
pub key_ops: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alg: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub x5u: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub x5c: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub x5t: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "x5t#S256")]
pub x5t_s256: Option<String>,
}
#[cfg(test)]
mod tests {
mod test {
use super::*;
use std::fs::File;
use std::str;
#[test]
fn load_jwk1() -> Result<(), Box<dyn std::error::Error>> {
let mut reader = File::open("testfiles/test1.json")?;
let data: RSAPubKeyJWK = serde_json::from_reader(&mut reader)?;
fn load_rsa_pubkey1() -> Result<(), serde_json::Error> {
let pubkey_json: JsonWebKey =
serde_json::from_str(include_str!("../testfiles/test1.json"))?;
let pubkey_data = pubkey_json.rsa_public_key().unwrap();
assert_eq!(pubkey_data.generic.kty, KeyType::Rsa);
assert_eq!(pubkey_data.e.big_uint, BigUint::from(65537u64));
Ok(())
}
#[test]
fn load_rsa_pubkey2() -> Result<(), serde_json::Error> {
let pubkey_json: JsonWebKey =
serde_json::from_str(include_str!("../testfiles/test2.json"))?;
let pubkey_data = pubkey_json.rsa_public_key().unwrap();
assert_eq!(pubkey_data.generic.kty, KeyType::Rsa);
assert_eq!(pubkey_data.generic.alg.as_ref().unwrap(), "RS256");
assert_eq!(pubkey_data.generic.use_.unwrap(), KeyUse::Signature);
assert_eq!(
data,
RSAPubKeyJWK {
kid: Some("fe41cf0f-7901-489f-9d4e-1437d6c1aa1f".to_string()),
kty: "RSA".to_string(),
use_: None,
n: "rAINWn65QjweP2o9mzZF0dj1V_qlyBs9anRd_OA_iJUk2vTXU9FrzPl1AT8xT570ZGq7UW_dLE-ANUcL10Xr2I-bzVL9IL6aYnjO9L_lqbilfScfJSfT81Oho-vnj5FJH1LaD6s90vStEcSH49kwNoDDK9BXYovFEtxFeFx-H0eRoxHdxo7_91YBPyez4JjBYrBs29Sro2DbVSRxaW384HWXhEYNtGp2Z3Qf22t2o4tUkfxs_fuaU24mwKCWykfnQ5Cq8V7NAIqgWxhVsubjy9yCZ0kFxCNf_cs9hkWIYtVNSDjFg9P30bwy1-37Y01Lb4KVBW6fN7whCq_y-NlJWQ".to_string(),
e: "AQAB".to_string()
}
pubkey_data.generic.kid.as_ref().unwrap(),
"ctFNPw6mrKynlD3atDovZGBlbWRXj7IK0IBODJ_hqeI"
);
Ok(())
}
#[test]
fn load_jwk2() -> Result<(), Box<dyn std::error::Error>> {
let mut reader = File::open("testfiles/test2.json")?;
let data: RSAPubKeyJWK = serde_json::from_reader(&mut reader)?;
assert_eq!(
data,
RSAPubKeyJWK {
kid: Some("ctFNPw6mrKynlD3atDovZGBlbWRXj7IK0IBODJ_hqeI".to_string()),
kty: "RSA".to_string(),
use_: Some("sig".to_string()),
n: "r3tms5oOWdyOO-XqMdNkLdp7tm5Eb7kY2ENPCCt-bpU6pC1-QOO3dfTs9LeiyeyonZpqD93ghW1pe5LB49rt1e2BqPNZdndGJZWmtAlv9YXCkLKat6GaG2e7gNzuq7Ls-my-vAYmS6B71KpkBTze2S3KcTjTEP6tPbJzgqZ6vPNK3EYbdCPZHi-QujRmGWUBeUdwsOnGWslaVlmkd4nIeqWYjV-mFD07WwB1y-pWBlC39A_RY4XUGP8WFxd0RSFNy3EoJw1yDK6_-1_xZZfzlRn0JpZsl6p-8zI8FgvMpQmXTSiAgfhYJGhBRZuvOPUrHBhwNE0GeqYYbUiOsXQHiw".to_string(),
e: "AQAB".to_string()
}
pubkey_data.generic.x5t.as_ref().unwrap(),
"ZsHe1ebgPQqmqNF8rjKqWEjh4hk"
);
assert_eq!(
pubkey_data.generic.x5t_s256.as_ref().unwrap(),
"VaYCCwkyvl8K71fldYXJtNjHAPTGom2ylqdAbedtKUI"
);
assert_eq!(pubkey_data.e.big_uint, BigUint::from(65537u64));
Ok(())
}
#[test]
fn test_parse_der1() -> Result<(), JWKConvertError> {
let pem_data = include_bytes!("../testfiles/test1.pem");
let pem_rsa = load_pem(&pem_data[..])?;
let jwk_data = include_bytes!("../testfiles/test1.json");
let jwk_rsa = load_jwk(&jwk_data[..])?;
assert_eq!(pem_rsa, jwk_rsa.pubkey);
fn load_private_keys() -> Result<(), serde_json::Error> {
let privkey_json: JsonWebKeySet =
serde_json::from_str(include_str!("../testfiles/example-private-key.json"))?;
assert_eq!(privkey_json.keys.len(), 2);
let generated_pem = jwk_rsa.pubkey.to_pem()?;
assert_eq!(generated_pem, str::from_utf8(&pem_data[..]).unwrap());
let ec_private_key = privkey_json.keys[0].ec_private_key().unwrap();
assert_eq!(
ec_private_key.public_key.generic.kty,
KeyType::EllipticCurve
);
assert_eq!(ec_private_key.public_key.crv, ECCurveParameter::P256);
assert_eq!(
ec_private_key.public_key.generic.use_.unwrap(),
KeyUse::Encryption
);
assert_eq!(ec_private_key.public_key.generic.kid.as_ref().unwrap(), "1");
let rsa_private_key = privkey_json.keys[1].rsa_private_key().unwrap();
assert_eq!(rsa_private_key.public_key.generic.kty, KeyType::Rsa);
assert!(rsa_private_key.optimizations.is_some());
let jwk_parsed: RSAPubKeyJWK =
serde_json::from_slice(jwk_data).map_err(JWKConvertError::JSONParseError)?;
let pem_jwk: RSAPubKeyJWK = serde_json::from_str(
&RSAJWK {
pubkey: pem_rsa,
kid: jwk_rsa.kid.clone(),
jwk_use: jwk_rsa.jwk_use,
}
.to_jwk()?,
)
.map_err(JWKConvertError::JSONParseError)?;
assert_eq!(jwk_parsed, pem_jwk);
Ok(())
}
#[test]
fn test_parse_der2() -> Result<(), JWKConvertError> {
let pem_data = include_bytes!("../testfiles/test2.pem");
let pem_rsa = load_pem(&pem_data[..])?;
let jwk_data = include_bytes!("../testfiles/test2.json");
let jwk_rsa = load_jwk(&jwk_data[..])?;
assert_eq!(pem_rsa, jwk_rsa.pubkey);
fn load_public_keys() -> Result<(), serde_json::Error> {
let pubkey_json: JsonWebKeySet =
serde_json::from_str(include_str!("../testfiles/example-public-key.json"))?;
let generated_pem = jwk_rsa.pubkey.to_pem()?;
assert_eq!(generated_pem, str::from_utf8(&pem_data[..]).unwrap());
let ec_public_key = pubkey_json.keys[0].ec_public_key().unwrap();
assert_eq!(ec_public_key.generic.kty, KeyType::EllipticCurve);
assert_eq!(ec_public_key.crv, ECCurveParameter::P256);
assert_eq!(ec_public_key.generic.use_.unwrap(), KeyUse::Encryption);
assert_eq!(ec_public_key.generic.kid.as_ref().unwrap(), "1");
assert_eq!(
ec_public_key.x.to_base64url(),
"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"
);
assert_eq!(
ec_public_key.y.as_ref().unwrap().to_base64url(),
"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
);
let jwk_parsed: RSAPubKeyJWK = serde_json::from_slice(jwk_data)?;
let pem_jwk: RSAPubKeyJWK = serde_json::from_str(
&RSAJWK {
pubkey: pem_rsa,
kid: jwk_rsa.kid.clone(),
jwk_use: jwk_rsa.jwk_use,
}
.to_jwk()?,
)?;
assert_eq!(jwk_parsed, pem_jwk);
let rsa_public_key = pubkey_json.keys[1].rsa_public_key().unwrap();
assert_eq!(rsa_public_key.generic.kty, KeyType::Rsa);
assert_eq!(rsa_public_key.e.to_base64url(), "AQAB");
assert_eq!(rsa_public_key.n.to_base64url(), "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw");
assert_eq!(rsa_public_key.generic.alg.as_ref().unwrap(), "RS256");
Ok(())
}
#[test]
fn load_symmetric_keys() -> Result<(), serde_json::Error> {
let symmetric_json: JsonWebKeySet =
serde_json::from_str(include_str!("../testfiles/example-symmetric-keys.json"))?;
let symmetric_key = symmetric_json.keys[0].symmetric_key().unwrap();
assert_eq!(symmetric_key.generic.kty, KeyType::OctetSequence);
assert_eq!(symmetric_key.generic.alg.as_ref().unwrap(), "A128KW");
assert_eq!(symmetric_key.k, "GawgguFyGrWKav7AX4VKUg");
let symmetric_key = symmetric_json.keys[1].symmetric_key().unwrap();
assert_eq!(symmetric_key.generic.kty, KeyType::OctetSequence);
assert_eq!(symmetric_key.k, "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow");
assert_eq!(
symmetric_key.generic.kid.as_ref().unwrap(),
"HMAC key used in JWS spec Appendix A.1 example"
);
Ok(())
}
}

View file

@ -0,0 +1,26 @@
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
"use": "enc",
"kid": "1"
},
{
"kty": "RSA",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e": "AQAB",
"d": "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q",
"p": "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs",
"q": "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk",
"dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0",
"dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk",
"qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU",
"alg": "RS256",
"kid": "2011-04-29"
}
]
}

View file

@ -0,0 +1,19 @@
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"use": "enc",
"kid": "1"
},
{
"kty": "RSA",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e": "AQAB",
"alg": "RS256",
"kid": "2011-04-29"
}
]
}

View file

@ -0,0 +1,14 @@
{
"keys": [
{
"kty": "oct",
"alg": "A128KW",
"k": "GawgguFyGrWKav7AX4VKUg"
},
{
"kty": "oct",
"k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow",
"kid": "HMAC key used in JWS spec Appendix A.1 example"
}
]
}