This commit is contained in:
Dmitry Belyaev 2024-10-22 15:45:36 +03:00
parent fa9c1ecb2c
commit c7677bdb70
Signed by: b4tman
GPG Key ID: 41A00BF15EA7E5F3
2 changed files with 100 additions and 76 deletions

View File

@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result};
use std::{path::PathBuf, sync::Arc}; use std::{path::PathBuf, sync::Arc};
use crate::common::{is_file_exist, read_file, write_file, AppConfig, OpenSSLProviderArg, VarsMap}; 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<T> pub(crate) struct Certs<T>
where where
@ -93,14 +93,14 @@ pub async fn build_client_config(config: &AppConfig, vars: VarsMap) -> Result<()
let created: bool; let created: bool;
if let OpenSSLProviderArg::ExternalBin(_) = config.openssl { 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 created = certs
.build_client_config() .build_client_config()
.await .await
.context("external openssl error")?; .context("external openssl error")?;
result_file = certs.config_file; result_file = certs.config_file;
} else { } else {
let certs = Certs::new(config, OpenSSLLibProvider::from_cfg(config, vars)); let certs = Certs::new(config, OpenSSLInternalProvider::from_cfg(config, vars));
created = certs created = certs
.build_client_config() .build_client_config()
.await .await

View File

@ -8,10 +8,10 @@ use openssl::{
stack::Stack, stack::Stack,
x509::{ x509::{
extension::{ExtendedKeyUsage, KeyUsage, SubjectAlternativeName}, 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}; use tokio::{fs, process::Command};
@ -35,6 +35,42 @@ lazy_static! {
}; };
} }
trait ToPemX {
fn to_pem_x(&self) -> Result<Vec<u8>>;
}
impl ToPemX for X509 {
fn to_pem_x(&self) -> Result<Vec<u8>> {
Ok(self.to_pem()?)
}
}
impl ToPemX for X509Req {
fn to_pem_x(&self) -> Result<Vec<u8>> {
Ok(self.to_pem()?)
}
}
impl ToPemX for PKey<Private> {
fn to_pem_x(&self) -> Result<Vec<u8>> {
Ok(self.private_key_to_pem_pkcs8()?)
}
}
struct Pem<'a, T: ToPemX>(&'a T);
trait WritePem {
async fn write<T: AsRef<Path>>(&self, path: T) -> Result<()>;
}
impl<'a, P: ToPemX> WritePem for Pem<'a, P> {
async fn write<T: AsRef<Path>>(&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<String> { fn get_time_str_x509(days: u32) -> Result<String> {
let dt = Utc::now(); let dt = Utc::now();
let dt = dt let dt = dt
@ -57,7 +93,7 @@ pub(crate) trait ICryptoProvider {
async fn sign(&self) -> Result<()>; async fn sign(&self) -> Result<()>;
} }
pub(crate) struct OpenSSLBinProvider { pub(crate) struct OpenSSLExternalProvider {
vars: VarsMap, vars: VarsMap,
base_dir: PathBuf, base_dir: PathBuf,
openssl_cnf: PathBuf, openssl_cnf: PathBuf,
@ -69,7 +105,7 @@ pub(crate) struct OpenSSLBinProvider {
req_days: u32, req_days: u32,
} }
impl OpenSSLBinProvider { impl OpenSSLExternalProvider {
async fn is_ca_exists(&self) -> bool { async fn is_ca_exists(&self) -> bool {
is_file_exist(&self.ca_file).await 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<()> { async fn request(&self) -> Result<()> {
if self.is_req_exists().await { if self.is_req_exists().await {
return Ok(()); return Ok(());
@ -188,7 +224,7 @@ impl ICryptoProvider for OpenSSLBinProvider {
} }
} }
pub(crate) struct OpenSSLLibProvider { pub(crate) struct OpenSSLInternalProvider {
vars: VarsMap, vars: VarsMap,
#[allow(unused)] #[allow(unused)]
base_dir: PathBuf, base_dir: PathBuf,
@ -204,7 +240,7 @@ pub(crate) struct OpenSSLLibProvider {
encoding: String, encoding: String,
} }
impl OpenSSLLibProvider { impl OpenSSLInternalProvider {
async fn is_ca_exists(&self) -> bool { async fn is_ca_exists(&self) -> bool {
is_file_exist(&self.ca_file).await is_file_exist(&self.ca_file).await
} }
@ -268,9 +304,9 @@ impl OpenSSLLibProvider {
Ok(X509::from_pem(text.as_bytes())?) Ok(X509::from_pem(text.as_bytes())?)
} }
async fn get_ca_key(&self) -> Result<Rsa<Private>> { async fn get_ca_key(&self) -> Result<PKey<Private>> {
let text = read_file(self.ca_key_file.clone(), &self.encoding).await?; 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<Private>, PKey<Private>)> { async fn get_key(&self) -> Result<(Rsa<Private>, PKey<Private>)> {
@ -290,8 +326,10 @@ impl OpenSSLLibProvider {
self.get_key().await self.get_key().await
} else { } else {
let (rsa, pkey) = self.generate_key_pair()?; let (rsa, pkey) = self.generate_key_pair()?;
let key_data = pkey.private_key_to_pem_pkcs8()?; Pem(&pkey)
tokio::fs::write(self.key_file.as_path(), key_data).await?; .write(&self.key_file)
.await
.context("key write pem")?;
Ok((rsa, pkey)) Ok((rsa, pkey))
} }
} }
@ -308,9 +346,42 @@ impl OpenSSLLibProvider {
} }
Ok(name_builder.build()) Ok(name_builder.build())
} }
fn gen_x509_extensions(
context: &openssl::x509::X509v3Context,
vars: &VarsMap,
) -> Result<Vec<X509Extension>> {
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<Stack<X509Extension>> {
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<()> { async fn request(&self) -> Result<()> {
if self.is_req_exists().await { if self.is_req_exists().await {
return Ok(()); return Ok(());
@ -326,44 +397,25 @@ impl ICryptoProvider for OpenSSLLibProvider {
let (_, pkey) = self.ensure_key().await?; let (_, pkey) = self.ensure_key().await?;
let name = self.build_x509_name()?; 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")?; let conf = Conf::new(ConfMethod::default()).context("conf new")?;
// Create certificate signing request (CSR) // Create certificate signing request (CSR)
let mut csr_builder = X509ReqBuilder::new()?; let mut csr_builder = X509ReqBuilder::new()?;
csr_builder.set_pubkey(&pkey).context("set pubkey")?;
csr_builder.set_version(2).context("set version")?; csr_builder.set_version(2).context("set version")?;
csr_builder.set_pubkey(&pkey).context("set pubkey")?;
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 csr_builder
.set_subject_name(&name) .set_subject_name(&name)
.context("set subject 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())?; csr_builder.sign(&pkey, MessageDigest::sha512())?;
let csr = csr_builder.build(); 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(()) Ok(())
} }
@ -388,22 +440,11 @@ impl ICryptoProvider for OpenSSLLibProvider {
let ca_key = self.get_ca_key().await?; let ca_key = self.get_ca_key().await?;
let ca_cert = self.get_ca_cert().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 req = self.get_req().await?;
let pub_key = req.public_key()?; let pub_key = req.public_key()?;
let subject_name = req.subject_name(); 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 mut builder = openssl::x509::X509Builder::new().context("new builder")?;
let not_before = Asn1Time::days_from_now(0).context("days_from_now 0")?; 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")?; 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")?; .context("set_subject_name")?;
let context = builder.x509v3_context(Some(&ca_cert), None); let context = builder.x509v3_context(Some(&ca_cert), None);
for extension in Self::gen_x509_extensions(&context, &self.vars)? {
let mut san_extension = SubjectAlternativeName::new(); builder.append_extension(extension).context("append ext")?;
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")?;
builder builder
.append_extension(san_ext) .sign(&ca_key, MessageDigest::sha512())
.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())
.context("builder.sign")?; .context("builder.sign")?;
let cert = builder.build(); let cert = builder.build();
let pem = cert.to_pem().context("cert.to_pem()")?; Pem(&cert)
fs::write(self.cert_file.as_path(), pem) .write(&self.cert_file)
.await .await
.context("fs::write")?; .context("cert.to_pem()")?;
Ok(()) Ok(())
} }
} }