diff --git a/src/lib.rs b/src/lib.rs index 58a28b5..7e33683 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -236,7 +236,7 @@ impl Account { Ok(Self { inner: Arc::new(AccountInner { id, - key: Key::from_pkcs8_der(BASE64_URL_SAFE_NO_PAD.decode(key_pkcs8_der)?)?, + key: Key::from_pkcs8_der(key_pkcs8_der)?, client: Client::new(directory_url, http).await?, }), }) @@ -315,7 +315,7 @@ impl Account { let id = account_url.ok_or("failed to get account URL")?; let credentials = AccountCredentials { id: id.clone(), - key_pkcs8: BASE64_URL_SAFE_NO_PAD.encode(key_pkcs8.as_ref()), + key_pkcs8: key_pkcs8.as_ref().to_vec(), directory: Some(server_url.to_owned()), // We support deserializing URLs for compatibility with versions pre 0.4, // but we prefer to get fresh URLs from the `server_url` for newer credentials. @@ -377,7 +377,7 @@ impl AccountInner { ) -> Result { Ok(Self { id: credentials.id, - key: Key::from_pkcs8_der(BASE64_URL_SAFE_NO_PAD.decode(&credentials.key_pkcs8)?)?, + key: Key::from_pkcs8_der(credentials.key_pkcs8.as_ref())?, client: match (credentials.directory, credentials.urls) { (Some(server_url), _) => Client::new(&server_url, http).await?, (None, Some(urls)) => Client { http, urls }, @@ -522,8 +522,8 @@ impl Key { )) } - fn from_pkcs8_der(pkcs8_der: Vec) -> Result { - let key = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &pkcs8_der)?; + fn from_pkcs8_der(pkcs8_der: &[u8]) -> Result { + let key = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, pkcs8_der)?; let thumb = BASE64_URL_SAFE_NO_PAD.encode(Jwk::thumb_sha256(&key)?); Ok(Self { diff --git a/src/types.rs b/src/types.rs index 3ab588c..e19f94b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -54,11 +54,51 @@ impl From<&'static str> for Error { #[derive(Deserialize, Serialize)] pub struct AccountCredentials { pub(crate) id: String, - pub(crate) key_pkcs8: String, + /// Stored in DER, serialized as base64 + #[serde(with = "pkcs8_serde")] + pub(crate) key_pkcs8: Vec, pub(crate) directory: Option, pub(crate) urls: Option, } +mod pkcs8_serde { + use std::fmt; + + use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; + use serde::{de, Deserializer, Serializer}; + + pub(crate) fn serialize(key_pkcs8: &[u8], serializer: S) -> Result + where + S: Serializer, + { + let encoded = BASE64_URL_SAFE_NO_PAD.encode(key_pkcs8.as_ref()); + serializer.serialize_str(&encoded) + } + + pub(crate) fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a base64-encoded PKCS#8 private key") + } + + fn visit_str(self, v: &str) -> Result, E> + where + E: de::Error, + { + BASE64_URL_SAFE_NO_PAD.decode(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) + } +} + /// An RFC 7807 problem document as returned by the ACME server #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")]