use chrono::{NaiveDate, NaiveDateTime, TimeZone, Utc};
use openssl::asn1::Asn1Time;
use std::fs::{File, create_dir_all};
use std::io::Write;
use std::path::Path;
pub trait X509Parts {
fn get_pem(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
fn get_private_key(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
fn pem_extension(&self) -> &'static str;
}
pub trait X509Common {
fn save<P: AsRef<Path>, F: AsRef<Path>>(
&self,
path: P,
filename: F,
) -> Result<(), Box<dyn std::error::Error>>;
}
impl<T: X509Parts> X509Common for T {
fn save<P: AsRef<Path>, F: AsRef<Path>>(
&self,
path: P,
filename: F,
) -> Result<(), Box<dyn std::error::Error>> {
create_dir_all(&path)?;
let os_file = filename
.as_ref()
.file_name()
.ok_or("Failed to extract file name")?;
let write_file = |suffix: &str, content: &[u8]| -> Result<(), Box<dyn std::error::Error>> {
let mut new_name = os_file.to_os_string();
new_name.push(suffix);
let full_path = path.as_ref().join(new_name);
let mut file = File::create(full_path)?;
file.write_all(content)?;
Ok(())
};
if let Ok(ref key) = self.get_private_key() {
write_file("_pkey.pem", key)?;
}
write_file(self.pem_extension(), &self.get_pem()?)?;
Ok(())
}
}
pub(crate) fn create_asn1_time_from_date(
date_str: &str,
) -> Result<Asn1Time, Box<dyn std::error::Error>> {
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
let datetime = NaiveDateTime::new(date, chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap());
let utc_datetime = Utc.from_utc_datetime(&datetime);
let asn1_time_str = utc_datetime.format("%Y%m%d%H%M%SZ").to_string();
let asn1_time = Asn1Time::from_str(&asn1_time_str)?;
Ok(asn1_time)
}