Can revoke issued certificates
This commit is contained in:
		| @@ -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<String> { | ||||
|  | ||||
|     Ok(String::from_utf8(cert)?) | ||||
| } | ||||
|  | ||||
| /// Check if a certificate is revoked | ||||
| fn is_revoked(cert: &X509, ca: &CertData) -> anyhow::Result<bool> { | ||||
|     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()?) | ||||
| } | ||||
|   | ||||
| @@ -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<DeviceId, Device>); | ||||
| @@ -129,12 +133,26 @@ impl DevicesList { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Get single certificate information | ||||
|     fn get_cert(&self, id: &DeviceId) -> anyhow::Result<X509> { | ||||
|         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); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user