mirror of
https://github.com/BitskiCo/jwk-rs
synced 2024-11-25 05:19:22 +00:00
Refactor PKCS#8 conversion
This commit is contained in:
parent
8b1d543598
commit
8aaf3d71c7
@ -12,13 +12,17 @@ edition = "2018"
|
||||
base64 = "0.12"
|
||||
bitflags = "1.2"
|
||||
derive_more = "0.99"
|
||||
num-bigint = { version = "0.2", optional = true }
|
||||
paste = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
syn = { version = "1.0", features = ["full"] } # required to parse const generics
|
||||
thiserror = "1.0"
|
||||
yasna = { version = "0.3", optional = true }
|
||||
yasna = { version = "0.3", optional = true, features = ["num-bigint"] }
|
||||
zeroize = { version = "1.1", features = ["zeroize_derive"] }
|
||||
|
||||
[features]
|
||||
conversion = ["yasna"]
|
||||
convert = ["num-bigint", "yasna"]
|
||||
|
||||
[dev-dependencies]
|
||||
jsonwebtoken = "7.2"
|
||||
|
@ -35,3 +35,8 @@ fn main() {
|
||||
let token = jwt::encode(&jwt::Header::default(), &() /* claims */, encoding_key).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* `convert` - enables `Key::{to_der, to_pem}`.
|
||||
This pulls in the [yasna](https://crates.io/crates/yasna) crate.
|
||||
|
181
src/lib.rs
181
src/lib.rs
@ -8,6 +8,8 @@ mod key_ops;
|
||||
mod tests;
|
||||
mod utils;
|
||||
|
||||
use std::array::FixedSizeArray;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@ -55,7 +57,7 @@ impl std::str::FromStr for JsonWebKey {
|
||||
(
|
||||
ES256,
|
||||
EC {
|
||||
params: Curve::P256 { .. },
|
||||
curve: Curve::P256 { .. },
|
||||
},
|
||||
)
|
||||
| (RS256, RSA { .. })
|
||||
@ -78,16 +80,20 @@ impl std::fmt::Display for JsonWebKey {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "kty")]
|
||||
pub enum Key {
|
||||
/// An elliptic curve, as per [RFC 7518 §6.2](https://tools.ietf.org/html/rfc7518#section-6.2).
|
||||
EC {
|
||||
#[serde(flatten)]
|
||||
params: Curve,
|
||||
curve: Curve,
|
||||
},
|
||||
/// An elliptic curve, as per [RFC 7518 §6.3](https://tools.ietf.org/html/rfc7518#section-6.3).
|
||||
/// See also: [RFC 3447](https://tools.ietf.org/html/rfc3447).
|
||||
RSA {
|
||||
#[serde(flatten)]
|
||||
public: RsaPublic,
|
||||
#[serde(flatten, default, skip_serializing_if = "Option::is_none")]
|
||||
private: Option<RsaPrivate>,
|
||||
},
|
||||
/// A symmetric key, as per [RFC 7518 §6.4](https://tools.ietf.org/html/rfc7518#section-6.4).
|
||||
#[serde(rename = "oct")]
|
||||
Symmetric {
|
||||
#[serde(rename = "k")]
|
||||
@ -102,7 +108,7 @@ impl Key {
|
||||
match self {
|
||||
Self::Symmetric { .. }
|
||||
| Self::EC {
|
||||
params: Curve::P256 { d: Some(_), .. },
|
||||
curve: Curve::P256 { d: Some(_), .. },
|
||||
..
|
||||
}
|
||||
| Self::RSA {
|
||||
@ -125,9 +131,9 @@ impl Key {
|
||||
Some(match self {
|
||||
Self::Symmetric { .. } => return None,
|
||||
Self::EC {
|
||||
params: Curve::P256 { x, y, .. },
|
||||
curve: Curve::P256 { x, y, .. },
|
||||
} => Self::EC {
|
||||
params: Curve::P256 {
|
||||
curve: Curve::P256 {
|
||||
x: x.clone(),
|
||||
y: y.clone(),
|
||||
d: None,
|
||||
@ -141,22 +147,25 @@ impl Key {
|
||||
}
|
||||
|
||||
/// If this key is asymmetric, encodes it as PKCS#8.
|
||||
#[cfg(feature = "conversion")]
|
||||
pub fn to_der(&self) -> Option<Vec<u8>> {
|
||||
#[cfg(feature = "convert")]
|
||||
pub fn to_der(&self) -> Result<Vec<u8>, PkcsConvertError> {
|
||||
use num_bigint::BigUint;
|
||||
use yasna::{models::ObjectIdentifier, DERWriter, DERWriterSeq, Tag};
|
||||
|
||||
use crate::utils::pkcs8;
|
||||
|
||||
if let Self::Symmetric { .. } = self {
|
||||
return None;
|
||||
return Err(PkcsConvertError::NotAsymmetric);
|
||||
}
|
||||
Some(yasna::construct_der(|writer| match self {
|
||||
|
||||
Ok(match self {
|
||||
Self::EC {
|
||||
params: Curve::P256 { d, x, y },
|
||||
curve: Curve::P256 { d, x, y },
|
||||
} => {
|
||||
let write_curve_oid = |writer: DERWriter| {
|
||||
writer.write_oid(&ObjectIdentifier::from_slice(&[
|
||||
1, 2, 840, 10045, 3, 1, 7, // prime256v1
|
||||
]));
|
||||
};
|
||||
let ec_public_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 2, 1]);
|
||||
let prime256v1_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 3, 1, 7]);
|
||||
let oids = &[Some(&ec_public_oid), Some(&prime256v1_oid)];
|
||||
|
||||
let write_public = |writer: DERWriter| {
|
||||
let public_bytes: Vec<u8> = [0x04 /* uncompressed */]
|
||||
.iter()
|
||||
@ -166,93 +175,77 @@ impl Key {
|
||||
.collect();
|
||||
writer.write_bitvec_bytes(&public_bytes, 8 * (32 * 2 + 1));
|
||||
};
|
||||
writer.write_sequence(|writer| {
|
||||
match d {
|
||||
Some(private_point) => {
|
||||
|
||||
match d {
|
||||
Some(private_point) => {
|
||||
pkcs8::write_private(oids, |writer: &mut DERWriterSeq| {
|
||||
writer.next().write_i8(1); // version
|
||||
writer.next().write_bytes(private_point.as_ref());
|
||||
writer.next().write_bytes(private_point.as_slice());
|
||||
writer.next().write_tagged(Tag::context(0), |writer| {
|
||||
write_curve_oid(writer);
|
||||
writer.write_oid(&prime256v1_oid)
|
||||
});
|
||||
writer.next().write_tagged(Tag::context(1), |writer| {
|
||||
write_public(writer);
|
||||
});
|
||||
}
|
||||
None => {
|
||||
writer.next().write_sequence(|writer| {
|
||||
writer.next().write_oid(&ObjectIdentifier::from_slice(&[
|
||||
1, 2, 840, 10045, 2, 1, // ecPublicKey
|
||||
]));
|
||||
write_curve_oid(writer.next());
|
||||
});
|
||||
write_public(writer.next());
|
||||
}
|
||||
};
|
||||
});
|
||||
writer.next().write_tagged(Tag::context(1), write_public);
|
||||
})
|
||||
}
|
||||
None => pkcs8::write_public(oids, write_public),
|
||||
}
|
||||
}
|
||||
Self::RSA { public, private } => {
|
||||
let write_alg_id = |writer: &mut DERWriterSeq| {
|
||||
writer.next().write_oid(&ObjectIdentifier::from_slice(&[
|
||||
1, 2, 840, 113549, 1, 1, 1, // rsaEncryption
|
||||
]));
|
||||
writer.next().write_null(); // parameters
|
||||
let rsa_encryption_oid = ObjectIdentifier::from_slice(&[
|
||||
1, 2, 840, 113549, 1, 1, 1, // rsaEncryption
|
||||
]);
|
||||
let oids = &[Some(&rsa_encryption_oid), None];
|
||||
let write_bytevec = |writer: DERWriter, vec: &ByteVec| {
|
||||
let bigint = BigUint::from_bytes_be(vec.as_slice());
|
||||
writer.write_biguint(&bigint);
|
||||
};
|
||||
|
||||
let write_public = |writer: &mut DERWriterSeq| {
|
||||
writer.next().write_bytes(&*public.n);
|
||||
write_bytevec(writer.next(), &public.n);
|
||||
writer.next().write_u32(PUBLIC_EXPONENT);
|
||||
};
|
||||
writer.write_sequence(|writer| {
|
||||
match private {
|
||||
Some(private) => {
|
||||
writer.next().write_i8(0); // version
|
||||
writer.next().write_sequence(|writer| {
|
||||
write_alg_id(writer);
|
||||
});
|
||||
writer
|
||||
.next()
|
||||
.write_tagged(yasna::tags::TAG_OCTETSTRING, |writer| {
|
||||
writer.write_sequence(|writer| {
|
||||
writer.next().write_i8(0); // version
|
||||
write_public(writer);
|
||||
writer.next().write_bytes(&private.d);
|
||||
if let Some(p) = &private.p {
|
||||
writer.next().write_bytes(p);
|
||||
}
|
||||
if let Some(q) = &private.q {
|
||||
writer.next().write_bytes(q);
|
||||
}
|
||||
if let Some(dp) = &private.dp {
|
||||
writer.next().write_bytes(dp);
|
||||
}
|
||||
if let Some(dq) = &private.dq {
|
||||
writer.next().write_bytes(dq);
|
||||
}
|
||||
if let Some(qi) = &private.qi {
|
||||
writer.next().write_bytes(qi);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let write_private = |writer: &mut DERWriterSeq, private: &RsaPrivate| {
|
||||
// https://tools.ietf.org/html/rfc3447#appendix-A.1.2
|
||||
writer.next().write_i8(0); // version (two-prime)
|
||||
write_public(writer);
|
||||
write_bytevec(writer.next(), &private.d);
|
||||
macro_rules! write_opt_bytevecs {
|
||||
($($param:ident),+) => {{
|
||||
$(write_bytevec(writer.next(), private.$param.as_ref().unwrap());)+
|
||||
}};
|
||||
}
|
||||
None => {
|
||||
write_alg_id(writer);
|
||||
writer
|
||||
.next()
|
||||
.write_tagged(yasna::tags::TAG_BITSTRING, |writer| {
|
||||
writer.write_sequence(|writer| {
|
||||
write_public(writer);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
write_opt_bytevecs!(p, q, dp, dq, qi);
|
||||
};
|
||||
|
||||
match private {
|
||||
Some(
|
||||
private
|
||||
@
|
||||
RsaPrivate {
|
||||
d: _,
|
||||
p: Some(_),
|
||||
q: Some(_),
|
||||
dp: Some(_),
|
||||
dq: Some(_),
|
||||
qi: Some(_),
|
||||
},
|
||||
) => pkcs8::write_private(oids, |writer| write_private(writer, private)),
|
||||
Some(_) => return Err(PkcsConvertError::MissingRsaParams),
|
||||
None => pkcs8::write_public(oids, |writer| {
|
||||
let body =
|
||||
yasna::construct_der(|writer| writer.write_sequence(write_public));
|
||||
writer.write_bitvec_bytes(&body, body.len() * 8);
|
||||
}),
|
||||
}
|
||||
}
|
||||
Self::Symmetric { .. } => unreachable!("checked above"),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// If this key is asymmetric, encodes it as PKCS#8 with PEM armoring.
|
||||
#[cfg(feature = "conversion")]
|
||||
pub fn to_pem(&self) -> Option<String> {
|
||||
#[cfg(feature = "convert")]
|
||||
pub fn to_pem(&self) -> Result<String, PkcsConvertError> {
|
||||
use std::fmt::Write;
|
||||
let der_b64 = base64::encode(self.to_der()?);
|
||||
let key_ty = if self.is_private() {
|
||||
@ -272,7 +265,7 @@ impl Key {
|
||||
.unwrap();
|
||||
}
|
||||
writeln!(&mut pem, "-----END {} KEY-----", key_ty).unwrap();
|
||||
Some(pem)
|
||||
Ok(pem)
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,8 +353,7 @@ pub enum JsonWebAlgorithm {
|
||||
ES256,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error)]
|
||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Serde(#[from] serde_json::Error),
|
||||
@ -372,3 +364,12 @@ pub enum Error {
|
||||
#[error("mismatched algorithm for key type")]
|
||||
MismatchedAlgorithm,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PkcsConvertError {
|
||||
#[error("encoding RSA JWK as PKCS#8 requires specifing all of p, q, dp, dq, qi")]
|
||||
MissingRsaParams,
|
||||
|
||||
#[error("a symmetric key can not be encoded using PKCS#8")]
|
||||
NotAsymmetric,
|
||||
}
|
||||
|
119
src/tests.rs
119
src/tests.rs
@ -2,10 +2,8 @@ use super::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn deserialize_es256() {
|
||||
// Generated using https://mkjwk.org
|
||||
let jwk_str = r#"{
|
||||
// Generated using https://mkjwk.org
|
||||
static P256_JWK_FIXTURE: &str = r#"{
|
||||
"kty": "EC",
|
||||
"d": "ZoKQ9j4dhIBlMRVrv-QG8P_T9sutv3_95eio9MtpgKg",
|
||||
"use": "enc",
|
||||
@ -15,13 +13,29 @@ fn deserialize_es256() {
|
||||
"y": "TjYZoHnctatEE6NCrKmXQdJJPnNzZEX8nBmZde3AY4k",
|
||||
"alg": "ES256"
|
||||
}"#;
|
||||
let jwk = JsonWebKey::from_str(jwk_str).unwrap();
|
||||
|
||||
static RSA_JWK_FIXTURE: &str = r#"{
|
||||
"p": "6AQ4yHef17an_i5LQPHNIxzpH65xWOSf_qCB7q-lXyM",
|
||||
"kty": "RSA",
|
||||
"q": "tSVfpefCsf1iWmAs1zYvxdEsUiv0VMEuQBtbTijj_OE",
|
||||
"d": "Qdp8a8Df5TlMaaloXApNF_3eu8sLHNWbXdg70e5YVTAs0WUfaIf5c3n96RrDDAzmMEwgKnJ7A1NJ9Nlzz4Z0AQ",
|
||||
"e": "AQAB",
|
||||
"use": "enc",
|
||||
"qi": "adhQHH8IGXFfLEMnZ5t_TeCp5zgSwQktJ2lmylxUG0M",
|
||||
"dp": "qVnLiKeoSG_Olz17OGBGd4a2sqVFnrjh_51wuaQDdTk",
|
||||
"dq": "GL_Ec6xYg2z1FRfyyGyU1lgf0BJFTZcfNI8ISIN5ssE",
|
||||
"n": "pCzbcd9kjvg5rfGHdEMWnXo49zbB6FLQ-m0B0BvVp0aojVWYa0xujC-ZP7ZhxByPxyc2PazwFJJi9ivZ_ggRww"
|
||||
}"#;
|
||||
|
||||
#[test]
|
||||
fn deserialize_es256() {
|
||||
let jwk = JsonWebKey::from_str(P256_JWK_FIXTURE).unwrap();
|
||||
assert_eq!(
|
||||
jwk,
|
||||
JsonWebKey {
|
||||
key: box Key::EC {
|
||||
// The parameters were decoded using a 10-liner Rust script.
|
||||
params: Curve::P256 {
|
||||
curve: Curve::P256 {
|
||||
d: Some(
|
||||
[
|
||||
102, 130, 144, 246, 62, 29, 132, 128, 101, 49, 21, 107, 191, 228, 6,
|
||||
@ -54,7 +68,7 @@ fn deserialize_es256() {
|
||||
fn serialize_es256() {
|
||||
let jwk = JsonWebKey {
|
||||
key: box Key::EC {
|
||||
params: Curve::P256 {
|
||||
curve: Curve::P256 {
|
||||
d: None,
|
||||
x: [1u8; 32].into(),
|
||||
y: [2u8; 32].into(),
|
||||
@ -114,19 +128,7 @@ fn serialize_hs256() {
|
||||
|
||||
#[test]
|
||||
fn deserialize_rs256() {
|
||||
let jwk_str = r#"{
|
||||
"p": "_LSip5o4eaGf25uvwyUq9ubRtKemrCaoCxumoj63Au0",
|
||||
"kty": "RSA",
|
||||
"q": "l20iLpicEW3uja0Zg2xP6DjZa86bD4IQ3wFXCcKCf1c",
|
||||
"d": "Xo0VAHtfV38HwJbAI6X-Fu7vuyoQjnuiSlQhcSjxn0BZfLP_DKxdJ2ANgTGVE0x243YHqhWRHLobbmDcnUuMOQ",
|
||||
"e": "AQAB",
|
||||
"qi": "2mzAaSr7I1D3vDtOhbWKS9-9ELRHKbAHz4dhn4DSCBo",
|
||||
"dp": "-kyswxeVEpyM6wdU2xRobu-HDMn145PSZFY6AX_e460",
|
||||
"alg": "RS256",
|
||||
"dq": "OqMWE3khJlatg8s-D_hHUSOCfg65WN4C7ng0XiEmK20",
|
||||
"n": "lXpGmBoIxj56TpptApaac6V19_7WWbq0a14a5UHBBlkc54NwIUa2X4p9OeK2sy6rLQ_1g1AcSwfsVUy8MP-Riw"
|
||||
}"#;
|
||||
let jwk = JsonWebKey::from_str(jwk_str).unwrap();
|
||||
let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap();
|
||||
assert_eq!(
|
||||
jwk,
|
||||
JsonWebKey {
|
||||
@ -262,24 +264,71 @@ fn mismatched_algorithm() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "conversion")]
|
||||
#[cfg(feature = "convert")]
|
||||
#[test]
|
||||
fn es256_to_pem() {
|
||||
let jwk_str = r#"{
|
||||
"kty": "EC",
|
||||
"d": "ZoKQ9j4dhIBlMRVrv-QG8P_T9sutv3_95eio9MtpgKg",
|
||||
"crv": "P-256",
|
||||
"x": "QOMHmv96tVlJv-uNqprnDSKIj5AiLTXKRomXYnav0N0",
|
||||
"y": "TjYZoHnctatEE6NCrKmXQdJJPnNzZEX8nBmZde3AY4k"
|
||||
}"#;
|
||||
let jwk = JsonWebKey::from_str(jwk_str).unwrap();
|
||||
fn p256_private_to_pem() {
|
||||
// generated using mkjwk, converted using node-jwk-to-pem, verified using openssl
|
||||
let jwk = JsonWebKey::from_str(P256_JWK_FIXTURE).unwrap();
|
||||
#[rustfmt::skip]
|
||||
assert_eq!(
|
||||
base64::encode(jwk.key.to_pem().unwrap()),
|
||||
jwk.key.to_pem().unwrap(),
|
||||
"-----BEGIN PRIVATE KEY-----
|
||||
MHcCAQEEIGaCkPY+HYSAZTEVa7/kBvD/0/bLrb9//eXoqPTLaYCooAoGCCqGSM49
|
||||
AwEHoUQDQgAEQOMHmv96tVlJv+uNqprnDSKIj5AiLTXKRomXYnav0N1ONhmgedy1
|
||||
q0QTo0KsqZdB0kk+c3NkRfycGZl17cBjiQ==
|
||||
-----END PRIVATE KEY-----"
|
||||
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgZoKQ9j4dhIBlMRVr
|
||||
v+QG8P/T9sutv3/95eio9MtpgKigCgYIKoZIzj0DAQehRANCAARA4wea/3q1WUm/
|
||||
642qmucNIoiPkCItNcpGiZdidq/Q3U42GaB53LWrRBOjQqypl0HSST5zc2RF/JwZ
|
||||
mXXtwGOJ
|
||||
-----END PRIVATE KEY-----
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "convert")]
|
||||
#[test]
|
||||
fn p256_public_to_pem() {
|
||||
let jwk = JsonWebKey::from_str(P256_JWK_FIXTURE).unwrap();
|
||||
#[rustfmt::skip]
|
||||
assert_eq!(
|
||||
jwk.key.to_public().unwrap().to_pem().unwrap(),
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQOMHmv96tVlJv+uNqprnDSKIj5Ai
|
||||
LTXKRomXYnav0N1ONhmgedy1q0QTo0KsqZdB0kk+c3NkRfycGZl17cBjiQ==
|
||||
-----END PUBLIC KEY-----
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "convert")]
|
||||
#[test]
|
||||
fn rsa_private_to_pem() {
|
||||
let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap();
|
||||
#[rustfmt::skip]
|
||||
assert_eq!(
|
||||
jwk.key.to_pem().unwrap(),
|
||||
"-----BEGIN PRIVATE KEY-----
|
||||
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApCzbcd9kjvg5rfGH
|
||||
dEMWnXo49zbB6FLQ+m0B0BvVp0aojVWYa0xujC+ZP7ZhxByPxyc2PazwFJJi9ivZ
|
||||
/ggRwwIDAQABAkBB2nxrwN/lOUxpqWhcCk0X/d67ywsc1Ztd2DvR7lhVMCzRZR9o
|
||||
h/lzef3pGsMMDOYwTCAqcnsDU0n02XPPhnQBAiEA6AQ4yHef17an/i5LQPHNIxzp
|
||||
H65xWOSf/qCB7q+lXyMCIQC1JV+l58Kx/WJaYCzXNi/F0SxSK/RUwS5AG1tOKOP8
|
||||
4QIhAKlZy4inqEhvzpc9ezhgRneGtrKlRZ644f+dcLmkA3U5AiAYv8RzrFiDbPUV
|
||||
F/LIbJTWWB/QEkVNlx80jwhIg3mywQIgadhQHH8IGXFfLEMnZ5t/TeCp5zgSwQkt
|
||||
J2lmylxUG0M=
|
||||
-----END PRIVATE KEY-----
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "convert")]
|
||||
#[test]
|
||||
fn rsa_public_to_pem() {
|
||||
let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap();
|
||||
#[rustfmt::skip]
|
||||
assert_eq!(
|
||||
jwk.key.to_public().unwrap().to_pem().unwrap(),
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKQs23HfZI74Oa3xh3RDFp16OPc2wehS
|
||||
0PptAdAb1adGqI1VmGtMbowvmT+2YcQcj8cnNj2s8BSSYvYr2f4IEcMCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
"
|
||||
);
|
||||
}
|
||||
|
50
src/utils.rs
50
src/utils.rs
@ -30,3 +30,53 @@ pub fn deserialize_base64<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D:
|
||||
de::Error::custom(err_msg.strip_suffix(".").unwrap_or(&err_msg))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "convert")]
|
||||
pub mod pkcs8 {
|
||||
use yasna::{
|
||||
models::{ObjectIdentifier, TaggedDerValue},
|
||||
DERWriter, DERWriterSeq,
|
||||
};
|
||||
|
||||
fn write_oids(writer: &mut DERWriterSeq, oids: &[Option<&ObjectIdentifier>]) {
|
||||
for oid in oids {
|
||||
match oid {
|
||||
Some(oid) => writer.next().write_oid(oid),
|
||||
None => writer.next().write_null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_private(
|
||||
oids: &[Option<&ObjectIdentifier>],
|
||||
body_writer: impl FnOnce(&mut DERWriterSeq),
|
||||
) -> Vec<u8> {
|
||||
yasna::construct_der(|writer| {
|
||||
writer.write_sequence(|writer| {
|
||||
writer.next().write_i8(0); // version
|
||||
writer
|
||||
.next()
|
||||
.write_sequence(|writer| write_oids(writer, oids));
|
||||
|
||||
let body = yasna::construct_der(|writer| writer.write_sequence(body_writer));
|
||||
writer
|
||||
.next()
|
||||
.write_tagged_der(&TaggedDerValue::from_octetstring(body));
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_public(
|
||||
oids: &[Option<&ObjectIdentifier>],
|
||||
body_writer: impl FnOnce(DERWriter),
|
||||
) -> Vec<u8> {
|
||||
yasna::construct_der(|writer| {
|
||||
writer.write_sequence(|writer| {
|
||||
writer
|
||||
.next()
|
||||
.write_sequence(|writer| write_oids(writer, oids));
|
||||
body_writer(writer.next());
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user