diff --git a/src/certs.rs b/src/certs.rs index 35fd385..b7ba444 100644 --- a/src/certs.rs +++ b/src/certs.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result}; use std::{path::PathBuf, sync::Arc}; use crate::common::{is_file_exist, read_file, write_file, AppConfig, OpenSSLProviderArg, VarsMap}; -use crate::crypto::{ICryptoProvider, OpenSSLBinProvider, OpenSSLLibProvider}; +use crate::crypto::{ICryptoProvider, OpenSSLExternalProvider, OpenSSLInternalProvider}; pub(crate) struct Certs where @@ -93,14 +93,14 @@ pub async fn build_client_config(config: &AppConfig, vars: VarsMap) -> Result<() let created: bool; if let OpenSSLProviderArg::ExternalBin(_) = config.openssl { - let certs = Certs::new(config, OpenSSLBinProvider::from_cfg(config, vars)); + let certs = Certs::new(config, OpenSSLExternalProvider::from_cfg(config, vars)); created = certs .build_client_config() .await .context("external openssl error")?; result_file = certs.config_file; } else { - let certs = Certs::new(config, OpenSSLLibProvider::from_cfg(config, vars)); + let certs = Certs::new(config, OpenSSLInternalProvider::from_cfg(config, vars)); created = certs .build_client_config() .await diff --git a/src/crypto.rs b/src/crypto.rs index 3f92228..2e6174a 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -8,10 +8,10 @@ use openssl::{ stack::Stack, x509::{ extension::{ExtendedKeyUsage, KeyUsage, SubjectAlternativeName}, - X509Name, X509NameBuilder, X509Req, X509ReqBuilder, X509, + X509Extension, X509Name, X509NameBuilder, X509Req, X509ReqBuilder, X509, }, }; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use tokio::{fs, process::Command}; @@ -35,6 +35,42 @@ lazy_static! { }; } +trait ToPemX { + fn to_pem_x(&self) -> Result>; +} + +impl ToPemX for X509 { + fn to_pem_x(&self) -> Result> { + Ok(self.to_pem()?) + } +} + +impl ToPemX for X509Req { + fn to_pem_x(&self) -> Result> { + Ok(self.to_pem()?) + } +} + +impl ToPemX for PKey { + fn to_pem_x(&self) -> Result> { + Ok(self.private_key_to_pem_pkcs8()?) + } +} + +struct Pem<'a, T: ToPemX>(&'a T); + +trait WritePem { + async fn write>(&self, path: T) -> Result<()>; +} + +impl<'a, P: ToPemX> WritePem for Pem<'a, P> { + async fn write>(&self, path: T) -> Result<()> { + let pem = self.0.to_pem_x().context("to_pem()")?; + fs::write(path, pem).await.context("write pem")?; + Ok(()) + } +} + fn get_time_str_x509(days: u32) -> Result { let dt = Utc::now(); let dt = dt @@ -57,7 +93,7 @@ pub(crate) trait ICryptoProvider { async fn sign(&self) -> Result<()>; } -pub(crate) struct OpenSSLBinProvider { +pub(crate) struct OpenSSLExternalProvider { vars: VarsMap, base_dir: PathBuf, openssl_cnf: PathBuf, @@ -69,7 +105,7 @@ pub(crate) struct OpenSSLBinProvider { req_days: u32, } -impl OpenSSLBinProvider { +impl OpenSSLExternalProvider { async fn is_ca_exists(&self) -> bool { is_file_exist(&self.ca_file).await } @@ -114,7 +150,7 @@ impl OpenSSLBinProvider { } } -impl ICryptoProvider for OpenSSLBinProvider { +impl ICryptoProvider for OpenSSLExternalProvider { async fn request(&self) -> Result<()> { if self.is_req_exists().await { return Ok(()); @@ -188,7 +224,7 @@ impl ICryptoProvider for OpenSSLBinProvider { } } -pub(crate) struct OpenSSLLibProvider { +pub(crate) struct OpenSSLInternalProvider { vars: VarsMap, #[allow(unused)] base_dir: PathBuf, @@ -204,7 +240,7 @@ pub(crate) struct OpenSSLLibProvider { encoding: String, } -impl OpenSSLLibProvider { +impl OpenSSLInternalProvider { async fn is_ca_exists(&self) -> bool { is_file_exist(&self.ca_file).await } @@ -268,9 +304,9 @@ impl OpenSSLLibProvider { Ok(X509::from_pem(text.as_bytes())?) } - async fn get_ca_key(&self) -> Result> { + async fn get_ca_key(&self) -> Result> { let text = read_file(self.ca_key_file.clone(), &self.encoding).await?; - Ok(Rsa::private_key_from_pem(text.as_bytes())?) + Ok(PKey::from_rsa(Rsa::private_key_from_pem(text.as_bytes())?)?) } async fn get_key(&self) -> Result<(Rsa, PKey)> { @@ -290,8 +326,10 @@ impl OpenSSLLibProvider { self.get_key().await } else { let (rsa, pkey) = self.generate_key_pair()?; - let key_data = pkey.private_key_to_pem_pkcs8()?; - tokio::fs::write(self.key_file.as_path(), key_data).await?; + Pem(&pkey) + .write(&self.key_file) + .await + .context("key write pem")?; Ok((rsa, pkey)) } } @@ -308,9 +346,42 @@ impl OpenSSLLibProvider { } Ok(name_builder.build()) } + + fn gen_x509_extensions( + context: &openssl::x509::X509v3Context, + vars: &VarsMap, + ) -> Result> { + let key_usage = KeyUsage::new() + .key_agreement() + .digital_signature() + .build()?; + let key_extended_ext = ExtendedKeyUsage::new().client_auth().build()?; + + let mut san_extension = SubjectAlternativeName::new(); + if let Some(name) = vars.get("KEY_NAME") { + san_extension.dns(name); + } + if let Some(email) = vars.get("KEY_EMAIL") { + san_extension.email(email); + } + let san_ext = san_extension.build(context).context("build san")?; + + Ok(vec![san_ext, key_usage, key_extended_ext]) + } + + fn gen_x509_extensions_stack( + context: &openssl::x509::X509v3Context, + vars: &VarsMap, + ) -> Result> { + let mut stack = Stack::new()?; + for extension in Self::gen_x509_extensions(context, vars)?.into_iter() { + stack.push(extension).context("push ext")?; + } + Ok(stack) + } } -impl ICryptoProvider for OpenSSLLibProvider { +impl ICryptoProvider for OpenSSLInternalProvider { async fn request(&self) -> Result<()> { if self.is_req_exists().await { return Ok(()); @@ -326,44 +397,25 @@ impl ICryptoProvider for OpenSSLLibProvider { let (_, pkey) = self.ensure_key().await?; let name = self.build_x509_name()?; - let key_usage = KeyUsage::new() - .key_agreement() - .digital_signature() - .build()?; - let key_extended_ext = ExtendedKeyUsage::new().client_auth().build()?; - let mut st = Stack::new()?; - st.push(key_usage).context("push key usage")?; - st.push(key_extended_ext).context("push key_extended_ext")?; - let conf = Conf::new(ConfMethod::default()).context("conf new")?; // Create certificate signing request (CSR) let mut csr_builder = X509ReqBuilder::new()?; - csr_builder.set_pubkey(&pkey).context("set pubkey")?; csr_builder.set_version(2).context("set version")?; - - let context = csr_builder.x509v3_context(Some(&conf)); - - let mut san_extension = SubjectAlternativeName::new(); - if let Some(name) = self.vars.get("KEY_NAME") { - san_extension.dns(name); - } - if let Some(email) = self.vars.get("KEY_EMAIL") { - san_extension.email(email); - } - let san_ext = san_extension.build(&context).context("build san")?; - - st.push(san_ext).context("push san")?; - + csr_builder.set_pubkey(&pkey).context("set pubkey")?; csr_builder .set_subject_name(&name) .context("set subject name")?; - csr_builder.add_extensions(&st)?; + let context = csr_builder.x509v3_context(Some(&conf)); + let extensions = Self::gen_x509_extensions_stack(&context, &self.vars)?; + csr_builder.add_extensions(&extensions)?; csr_builder.sign(&pkey, MessageDigest::sha512())?; let csr = csr_builder.build(); - let pem = csr.to_pem()?; - fs::write(self.req_file.as_path(), pem).await?; + Pem(&csr) + .write(&self.req_file) + .await + .context("req write pem")?; Ok(()) } @@ -388,22 +440,11 @@ impl ICryptoProvider for OpenSSLLibProvider { let ca_key = self.get_ca_key().await?; let ca_cert = self.get_ca_cert().await?; - let ca_pkey = PKey::from_rsa(ca_key.clone())?; let req = self.get_req().await?; let pub_key = req.public_key()?; let subject_name = req.subject_name(); - let key_usage = KeyUsage::new() - .key_agreement() - .digital_signature() - .build() - .context("new key_usage")?; - let key_extended_ext = ExtendedKeyUsage::new() - .client_auth() - .build() - .context("new key_extended_ext")?; - let mut builder = openssl::x509::X509Builder::new().context("new builder")?; let not_before = Asn1Time::days_from_now(0).context("days_from_now 0")?; let na_s = get_time_str_x509(self.req_days).context("na_s get_time_str_x509")?; @@ -423,36 +464,19 @@ impl ICryptoProvider for OpenSSLLibProvider { .context("set_subject_name")?; let context = builder.x509v3_context(Some(&ca_cert), None); - - let mut san_extension = SubjectAlternativeName::new(); - if let Some(name) = self.vars.get("KEY_NAME") { - san_extension.dns(name); + for extension in Self::gen_x509_extensions(&context, &self.vars)? { + builder.append_extension(extension).context("append ext")?; } - if let Some(email) = self.vars.get("KEY_EMAIL") { - san_extension.email(email); - } - let san_ext = san_extension.build(&context).context("build san")?; builder - .append_extension(san_ext) - .context("append san ext")?; - builder - .append_extension(key_usage) - .context("append key_usage ext")?; - builder - .append_extension(key_extended_ext) - .context("append key_extended_ext ext")?; - - builder - .sign(&ca_pkey, MessageDigest::sha512()) + .sign(&ca_key, MessageDigest::sha512()) .context("builder.sign")?; let cert = builder.build(); - let pem = cert.to_pem().context("cert.to_pem()")?; - fs::write(self.cert_file.as_path(), pem) + Pem(&cert) + .write(&self.cert_file) .await - .context("fs::write")?; - + .context("cert.to_pem()")?; Ok(()) } }