build!: rename to -repaired suffix
This commit is contained in:
parent
b4e4a4ad60
commit
b95aaffe53
16 changed files with 8 additions and 8 deletions
30
jsonwebkey-convert-repaired/Cargo.toml
Normal file
30
jsonwebkey-convert-repaired/Cargo.toml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "jsonwebkey-convert-repaired"
|
||||
version = "0.3.1"
|
||||
authors = ["Okamura Yasunobu <okamura@informationsea.info>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
description = "Convert an RSA public key between Json Web Key and DER/PEM format (dependencies repaired)."
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/informationsea/jsonwebkey-rs"
|
||||
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 = { 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 = "^0.13"
|
||||
lazy_static = "1"
|
||||
thiserror = "1"
|
||||
29
jsonwebkey-convert-repaired/README.md
Normal file
29
jsonwebkey-convert-repaired/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# jsonwebkey-convert
|
||||
Convert an RSA public key between Json Web Key and DER/PEM format.
|
||||
|
||||
## Convert PEM to JWK
|
||||
``` rust
|
||||
use jsonwebkey_convert::*;
|
||||
use jsonwebkey_convert::der::FromPem;
|
||||
|
||||
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
|
||||
|
||||
```rust
|
||||
use jsonwebkey_convert::*;
|
||||
use jsonwebkey_convert::der::ToPem;
|
||||
|
||||
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(())
|
||||
}
|
||||
```
|
||||
170
jsonwebkey-convert-repaired/src/der.rs
Normal file
170
jsonwebkey-convert-repaired/src/der.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
12
jsonwebkey-convert-repaired/src/jwt.rs
Normal file
12
jsonwebkey-convert-repaired/src/jwt.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
495
jsonwebkey-convert-repaired/src/lib.rs
Normal file
495
jsonwebkey-convert-repaired/src/lib.rs
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
//! # jsonwebkey-convert
|
||||
//! 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<(), 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<(), Error> {
|
||||
//! # let jwk_data = include_str!("../testfiles/test1.json");
|
||||
//! let rsa_jwk: RSAPublicKey = jwk_data.parse()?;
|
||||
//! let pem_data = rsa_jwk.to_pem()?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
/// 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 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::DecodeError),
|
||||
#[error(transparent)]
|
||||
JSONParseError(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
/// A set of Json Web Keys
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct JsonWebKeySet {
|
||||
pub keys: Vec<JsonWebKey>,
|
||||
}
|
||||
|
||||
impl JsonWebKeySet {
|
||||
pub fn from_bytes(data: &[u8]) -> Result<JsonWebKeySet, Error> {
|
||||
Ok(serde_json::from_slice(data)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for JsonWebKeySet {
|
||||
type Err = Error;
|
||||
fn from_str(data: &str) -> Result<JsonWebKeySet, Error> {
|
||||
Ok(serde_json::from_str(data)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 Into<BigUint> for Base64BigUint {
|
||||
fn into(self) -> BigUint {
|
||||
self.big_uint
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Base64BigUint {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.to_base64url().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
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!(
|
||||
pubkey_data.generic.kid.as_ref().unwrap(),
|
||||
"ctFNPw6mrKynlD3atDovZGBlbWRXj7IK0IBODJ_hqeI"
|
||||
);
|
||||
assert_eq!(
|
||||
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 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 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());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_public_keys() -> Result<(), serde_json::Error> {
|
||||
let pubkey_json: JsonWebKeySet =
|
||||
serde_json::from_str(include_str!("../testfiles/example-public-key.json"))?;
|
||||
|
||||
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 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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
jsonwebkey-convert-repaired/testfiles/test1.json
Normal file
6
jsonwebkey-convert-repaired/testfiles/test1.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"kty": "RSA",
|
||||
"e": "AQAB",
|
||||
"kid": "fe41cf0f-7901-489f-9d4e-1437d6c1aa1f",
|
||||
"n": "rAINWn65QjweP2o9mzZF0dj1V_qlyBs9anRd_OA_iJUk2vTXU9FrzPl1AT8xT570ZGq7UW_dLE-ANUcL10Xr2I-bzVL9IL6aYnjO9L_lqbilfScfJSfT81Oho-vnj5FJH1LaD6s90vStEcSH49kwNoDDK9BXYovFEtxFeFx-H0eRoxHdxo7_91YBPyez4JjBYrBs29Sro2DbVSRxaW384HWXhEYNtGp2Z3Qf22t2o4tUkfxs_fuaU24mwKCWykfnQ5Cq8V7NAIqgWxhVsubjy9yCZ0kFxCNf_cs9hkWIYtVNSDjFg9P30bwy1-37Y01Lb4KVBW6fN7whCq_y-NlJWQ"
|
||||
}
|
||||
9
jsonwebkey-convert-repaired/testfiles/test1.pem
Normal file
9
jsonwebkey-convert-repaired/testfiles/test1.pem
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArAINWn65QjweP2o9mzZF
|
||||
0dj1V/qlyBs9anRd/OA/iJUk2vTXU9FrzPl1AT8xT570ZGq7UW/dLE+ANUcL10Xr
|
||||
2I+bzVL9IL6aYnjO9L/lqbilfScfJSfT81Oho+vnj5FJH1LaD6s90vStEcSH49kw
|
||||
NoDDK9BXYovFEtxFeFx+H0eRoxHdxo7/91YBPyez4JjBYrBs29Sro2DbVSRxaW38
|
||||
4HWXhEYNtGp2Z3Qf22t2o4tUkfxs/fuaU24mwKCWykfnQ5Cq8V7NAIqgWxhVsubj
|
||||
y9yCZ0kFxCNf/cs9hkWIYtVNSDjFg9P30bwy1+37Y01Lb4KVBW6fN7whCq/y+NlJ
|
||||
WQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
13
jsonwebkey-convert-repaired/testfiles/test2.json
Normal file
13
jsonwebkey-convert-repaired/testfiles/test2.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"kid": "ctFNPw6mrKynlD3atDovZGBlbWRXj7IK0IBODJ_hqeI",
|
||||
"kty": "RSA",
|
||||
"alg": "RS256",
|
||||
"use": "sig",
|
||||
"n": "r3tms5oOWdyOO-XqMdNkLdp7tm5Eb7kY2ENPCCt-bpU6pC1-QOO3dfTs9LeiyeyonZpqD93ghW1pe5LB49rt1e2BqPNZdndGJZWmtAlv9YXCkLKat6GaG2e7gNzuq7Ls-my-vAYmS6B71KpkBTze2S3KcTjTEP6tPbJzgqZ6vPNK3EYbdCPZHi-QujRmGWUBeUdwsOnGWslaVlmkd4nIeqWYjV-mFD07WwB1y-pWBlC39A_RY4XUGP8WFxd0RSFNy3EoJw1yDK6_-1_xZZfzlRn0JpZsl6p-8zI8FgvMpQmXTSiAgfhYJGhBRZuvOPUrHBhwNE0GeqYYbUiOsXQHiw",
|
||||
"e": "AQAB",
|
||||
"x5c": [
|
||||
"MIIClTCCAX0CBgFzYPXiUDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDAN2bmMwHhcNMjAwNzE4MDgwNDUzWhcNMzAwNzE4MDgwNjMzWjAOMQwwCgYDVQQDDAN2bmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCve2azmg5Z3I475eox02Qt2nu2bkRvuRjYQ08IK35ulTqkLX5A47d19Oz0t6LJ7KidmmoP3eCFbWl7ksHj2u3V7YGo81l2d0Yllaa0CW/1hcKQspq3oZobZ7uA3O6rsuz6bL68BiZLoHvUqmQFPN7ZLcpxONMQ/q09snOCpnq880rcRht0I9keL5C6NGYZZQF5R3Cw6cZayVpWWaR3ich6pZiNX6YUPTtbAHXL6lYGULf0D9FjhdQY/xYXF3RFIU3LcSgnDXIMrr/7X/Fll/OVGfQmlmyXqn7zMjwWC8ylCZdNKICB+FgkaEFFm6849SscGHA0TQZ6phhtSI6xdAeLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACRzkIzJcASypr/CVsI30JAQLfWHJWqBrPq3WEch9oCXQ6J567MNRptV40/rLqep6CizJxO+eogip7z/9cSyLGiZrz36YwNvxjzDw7MvwAgbSR18QRjMAzGhLm2EKdJWSpJXGFVfuzOHklEDstx0ZuNO11XgWrx7RZdeSQDyKEoQ1hJH0tc+8UCYRRZq3M1rm9obTWX+rLz5nmFFucn+4pjcfwl1wYFUWToEkOKHm4e45hDFXaX/Z3GmeCXhs8kdCtYMYKjNSMdAGc1/NCnNF56tUI3bIPbZqWtl5U+rI3Gzm6DX82DyVcwi/A0KltsB9yF335FsXFLneO51xTcFSR4="
|
||||
],
|
||||
"x5t": "ZsHe1ebgPQqmqNF8rjKqWEjh4hk",
|
||||
"x5t#S256": "VaYCCwkyvl8K71fldYXJtNjHAPTGom2ylqdAbedtKUI"
|
||||
}
|
||||
9
jsonwebkey-convert-repaired/testfiles/test2.pem
Normal file
9
jsonwebkey-convert-repaired/testfiles/test2.pem
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3tms5oOWdyOO+XqMdNk
|
||||
Ldp7tm5Eb7kY2ENPCCt+bpU6pC1+QOO3dfTs9LeiyeyonZpqD93ghW1pe5LB49rt
|
||||
1e2BqPNZdndGJZWmtAlv9YXCkLKat6GaG2e7gNzuq7Ls+my+vAYmS6B71KpkBTze
|
||||
2S3KcTjTEP6tPbJzgqZ6vPNK3EYbdCPZHi+QujRmGWUBeUdwsOnGWslaVlmkd4nI
|
||||
eqWYjV+mFD07WwB1y+pWBlC39A/RY4XUGP8WFxd0RSFNy3EoJw1yDK6/+1/xZZfz
|
||||
lRn0JpZsl6p+8zI8FgvMpQmXTSiAgfhYJGhBRZuvOPUrHBhwNE0GeqYYbUiOsXQH
|
||||
iwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
Loading…
Add table
Add a link
Reference in a new issue