This commit is contained in:
Pierre HUBERT 2024-06-28 19:19:17 +02:00
parent 8bac181552
commit 32d5707055
6 changed files with 132 additions and 4 deletions

View File

@ -106,9 +106,12 @@ dependencies = [
"asn1",
"clap",
"env_logger",
"foreign-types-shared",
"lazy_static",
"libc",
"log",
"openssl",
"openssl-sys",
"thiserror",
]

View File

@ -11,4 +11,7 @@ clap = { version = "4.5.7", features = ["derive", "env"] }
anyhow = "1.0.86"
thiserror = "1.0.61"
openssl = { version = "0.10.64" }
openssl-sys = "0.9.102"
libc = "0.2.155"
foreign-types-shared = "0.1.1"
asn1 = "0.16"

View File

@ -1,2 +1,3 @@
pub mod crl_extension;
pub mod openssl_utils;
pub mod pki;

View File

@ -0,0 +1,24 @@
use openssl::asn1::{Asn1Time, Asn1TimeRef};
/// Clone Asn1 time
pub fn clone_asn1_time(time: &Asn1TimeRef) -> anyhow::Result<Asn1Time> {
let diff = time.diff(Asn1Time::from_unix(0)?.as_ref())?;
let days = diff.days.abs();
let secs = diff.secs.abs();
Ok(Asn1Time::from_unix((days * 3600 * 24 + secs) as i64)?)
}
#[cfg(test)]
mod test {
use crate::crypto::openssl_utils::clone_asn1_time;
use openssl::asn1::Asn1Time;
use std::cmp::Ordering;
#[test]
fn test_clone_asn1_time() {
let a = Asn1Time::from_unix(10).unwrap();
let b = clone_asn1_time(a.as_ref()).unwrap();
assert_eq!(a.compare(&b).unwrap(), Ordering::Equal);
}
}

View File

@ -1,5 +1,9 @@
use crate::app_config::AppConfig;
use crate::crypto::crl_extension::CRLDistributionPointExt;
use std::cmp::Ordering;
use std::path::{Path, PathBuf};
use foreign_types_shared::ForeignType;
use foreign_types_shared::ForeignTypeRef;
use libc::c_long;
use openssl::asn1::Asn1Time;
use openssl::bn::{BigNum, MsbOption};
use openssl::ec::EcGroup;
@ -7,8 +11,22 @@ use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{PKey, Private};
use openssl::x509::extension::{BasicConstraints, KeyUsage, SubjectKeyIdentifier};
use openssl::x509::{X509NameBuilder, X509};
use std::path::{Path, PathBuf};
use openssl::x509::{ReasonCode, X509Crl, X509NameBuilder, X509};
use openssl_sys::{X509_CRL_free, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate, X509_CRL_set_issuer_name, X509_CRL_set_version};
use crate::app_config::AppConfig;
use crate::crypto::crl_extension::CRLDistributionPointExt;
use crate::crypto::openssl_utils::clone_asn1_time;
#[derive(thiserror::Error, Debug)]
pub enum PKIError {
#[error("Certification Authority does not have a CRL")]
MissingCRL,
#[error("Certification Authority does not have a CRL next update time")]
MissingCRLNextUpdate,
#[error("Failed to initialize CRL! {0}")]
GenCRLError(&'static str),
}
/// Certificate and private key
struct CertData {
@ -48,6 +66,11 @@ fn load_certificate_from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<X509> {
Ok(X509::from_pem(&std::fs::read(path)?)?)
}
/// Load CRL from PEM file
fn load_crl_from_file<P: AsRef<Path>>(path: P) -> anyhow::Result<X509Crl> {
Ok(X509Crl::from_pem(&std::fs::read(path)?)?)
}
#[derive(Default)]
struct GenCertificateReq<'a> {
cn: &'a str,
@ -202,3 +225,72 @@ pub fn initialize_devices_ca() -> anyhow::Result<()> {
Ok(())
}
/// Intialize or refresh a CRL
fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
let crl_path = d.crl.as_ref().ok_or(PKIError::MissingCRL)?;
let old_list = if crl_path.exists() {
let crl = load_crl_from_file(crl_path)?;
// Check if revocation is un-needed
let next_update = crl.next_update().ok_or(PKIError::MissingCRLNextUpdate)?;
if next_update.compare(Asn1Time::days_from_now(0)?.as_ref())? < Ordering::Greater {
return Ok(());
}
match crl.get_revoked() {
Some(l) => Some(
l.iter()
.map(|r| {
Ok((
r.serial_number().to_owned()?,
clone_asn1_time(r.revocation_date())?,
r.extension::<ReasonCode>()?,
))
})
.collect::<anyhow::Result<Vec<_>>>()?,
),
None => None,
}
} else {
None
};
log::info!("Generating a new CRL...");
// based on https://github.com/openssl/openssl/blob/master/crypto/x509/x509_vfy.c
unsafe {
let crl = openssl_sys::X509_CRL_new();
if crl.is_null() {
return Err(PKIError::GenCRLError("Could not construct CRL!").into());
}
const X509_CRL_VERSION_2: c_long = 1;
if X509_CRL_set_version(crl, X509_CRL_VERSION_2) == 0 {
return Err(PKIError::GenCRLError("X509_CRL_set_version").into());
}
if X509_CRL_set_issuer_name(crl, d.cert.issuer_name().as_ptr()) == 0 {
return Err(PKIError::GenCRLError("X509_CRL_set_issuer_name").into());
}
let last_update = Asn1Time::days_from_now(0)?;
if X509_CRL_set1_lastUpdate(crl, last_update.as_ptr()) == 0 {
return Err(PKIError::GenCRLError("X509_CRL_set1_lastUpdate").into());
}
let next_update = Asn1Time::days_from_now(10)?;
if X509_CRL_set1_nextUpdate(crl, next_update.as_ptr()) == 0 {
return Err(PKIError::GenCRLError("X509_CRL_set1_nextUpdate").into());
}
X509_CRL_free(crl);
}
Ok(())
}
/// Initialize or refresh Root CA CRL, if needed
pub fn initialize_root_ca_crl() -> anyhow::Result<()> {
refresh_crl(&CertData::load_root_ca()?)
}

View File

@ -3,6 +3,9 @@ use central_backend::crypto::pki;
use central_backend::utils::files_utils::create_directory_if_missing;
fn main() {
// Initialize OpenSSL
openssl_sys::init();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
// Initialize storage
@ -12,4 +15,6 @@ fn main() {
pki::initialize_root_ca().expect("Failed to initialize Root CA!");
pki::initialize_web_ca().expect("Failed to initialize web CA!");
pki::initialize_devices_ca().expect("Failed to initialize devices CA!");
pki::initialize_root_ca_crl().expect("Failed to initialize Root CA!");
}