save private key generated during acme handshake

main
Brian Picciano 1 year ago
parent 06cda77772
commit 209daacf1b
  1. 11
      TODO
  2. 3
      src/domain/acme.rs
  3. 6
      src/domain/acme/manager.rs
  4. 70
      src/domain/acme/store.rs

11
TODO

@ -0,0 +1,11 @@
- make acme store implement https://docs.rs/rustls/latest/rustls/server/trait.ResolvesServerCert.html
- pass that into https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html#
- turn that into a TlsAcceptor (From is implemented here:
https://docs.rs/tokio-rustls/latest/tokio_rustls/struct.TlsAcceptor.html#impl-From%3CArc%3CServerConfig%3E%3E-for-TlsAcceptor)
- use tls-listener crate to wrap hyper accepter: https://github.com/tmccombs/tls-listener/blob/main/examples/http.rs#L24
- https://github.com/tmccombs/tls-listener/blob/main/examples/tls_config/mod.rs
- logging

@ -1,5 +1,6 @@
pub mod manager;
pub mod store;
pub type AccountKey = openssl::pkey::PKey<openssl::pkey::Private>;
pub type PrivateKey = openssl::pkey::PKey<openssl::pkey::Private>;
pub type Certificate = openssl::x509::X509;

@ -84,7 +84,7 @@ where
// if there's an existing cert, and its expiry (determined by the soonest value of
// not_after amongst its parts) is later than 30 days from now, then we consider it to be
// synced.
if let Ok(cert) = self.store.get_certificate(domain.as_str()) {
if let Ok((_, cert)) = self.store.get_certificate(domain.as_str()) {
let thirty_days = openssl::asn1::Asn1Time::days_from_now(30)
.expect("parsed thirty days from now as Asn1Time");
@ -211,7 +211,7 @@ where
// Create a certificate signing request for the order, and request
// the certificate.
let order = order
.finalize(acme2::Csr::Automatic(pkey))
.finalize(acme2::Csr::Automatic(pkey.clone()))
.await
.map_unexpected()?;
@ -260,7 +260,7 @@ where
println!("certificate for {} successfully retrieved", domain.as_str());
self.store
.set_certificate(domain.as_str(), cert)
.set_certificate(domain.as_str(), &pkey, cert)
.map_unexpected()?;
Ok(())

@ -1,11 +1,12 @@
use std::io::{Read, Write};
use std::{fs, io, path, sync};
use crate::domain::acme::{AccountKey, Certificate};
use crate::domain::acme::{Certificate, PrivateKey};
use crate::error;
use crate::error::{MapUnexpected, ToUnexpected};
use hex::ToHex;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(thiserror::Error, Debug)]
@ -37,8 +38,8 @@ pub enum GetCertificateError {
#[mockall::automock]
pub trait Store {
fn set_account_key(&self, k: &AccountKey) -> Result<(), error::Unexpected>;
fn get_account_key(&self) -> Result<AccountKey, GetAccountKeyError>;
fn set_account_key(&self, k: &PrivateKey) -> Result<(), error::Unexpected>;
fn get_account_key(&self) -> Result<PrivateKey, GetAccountKeyError>;
fn set_http01_challenge_key(&self, token: &str, key: &str) -> Result<(), error::Unexpected>;
fn get_http01_challenge_key(&self, token: &str) -> Result<String, GetHttp01ChallengeKeyError>;
@ -47,15 +48,25 @@ pub trait Store {
fn set_certificate(
&self,
domain: &str,
key: &PrivateKey,
cert: Vec<Certificate>,
) -> Result<(), error::Unexpected>;
/// Returned vec is guaranteed to have len > 0
fn get_certificate(&self, domain: &str) -> Result<Vec<Certificate>, GetCertificateError>;
fn get_certificate(
&self,
domain: &str,
) -> Result<(PrivateKey, Vec<Certificate>), GetCertificateError>;
}
pub trait BoxedStore: Store + Send + Sync + Clone + 'static {}
#[derive(Debug, Serialize, Deserialize)]
struct StoredPKeyCert {
private_key_pem: String,
cert_pems: Vec<String>,
}
struct FSStore {
dir_path: path::PathBuf,
}
@ -86,21 +97,24 @@ impl FSStore {
}
fn certificate_path(&self, domain: &str) -> path::PathBuf {
self.dir_path.join("certificates").join(domain)
self.dir_path
.join("certificates")
.join(domain)
.with_extension("json")
}
}
impl BoxedStore for sync::Arc<FSStore> {}
impl Store for sync::Arc<FSStore> {
fn set_account_key(&self, k: &AccountKey) -> Result<(), error::Unexpected> {
fn set_account_key(&self, k: &PrivateKey) -> Result<(), error::Unexpected> {
let mut file = fs::File::create(self.account_key_path()).map_unexpected()?;
let pem = k.private_key_to_pem_pkcs8().map_unexpected()?;
file.write_all(&pem).map_unexpected()?;
Ok(())
}
fn get_account_key(&self) -> Result<AccountKey, GetAccountKeyError> {
fn get_account_key(&self) -> Result<PrivateKey, GetAccountKeyError> {
let mut file = fs::File::open(self.account_key_path()).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => GetAccountKeyError::NotFound,
_ => e.to_unexpected().into(),
@ -109,7 +123,7 @@ impl Store for sync::Arc<FSStore> {
let mut pem = Vec::<u8>::new();
file.read_to_end(&mut pem).map_unexpected()?;
let k = AccountKey::private_key_from_pem(&pem).map_unexpected()?;
let k = PrivateKey::private_key_from_pem(&pem).map_unexpected()?;
Ok(k)
}
@ -140,38 +154,50 @@ impl Store for sync::Arc<FSStore> {
fn set_certificate(
&self,
domain: &str,
key: &PrivateKey,
cert: Vec<Certificate>,
) -> Result<(), error::Unexpected> {
let cert: Vec<String> = cert
.into_iter()
.map(|cert| {
let cert_pem = cert.to_pem().map_unexpected()?;
let cert_pem = String::from_utf8(cert_pem).map_unexpected()?;
Ok::<String, error::Unexpected>(cert_pem)
})
.try_collect()?;
let to_store = StoredPKeyCert {
private_key_pem: String::from_utf8(key.private_key_to_pem_pkcs8().map_unexpected()?)
.map_unexpected()?,
cert_pems: cert
.into_iter()
.map(|cert| {
let cert_pem = cert.to_pem().map_unexpected()?;
let cert_pem = String::from_utf8(cert_pem).map_unexpected()?;
Ok::<String, error::Unexpected>(cert_pem)
})
.try_collect()?,
};
let cert_file = fs::File::create(self.certificate_path(domain)).map_unexpected()?;
serde_json::to_writer(cert_file, &cert).map_unexpected()?;
serde_json::to_writer(cert_file, &to_store).map_unexpected()?;
Ok(())
}
fn get_certificate(&self, domain: &str) -> Result<Vec<Certificate>, GetCertificateError> {
fn get_certificate(
&self,
domain: &str,
) -> Result<(PrivateKey, Vec<Certificate>), GetCertificateError> {
let file = fs::File::open(self.certificate_path(domain)).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => GetCertificateError::NotFound,
_ => e.to_unexpected().into(),
})?;
let cert: Vec<String> = serde_json::from_reader(file).map_unexpected()?;
let stored: StoredPKeyCert = serde_json::from_reader(file).map_unexpected()?;
let key =
PrivateKey::private_key_from_pem(stored.private_key_pem.as_bytes()).map_unexpected()?;
let cert: Vec<Certificate> = cert
let cert: Vec<Certificate> = stored
.cert_pems
.into_iter()
.map(|cert| openssl::x509::X509::from_pem(cert.as_bytes()).map_unexpected())
.try_collect()?;
Ok(cert)
Ok((key, cert))
}
}
@ -187,7 +213,7 @@ mod tests {
assert!(matches!(
store.get_account_key(),
Err::<AccountKey, GetAccountKeyError>(GetAccountKeyError::NotFound)
Err::<PrivateKey, GetAccountKeyError>(GetAccountKeyError::NotFound)
));
let k = acme2::gen_rsa_private_key(4096).expect("private key generated");

Loading…
Cancel
Save