Generate server certificate
This commit is contained in:
parent
f4fde9bc46
commit
09f526bfb7
@ -7,15 +7,15 @@ use std::path::{Path, PathBuf};
|
|||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
/// The port the server will listen to (using HTTPS)
|
/// The port the server will listen to (using HTTPS)
|
||||||
#[arg(short, long, env, default_value = "0.0.0.0:8443")]
|
#[arg(short, long, env, default_value = "0.0.0.0:8443")]
|
||||||
listen_address: String,
|
pub listen_address: String,
|
||||||
|
|
||||||
/// The port the server will listen to (using HTTP, for unsecure connections)
|
/// The port the server will listen to (using HTTP, for unsecure connections)
|
||||||
#[arg(short, long, env, default_value = "0.0.0.0:8080")]
|
#[arg(short, long, env, default_value = "0.0.0.0:8080")]
|
||||||
unsecure_listen_address: String,
|
pub unsecure_listen_address: String,
|
||||||
|
|
||||||
/// Public server hostname (assuming that the ports used are the same for listen address)
|
/// Public server hostname (assuming that the ports used are the same for listen address)
|
||||||
#[arg(short('H'), long, env, default_value = "localhost")]
|
#[arg(short('H'), long, env, default_value = "localhost")]
|
||||||
hostname: String,
|
pub hostname: String,
|
||||||
|
|
||||||
/// Server storage path
|
/// Server storage path
|
||||||
#[arg(short, long, env, default_value = "storage")]
|
#[arg(short, long, env, default_value = "storage")]
|
||||||
@ -106,6 +106,16 @@ impl AppConfig {
|
|||||||
pub fn devices_ca_priv_key_path(&self) -> PathBuf {
|
pub fn devices_ca_priv_key_path(&self) -> PathBuf {
|
||||||
self.pki_path().join("devices_ca.key")
|
self.pki_path().join("devices_ca.key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get PKI server cert path
|
||||||
|
pub fn server_cert_path(&self) -> PathBuf {
|
||||||
|
self.pki_path().join("server.pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get PKI server private key path
|
||||||
|
pub fn server_priv_key_path(&self) -> PathBuf {
|
||||||
|
self.pki_path().join("server.key")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -10,7 +10,9 @@ use openssl::ec::EcGroup;
|
|||||||
use openssl::hash::MessageDigest;
|
use openssl::hash::MessageDigest;
|
||||||
use openssl::nid::Nid;
|
use openssl::nid::Nid;
|
||||||
use openssl::pkey::{PKey, Private};
|
use openssl::pkey::{PKey, Private};
|
||||||
use openssl::x509::extension::{BasicConstraints, KeyUsage, SubjectKeyIdentifier};
|
use openssl::x509::extension::{
|
||||||
|
BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier,
|
||||||
|
};
|
||||||
use openssl::x509::{X509Crl, X509NameBuilder, X509};
|
use openssl::x509::{X509Crl, X509NameBuilder, X509};
|
||||||
use openssl_sys::{
|
use openssl_sys::{
|
||||||
X509_CRL_add0_revoked, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate,
|
X509_CRL_add0_revoked, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate,
|
||||||
@ -31,7 +33,7 @@ pub enum PKIError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Certificate and private key
|
/// Certificate and private key
|
||||||
struct CertData {
|
pub struct CertData {
|
||||||
cert: X509,
|
cert: X509,
|
||||||
key: PKey<Private>,
|
key: PKey<Private>,
|
||||||
crl: Option<PathBuf>,
|
crl: Option<PathBuf>,
|
||||||
@ -64,6 +66,15 @@ impl CertData {
|
|||||||
crl: Some(AppConfig::get().devices_ca_crl_path()),
|
crl: Some(AppConfig::get().devices_ca_crl_path()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load server CA
|
||||||
|
pub fn load_server() -> anyhow::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
cert: load_certificate_from_file(AppConfig::get().server_cert_path())?,
|
||||||
|
key: load_priv_key_from_file(AppConfig::get().server_priv_key_path())?,
|
||||||
|
crl: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate private key
|
/// Generate private key
|
||||||
@ -96,6 +107,8 @@ struct GenCertificateReq<'a> {
|
|||||||
cn: &'a str,
|
cn: &'a str,
|
||||||
issuer: Option<&'a CertData>,
|
issuer: Option<&'a CertData>,
|
||||||
ca: bool,
|
ca: bool,
|
||||||
|
web_server: bool,
|
||||||
|
subject_alternative_names: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate certificate
|
/// Generate certificate
|
||||||
@ -122,7 +135,7 @@ fn gen_certificate(req: GenCertificateReq) -> anyhow::Result<(Vec<u8>, Vec<u8>)>
|
|||||||
// Self-signed certificate
|
// Self-signed certificate
|
||||||
None => cert_builder.set_issuer_name(&x509_name)?,
|
None => cert_builder.set_issuer_name(&x509_name)?,
|
||||||
// Certificate signed by another CA
|
// Certificate signed by another CA
|
||||||
Some(i) => cert_builder.set_issuer_name(i.cert.issuer_name())?,
|
Some(i) => cert_builder.set_issuer_name(i.cert.subject_name())?,
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_builder.set_pubkey(&key_pair)?;
|
cert_builder.set_pubkey(&key_pair)?;
|
||||||
@ -154,11 +167,34 @@ fn gen_certificate(req: GenCertificateReq) -> anyhow::Result<(Vec<u8>, Vec<u8>)>
|
|||||||
|
|
||||||
// Key usage
|
// Key usage
|
||||||
let mut key_usage = KeyUsage::new();
|
let mut key_usage = KeyUsage::new();
|
||||||
|
let mut eku = None;
|
||||||
if req.ca {
|
if req.ca {
|
||||||
key_usage.key_cert_sign().crl_sign();
|
key_usage.key_cert_sign().crl_sign();
|
||||||
}
|
}
|
||||||
|
if req.web_server {
|
||||||
|
key_usage.digital_signature().key_encipherment();
|
||||||
|
eku = Some(
|
||||||
|
ExtendedKeyUsage::new()
|
||||||
|
.server_auth()
|
||||||
|
.client_auth()
|
||||||
|
.build()?,
|
||||||
|
);
|
||||||
|
}
|
||||||
cert_builder.append_extension(key_usage.critical().build()?)?;
|
cert_builder.append_extension(key_usage.critical().build()?)?;
|
||||||
|
|
||||||
|
if let Some(eku) = eku {
|
||||||
|
cert_builder.append_extension(eku)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subject alternative names
|
||||||
|
if !req.subject_alternative_names.is_empty() {
|
||||||
|
let mut ext = SubjectAlternativeName::new();
|
||||||
|
for subj in req.subject_alternative_names {
|
||||||
|
ext.dns(subj);
|
||||||
|
}
|
||||||
|
cert_builder.append_extension(ext.build(&cert_builder.x509v3_context(None, None))?)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Subject key identifier
|
// Subject key identifier
|
||||||
let subject_key_identifier =
|
let subject_key_identifier =
|
||||||
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
|
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
|
||||||
@ -191,6 +227,7 @@ pub fn initialize_root_ca() -> anyhow::Result<()> {
|
|||||||
cn: "SolarEnergy Root CA",
|
cn: "SolarEnergy Root CA",
|
||||||
issuer: None,
|
issuer: None,
|
||||||
ca: true,
|
ca: true,
|
||||||
|
..Default::default()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Serialize generated web CA
|
// Serialize generated web CA
|
||||||
@ -214,6 +251,7 @@ pub fn initialize_web_ca() -> anyhow::Result<()> {
|
|||||||
cn: "SolarEnergy Web CA",
|
cn: "SolarEnergy Web CA",
|
||||||
issuer: Some(&CertData::load_root_ca()?),
|
issuer: Some(&CertData::load_root_ca()?),
|
||||||
ca: true,
|
ca: true,
|
||||||
|
..Default::default()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Serialize generated web CA
|
// Serialize generated web CA
|
||||||
@ -237,6 +275,7 @@ pub fn initialize_devices_ca() -> anyhow::Result<()> {
|
|||||||
cn: "SolarEnergy Devices CA",
|
cn: "SolarEnergy Devices CA",
|
||||||
issuer: Some(&CertData::load_root_ca()?),
|
issuer: Some(&CertData::load_root_ca()?),
|
||||||
ca: true,
|
ca: true,
|
||||||
|
..Default::default()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Serialize generated devices CA
|
// Serialize generated devices CA
|
||||||
@ -246,7 +285,31 @@ pub fn initialize_devices_ca() -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intialize or refresh a CRL
|
/// Initialize server certificate, if required
|
||||||
|
pub fn initialize_server_ca() -> anyhow::Result<()> {
|
||||||
|
if AppConfig::get().server_cert_path().exists()
|
||||||
|
&& AppConfig::get().server_priv_key_path().exists()
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Generating server certificate...");
|
||||||
|
|
||||||
|
let (key, cert) = gen_certificate(GenCertificateReq {
|
||||||
|
cn: &AppConfig::get().hostname,
|
||||||
|
issuer: Some(&CertData::load_web_ca()?),
|
||||||
|
web_server: true,
|
||||||
|
subject_alternative_names: vec![AppConfig::get().hostname.as_str()],
|
||||||
|
..Default::default()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
std::fs::write(AppConfig::get().server_priv_key_path(), key)?;
|
||||||
|
std::fs::write(AppConfig::get().server_cert_path(), cert)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize or refresh a CRL
|
||||||
fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
|
fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
|
||||||
let crl_path = d.crl.as_ref().ok_or(PKIError::MissingCRL)?;
|
let crl_path = d.crl.as_ref().ok_or(PKIError::MissingCRL)?;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ fn main() {
|
|||||||
pki::initialize_root_ca().expect("Failed to initialize Root CA!");
|
pki::initialize_root_ca().expect("Failed to initialize Root CA!");
|
||||||
pki::initialize_web_ca().expect("Failed to initialize web CA!");
|
pki::initialize_web_ca().expect("Failed to initialize web CA!");
|
||||||
pki::initialize_devices_ca().expect("Failed to initialize devices CA!");
|
pki::initialize_devices_ca().expect("Failed to initialize devices CA!");
|
||||||
|
pki::initialize_server_ca().expect("Failed to initialize server certificate!");
|
||||||
|
|
||||||
pki::refresh_crls().expect("Failed to initialize Root CA!");
|
pki::refresh_crls().expect("Failed to initialize Root CA!");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user