use lazy_static::lazy_static;
use openssl::{
ec::{Asn1Flag, EcGroup, EcKey},
hash::MessageDigest,
nid::Nid,
pkey::{self, PKey},
rsa::Rsa,
stack::Stack,
x509::{extension::SubjectAlternativeName, X509Req, X509ReqBuilder, X509},
};
use crate::error;
lazy_static! {
pub(crate) static ref EC_GROUP_P256: EcGroup = ec_group(Nid::X9_62_PRIME256V1);
pub(crate) static ref EC_GROUP_P384: EcGroup = ec_group(Nid::SECP384R1);
}
fn ec_group(nid: Nid) -> EcGroup {
let mut g = EcGroup::from_curve_name(nid).expect("EcGroup");
g.set_asn1_flag(Asn1Flag::NAMED_CURVE);
g
}
pub fn create_rsa_key(bits: u32) -> Result<PKey<pkey::Private>, error::Error> {
let pri_key_rsa = Rsa::generate(bits)?;
let pkey = PKey::from_rsa(pri_key_rsa)?;
Ok(pkey)
}
pub fn create_p256_key() -> Result<PKey<pkey::Private>, error::Error> {
let pri_key_ec = EcKey::generate(&*EC_GROUP_P256)?;
let pkey = PKey::from_ec_key(pri_key_ec)?;
Ok(pkey)
}
pub fn create_p384_key() -> Result<PKey<pkey::Private>, error::Error> {
let pri_key_ec = EcKey::generate(&*EC_GROUP_P384)?;
let pkey = PKey::from_ec_key(pri_key_ec)?;
Ok(pkey)
}
pub(crate) fn create_csr(
pkey: &PKey<pkey::Private>,
domains: &[&str],
) -> Result<X509Req, error::Error> {
let mut req_bld = X509ReqBuilder::new()?;
req_bld.set_pubkey(&pkey)?;
let mut stack = Stack::new()?;
let ctx = req_bld.x509v3_context(None);
let as_lst = domains
.iter()
.map(|&e| format!("DNS:{}", e))
.collect::<Vec<_>>()
.join(", ");
let as_lst = as_lst[4..].to_string();
let mut an = SubjectAlternativeName::new();
an.dns(&as_lst);
let ext = an.build(&ctx)?;
stack.push(ext).expect("Stack::push");
req_bld.add_extensions(&stack)?;
req_bld.sign(pkey, MessageDigest::sha256())?;
Ok(req_bld.build())
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Certificate {
private_key: String,
certificate: String,
}
impl Certificate {
pub(crate) fn new(private_key: String, certificate: String) -> Self {
Certificate {
private_key,
certificate,
}
}
pub fn parse(private_key: String, certificate: String) -> Result<Self, error::Error> {
X509::from_pem(certificate.as_bytes())?;
PKey::private_key_from_pem(private_key.as_bytes())?;
Ok(Certificate {
private_key,
certificate,
})
}
pub fn private_key(&self) -> &str {
&self.private_key
}
pub fn private_key_der(&self) -> Result<Vec<u8>, error::Error> {
let pkey = PKey::private_key_from_pem(self.private_key.as_bytes())?;
let der = pkey.private_key_to_der()?;
Ok(der)
}
pub fn certificate(&self) -> &str {
&self.certificate
}
pub fn certificate_der(&self) -> Result<Vec<u8>, error::Error> {
let x509 = X509::from_pem(self.certificate.as_bytes())?;
let der = x509.to_der()?;
Ok(der)
}
pub fn expiry(&self) -> Result<chrono::DateTime<chrono::Utc>, error::Error> {
if cfg!(test) {
return Ok(parse_date("May 15 11:11:11 2015 GMT")?);
}
let x509 = X509::from_pem(self.certificate.as_bytes())?;
let not_after = format!("{}", x509.not_after());
let expires = parse_date(¬_after)?;
Ok(expires)
}
}
fn parse_date(s: &str) -> Result<chrono::DateTime<chrono::Utc>, error::Error> {
debug!("Parse date/time: {}", s);
let ndt = chrono::NaiveDateTime::parse_from_str(s, "%h %e %H:%M:%S %Y GMT")?;
Ok(chrono::DateTime::<chrono::Utc>::from_utc(ndt, chrono::Utc))
}