Generate Root CA
This commit is contained in:
60
central_backend/src/app_config.rs
Normal file
60
central_backend/src/app_config.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use clap::Parser;
|
||||
|
||||
/// Solar system central backend
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
pub struct AppConfig {
|
||||
/// The port the server will listen to (using HTTPS)
|
||||
#[arg(short, long, env, default_value = "0.0.0.0:8443")]
|
||||
listen_address: String,
|
||||
|
||||
/// Server storage path
|
||||
#[arg(short, long, env, default_value = "storage")]
|
||||
storage: String,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref ARGS: AppConfig = {
|
||||
AppConfig::parse()
|
||||
};
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
/// Get parsed command line arguments
|
||||
pub fn get() -> &'static AppConfig {
|
||||
&ARGS
|
||||
}
|
||||
|
||||
|
||||
/// Get storage path
|
||||
pub fn storage_path(&self) -> PathBuf {
|
||||
Path::new(&self.storage).to_path_buf()
|
||||
}
|
||||
|
||||
/// Get PKI storage path
|
||||
pub fn pki_path(&self) -> PathBuf {
|
||||
self.storage_path().join("pki")
|
||||
}
|
||||
|
||||
/// Get PKI root CA cert path
|
||||
pub fn root_ca_cert_path(&self) -> PathBuf {
|
||||
self.pki_path().join("root_ca.pem")
|
||||
}
|
||||
|
||||
/// Get PKI root CA private key path
|
||||
pub fn root_ca_priv_key_path(&self) -> PathBuf {
|
||||
self.pki_path().join("root_ca.key")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::app_config::AppConfig;
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
use clap::CommandFactory;
|
||||
AppConfig::command().debug_assert()
|
||||
}
|
||||
}
|
3
central_backend/src/lib.rs
Normal file
3
central_backend/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod app_config;
|
||||
pub mod pki;
|
||||
pub mod utils;
|
13
central_backend/src/main.rs
Normal file
13
central_backend/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use central_backend::app_config::AppConfig;
|
||||
use central_backend::pki;
|
||||
use central_backend::utils::files_utils::create_directory_if_missing;
|
||||
|
||||
fn main() {
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
// Initialize storage
|
||||
create_directory_if_missing(&AppConfig::get().pki_path()).unwrap();
|
||||
|
||||
// Initialize PKI
|
||||
pki::initialize_root_ca().expect("Failed to initialize Root CA!");
|
||||
}
|
68
central_backend/src/pki.rs
Normal file
68
central_backend/src/pki.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use openssl::asn1::Asn1Time;
|
||||
use openssl::bn::{BigNum, MsbOption};
|
||||
use openssl::ec::EcGroup;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::nid::Nid;
|
||||
use openssl::pkey::PKey;
|
||||
use openssl::x509::extension::{BasicConstraints, KeyUsage, SubjectKeyIdentifier};
|
||||
use openssl::x509::{X509, X509NameBuilder};
|
||||
use crate::app_config::AppConfig;
|
||||
|
||||
/// Initialize Root CA, if required
|
||||
pub fn initialize_root_ca() -> anyhow::Result<()> {
|
||||
if AppConfig::get().root_ca_cert_path().exists()
|
||||
&& AppConfig::get().root_ca_priv_key_path().exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::info!("Generating root ca...");
|
||||
|
||||
// Generate root private key
|
||||
let nid = Nid::X9_62_PRIME256V1; // NIST P-256 curve
|
||||
let group = EcGroup::from_curve_name(nid)?;
|
||||
let key = openssl::ec::EcKey::generate(&group)?;
|
||||
let key_pair = PKey::from_ec_key(key.clone())?;
|
||||
|
||||
let mut x509_name = X509NameBuilder::new()?;
|
||||
x509_name.append_entry_by_text("C", "FR")?;
|
||||
x509_name.append_entry_by_text("CN", "SolarEnergy Root CA")?;
|
||||
let x509_name = x509_name.build();
|
||||
|
||||
let mut cert_builder = X509::builder()?;
|
||||
cert_builder.set_version(2)?;
|
||||
let serial_number = {
|
||||
let mut serial = BigNum::new()?;
|
||||
serial.rand(159, MsbOption::MAYBE_ZERO, false)?;
|
||||
serial.to_asn1_integer()?
|
||||
};
|
||||
cert_builder.set_serial_number(&serial_number)?;
|
||||
cert_builder.set_subject_name(&x509_name)?;
|
||||
cert_builder.set_issuer_name(&x509_name)?;
|
||||
cert_builder.set_pubkey(&key_pair)?;
|
||||
let not_before = Asn1Time::days_from_now(0)?;
|
||||
cert_builder.set_not_before(¬_before)?;
|
||||
let not_after = Asn1Time::days_from_now(365 * 30)?;
|
||||
cert_builder.set_not_after(¬_after)?;
|
||||
|
||||
cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?;
|
||||
cert_builder.append_extension(
|
||||
KeyUsage::new()
|
||||
.critical()
|
||||
.key_cert_sign()
|
||||
.crl_sign()
|
||||
.build()?,
|
||||
)?;
|
||||
|
||||
let subject_key_identifier =
|
||||
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
|
||||
cert_builder.append_extension(subject_key_identifier)?;
|
||||
|
||||
cert_builder.sign(&key_pair, MessageDigest::sha256())?;
|
||||
let cert = cert_builder.build();
|
||||
|
||||
// Serialize generated root CA
|
||||
std::fs::write(AppConfig::get().root_ca_priv_key_path(), key.private_key_to_pem()?)?;
|
||||
std::fs::write(AppConfig::get().root_ca_cert_path(), cert.to_pem()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
10
central_backend/src/utils/files_utils.rs
Normal file
10
central_backend/src/utils/files_utils.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
/// Create directory if missing
|
||||
pub fn create_directory_if_missing<P: AsRef<Path>>(path: P) -> anyhow::Result<()> {
|
||||
let path = path.as_ref();
|
||||
if !path.exists() {
|
||||
std::fs::create_dir_all(path)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
1
central_backend/src/utils/mod.rs
Normal file
1
central_backend/src/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod files_utils;
|
Reference in New Issue
Block a user