From 717ad5b5e01e472f73031be7154218a7ff905f2f Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Wed, 17 Jul 2024 18:31:57 +0200 Subject: [PATCH] Can revoke issued certificates --- central_backend/src/crypto/pki.rs | 72 ++++++++++++++++++--- central_backend/src/devices/devices_list.rs | 24 ++++++- central_backend/src/main.rs | 2 + 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/central_backend/src/crypto/pki.rs b/central_backend/src/crypto/pki.rs index 66ae61a..b8d8562 100644 --- a/central_backend/src/crypto/pki.rs +++ b/central_backend/src/crypto/pki.rs @@ -13,10 +13,11 @@ use openssl::pkey::{PKey, Private}; use openssl::x509::extension::{ BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier, }; -use openssl::x509::{X509Crl, X509Name, X509NameBuilder, X509Req, X509}; +use openssl::x509::{CrlStatus, X509Crl, X509Name, X509NameBuilder, X509Req, X509}; use openssl_sys::{ - X509_CRL_add0_revoked, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate, + X509_CRL_add0_revoked, X509_CRL_new, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate, X509_CRL_set_issuer_name, X509_CRL_set_version, X509_CRL_sign, X509_REVOKED_dup, + X509_REVOKED_new, X509_REVOKED_set_revocationDate, X509_REVOKED_set_serialNumber, }; use crate::app_config::AppConfig; @@ -365,7 +366,7 @@ pub fn initialize_server_ca() -> anyhow::Result<()> { } /// Initialize or refresh a CRL -fn refresh_crl(d: &CertData) -> anyhow::Result<()> { +fn refresh_crl(d: &CertData, new_cert: Option<&X509>) -> anyhow::Result<()> { let crl_path = d.crl.as_ref().ok_or(PKIError::MissingCRL)?; let old_crl = if crl_path.exists() { @@ -373,7 +374,9 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> { // 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 { + if next_update.compare(Asn1Time::days_from_now(0)?.as_ref())? == Ordering::Greater + && new_cert.is_none() + { return Ok(()); } @@ -386,7 +389,7 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> { // based on https://github.com/openssl/openssl/blob/master/crypto/x509/x509_vfy.c unsafe { - let crl = openssl_sys::X509_CRL_new(); + let crl = X509_CRL_new(); if crl.is_null() { return Err(PKIError::GenCRLError("Could not construct CRL!").into()); } @@ -420,6 +423,31 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> { } } + // If requested, add new entry + if let Some(new_cert) = new_cert { + let entry = X509_REVOKED_new(); + if entry.is_null() { + return Err(PKIError::GenCRLError("X509_CRL_new for new entry").into()); + } + + if X509_REVOKED_set_serialNumber(entry, new_cert.serial_number().as_ptr()) == 0 { + return Err( + PKIError::GenCRLError("X509_REVOKED_set_serialNumber for new entry").into(), + ); + } + + let revocation_date = Asn1Time::days_from_now(0)?; + if X509_REVOKED_set_revocationDate(entry, revocation_date.as_ptr()) == 0 { + return Err( + PKIError::GenCRLError("X509_REVOKED_set_revocationDate for new entry").into(), + ); + } + + if X509_CRL_add0_revoked(crl, X509_REVOKED_dup(entry)) == 0 { + return Err(PKIError::GenCRLError("X509_CRL_add0_revoked for new entry").into()); + } + } + let md = MessageDigest::sha256(); if X509_CRL_sign(crl, d.key.as_ptr(), md.as_ptr()) == 0 { return Err(PKIError::GenCRLError("X509_CRL_sign").into()); @@ -434,9 +462,9 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> { /// Refresh revocation lists pub fn refresh_crls() -> anyhow::Result<()> { - refresh_crl(&CertData::load_root_ca()?)?; - refresh_crl(&CertData::load_web_ca()?)?; - refresh_crl(&CertData::load_devices_ca()?)?; + refresh_crl(&CertData::load_root_ca()?, None)?; + refresh_crl(&CertData::load_web_ca()?, None)?; + refresh_crl(&CertData::load_devices_ca()?, None)?; Ok(()) } @@ -451,3 +479,31 @@ pub fn gen_certificate_for_device(csr: &X509Req) -> anyhow::Result { Ok(String::from_utf8(cert)?) } + +/// Check if a certificate is revoked +fn is_revoked(cert: &X509, ca: &CertData) -> anyhow::Result { + let crl = X509Crl::from_pem(&std::fs::read( + ca.crl.as_ref().ok_or(PKIError::MissingCRL)?, + )?)?; + + let res = crl.get_by_cert(cert); + + Ok(matches!(res, CrlStatus::Revoked(_))) +} + +/// Revoke a certificate +pub fn revoke(cert: &X509, ca: &CertData) -> anyhow::Result<()> { + // Check if certificate is already revoked + if is_revoked(cert, ca)? { + // No op + return Ok(()); + } + + refresh_crl(ca, Some(cert))?; + Ok(()) +} + +/// Revoke a device certificate +pub fn revoke_device_cert(cert: &X509) -> anyhow::Result<()> { + revoke(cert, &CertData::load_devices_ca()?) +} diff --git a/central_backend/src/devices/devices_list.rs b/central_backend/src/devices/devices_list.rs index 0cbaa65..4dd96ca 100644 --- a/central_backend/src/devices/devices_list.rs +++ b/central_backend/src/devices/devices_list.rs @@ -2,7 +2,7 @@ use crate::app_config::AppConfig; use crate::crypto::pki; use crate::devices::device::{Device, DeviceId, DeviceInfo}; use crate::utils::time_utils::time_secs; -use openssl::x509::X509Req; +use openssl::x509::{X509Req, X509}; use std::collections::HashMap; #[derive(thiserror::Error, Debug)] @@ -15,6 +15,10 @@ pub enum DevicesListError { ValidateDeviceFailedDeviceNotFound, #[error("Validated device failed: the device is already validated!")] ValidateDeviceFailedDeviceAlreadyValidated, + #[error("Requested device was not found")] + DeviceNotFound, + #[error("Requested device is not validated")] + DeviceNotValidated, } pub struct DevicesList(HashMap); @@ -129,12 +133,26 @@ impl DevicesList { Ok(()) } + /// Get single certificate information + fn get_cert(&self, id: &DeviceId) -> anyhow::Result { + let dev = self + .get_single(id) + .ok_or(DevicesListError::DeviceNotFound)?; + if !dev.validated { + return Err(DevicesListError::DeviceNotValidated.into()); + } + + Ok(X509::from_pem(&std::fs::read( + AppConfig::get().device_cert_path(id), + )?)?) + } + /// Delete a device pub fn delete(&mut self, id: &DeviceId) -> anyhow::Result<()> { let crt_path = AppConfig::get().device_cert_path(id); if crt_path.is_file() { - // TODO : implement - unimplemented!("Certificate revocation not implemented yet!"); + let cert = self.get_cert(id)?; + pki::revoke_device_cert(&cert)?; } let csr_path = AppConfig::get().device_csr_path(id); diff --git a/central_backend/src/main.rs b/central_backend/src/main.rs index fefc806..efd33af 100644 --- a/central_backend/src/main.rs +++ b/central_backend/src/main.rs @@ -25,6 +25,8 @@ async fn main() -> std::io::Result<()> { pki::refresh_crls().expect("Failed to initialize Root CA!"); + // TODO : schedule CRL auto renewal + // Initialize energy actor let actor = EnergyActor::new() .await