diff --git a/src/config.rs b/src/config.rs index 20513d4..a98e8d7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,40 +5,12 @@ use acme_lib::DirectoryUrl; use std::path::PathBuf; use std::str::FromStr; use crate::x509; -use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo, X509Certificate}; +use crate::x509::{X509PublicKeyAlgo, X509Certificate}; use std::time::SystemTime; pub const CERT_NAME: &str = "cert.pem"; pub const KEY_NAME: &str = "key.pem"; -#[derive(Debug, Clone, Copy)] -pub enum AcmeAlgo { - Ec256, - Ec384, - Ec521, - Rsa(u32), -} - -impl Default for AcmeAlgo { - fn default() -> Self { - Self::Ec384 - } -} - -impl AcmeAlgo { - pub fn parse(s: &str) -> XResult { - match s { - "ec256" => Ok(AcmeAlgo::Ec256), - "ec384" => Ok(AcmeAlgo::Ec384), - "ec521" => Ok(AcmeAlgo::Ec521), - "rsa2048" => Ok(AcmeAlgo::Rsa(2048)), - "rsa3072" => Ok(AcmeAlgo::Rsa(3072)), - "rsa4096" => Ok(AcmeAlgo::Rsa(4096)), - _ => simple_error!("Unknown algo: {}", s), - } - } -} - #[derive(Debug, Clone, Copy)] pub enum AcmeMode { Prod, @@ -112,7 +84,7 @@ impl CertConfig { } Ok(None) => { if fs::read_dir(&item.path).is_err() { - information!("Create path: {}", item.path); + information!("Create certificate path: {}", item.path); fs::create_dir_all(&item.path).ok(); } filtered_cert_items.push(item2); @@ -150,11 +122,8 @@ impl CertConfigItem { if self.public_key_algo.is_none() { self.public_key_algo = match &self.algo { None => Some(X509PublicKeyAlgo::Rsa(2048)), - Some(algo) => match AcmeAlgo::parse(&algo) { - Ok(AcmeAlgo::Rsa(bit_length)) => Some(X509PublicKeyAlgo::Rsa(bit_length)), - Ok(AcmeAlgo::Ec256) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1)), - Ok(AcmeAlgo::Ec384) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1)), - Ok(AcmeAlgo::Ec521) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1)), + Some(algo) => match X509PublicKeyAlgo::from_str(&algo) { + Ok(algo) => Some(algo), Err(_) => return simple_error!("Unknown algo: {}", algo), }, }; diff --git a/src/main.rs b/src/main.rs index 1727038..e3615b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,12 +7,14 @@ mod config; mod x509; // mod simple_thread_pool; +use std::env; use rust_util::XResult; use acme_lib::Directory; use acme_lib::{create_p384_key, create_p256_key, create_rsa_key}; use acme_lib::persist::FilePersist; use clap::{App, Arg}; use std::fs; +use std::str::FromStr; use std::sync::RwLock; use std::collections::BTreeMap; use tide::Request; @@ -21,9 +23,9 @@ use std::time::Duration; use async_std::task; use async_std::channel; use async_std::channel::Sender; -use config::AcmeAlgo; use config::AcmeMode; use crate::config::{CertConfig, CERT_NAME, KEY_NAME}; +use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo}; const NAME: &str = env!("CARGO_PKG_NAME"); const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -39,7 +41,7 @@ struct AcmeRequest<'a> { contract_email: &'a str, primary_name: &'a str, alt_names: &'a [&'a str], - algo: AcmeAlgo, + algo: X509PublicKeyAlgo, mode: AcmeMode, account_dir: &'a str, timeout: u64, @@ -49,8 +51,6 @@ struct AcmeRequest<'a> { #[async_std::main] async fn main() -> tide::Result<()> { - println!("{}", include_str!("logo.txt")); - let matches = App::new(NAME) .version(VERSION) .about(DESCRIPTION) @@ -66,21 +66,36 @@ async fn main() -> tide::Result<()> { .arg(Arg::with_name("mode").short("m").long("mode").takes_value(true).default_value("prod").help("Mode")) .arg(Arg::with_name("dir").long("dir").takes_value(true).default_value("acme_dir").help("Account key dir")) .arg(Arg::with_name("config").short("c").long("config").takes_value(true).help("Cert config")) + .arg(Arg::with_name("hide-logo").long("hide-logo").help("Hide logo")) .get_matches(); + if matches.is_present("verbose") { + env::set_var("LOGGER_LEVEL", "*"); + } + if matches.is_present("version") { + println!("{}", include_str!("logo.txt")); information!("{} v{}", NAME, VERSION); exit(1); } + if !matches.is_present("hide-logo") { + println!("{}", include_str!("logo.txt")); + } + + debugging!("Clap matches: {:?}", matches); + let email = matches.value_of("email").unwrap_or_else(|| { failure!("Email is not assigned."); exit(1); }); - if let None = matches.value_of("type") { - failure!("Type is not assigned."); - exit(1); + match matches.value_of("type") { + Some("http") => {} + _ => { + failure!("Type is not assigned or must be http."); + exit(1); + } } let port: u16 = match matches.value_of("port") { Some(p) => p.parse().unwrap_or_else(|e| { @@ -103,7 +118,7 @@ async fn main() -> tide::Result<()> { } }; let algo = match matches.value_of("algo") { - Some(a) => AcmeAlgo::parse(a).unwrap_or_else(|e| { + Some(a) => X509PublicKeyAlgo::from_str(a).unwrap_or_else(|e| { failure!("{}", e); exit(1); }), @@ -150,7 +165,7 @@ async fn main() -> tide::Result<()> { timeout, ..Default::default() }; - if let Err(e) = request_domains(acme_request) { + if let Err(e) = request_acme_certificate(acme_request) { failure!("Request certificate by acme failed: {}", e); exit(1); } @@ -179,7 +194,7 @@ async fn main() -> tide::Result<()> { cert_file: Some(format!("{}/{}", item.path, CERT_NAME)), key_file: Some(format!("{}/{}", item.path, KEY_NAME)), }; - if let Err(e) = request_domains(acme_request) { + if let Err(e) = request_acme_certificate(acme_request) { failure!("Request certificate: {}, by acme failed: {}", item.path, e); } } @@ -190,7 +205,7 @@ async fn main() -> tide::Result<()> { Ok(()) } -fn request_domains(acme_request: AcmeRequest) -> XResult<()> { +fn request_acme_certificate(acme_request: AcmeRequest) -> XResult<()> { information!("Acme mode: {:?}", acme_request.mode); let url = acme_request.mode.directory_url(); information!("Acme dir: {}", acme_request.account_dir); @@ -203,41 +218,48 @@ fn request_domains(acme_request: AcmeRequest) -> XResult<()> { let mut order_csr_index = 0; let ord_csr = loop { if let Some(ord_csr) = ord_new.confirm_validations() { + debugging!("Valid acme certificate http challenge success"); break ord_csr; } information!("Loop for acme challenge auth, #{}", order_csr_index); order_csr_index += 1; + debugging!("Start acme certificate http challenge"); let auths = opt_result!(ord_new.authorizations(), "Order auth failed: {}"); for auth in &auths { let chall = auth.http_challenge(); let token = chall.http_token(); - // let path = format!(".well-known/acme-challenge/{}", token); let proof = chall.http_proof(); { - information!("Add acme challenge: {} -> {}",token, proof); + information!("Add acme http challenge: {} -> {}",token, proof); TOKEN_MAP.write().unwrap().insert(token.to_string(), proof); } - opt_result!(chall.validate(acme_request.timeout), "Validate challenge failed: {}"); + debugging!("Valid acme certificate http challenge"); + opt_result!(chall.validate(acme_request.timeout), "Validate http challenge failed: {}"); } + debugging!("Refresh acme certificate order"); opt_result!(ord_new.refresh(), "Refresh order failed: {}"); }; - information!("Generate private key, type: {:?}", acme_request.algo); + information!("Generate private key, key type: {:?}", acme_request.algo); let pkey_pri = match acme_request.algo { - AcmeAlgo::Ec256 => create_p256_key(), - AcmeAlgo::Ec384 => create_p384_key(), - AcmeAlgo::Ec521 => return simple_error!("Algo ec521 is not supported"), - AcmeAlgo::Rsa(bits) => create_rsa_key(bits), + X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1) => create_p256_key(), + X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1) => create_p384_key(), + X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1) => return simple_error!("Algo ec521 is not supported"), + X509PublicKeyAlgo::Rsa(bits) => create_rsa_key(bits), }; + debugging!("Invoking csr finalize pkey"); let ord_cert = opt_result!( ord_csr.finalize_pkey(pkey_pri, acme_request.timeout), "Submit CSR failed: {}"); + debugging!("Downloading and save cert"); let cert = opt_result!( ord_cert.download_and_save_cert(), "Download and save certificate failed: {}"); if let (Some(cert_file), Some(key_file)) = (&acme_request.cert_file, &acme_request.key_file) { + debugging!("Certificate key: {}", cert.private_key()); + debugging!("Certificate pem: {}", cert.certificate()); information!("Write file: {}", cert_file); if let Err(e) = fs::write(cert_file, cert.certificate()) { failure!("Write file: {}, failed: {}", cert_file, e); @@ -246,6 +268,7 @@ fn request_domains(acme_request: AcmeRequest) -> XResult<()> { if let Err(e) = fs::write(key_file, cert.private_key()) { failure!("Write file: {}, failed: {}", key_file, e); } + success!("Write files success: {} and {}", cert_file, key_file); } else { information!("Certificate key: {}", cert.private_key()); information!("Certificate pem: {}", cert.certificate()); diff --git a/src/x509.rs b/src/x509.rs index 9269d18..4f8ae8a 100644 --- a/src/x509.rs +++ b/src/x509.rs @@ -8,6 +8,7 @@ use rust_util::XResult; use x509_parser::der_parser::parse_der; use x509_parser::x509::SubjectPublicKeyInfo; use x509_parser::der_parser::ber::BerObjectContent; +use std::error::Error; lazy_static! { static ref OID_COMMON_NAME: Oid<'static> = Oid::from_str("2.5.4.3").unwrap(); @@ -28,19 +29,52 @@ pub enum X509IssuerAlgo { EcdsaWithSha256, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum X509EcPublicKeyAlgo { Secp256r1, Secp384r1, Secp521r1, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum X509PublicKeyAlgo { EcKey(X509EcPublicKeyAlgo), Rsa(u32), } +impl Default for X509PublicKeyAlgo { + fn default() -> Self { + X509PublicKeyAlgo::Rsa(2048) + } +} + +impl ToString for X509PublicKeyAlgo { + fn to_string(&self) -> String { + match self { + Self::Rsa(bit_length) => format!("rsa{}", bit_length), + Self::EcKey(X509EcPublicKeyAlgo::Secp256r1) => "p256".into(), + Self::EcKey(X509EcPublicKeyAlgo::Secp384r1) => "p384".into(), + Self::EcKey(X509EcPublicKeyAlgo::Secp521r1) => "p521".into(), + } + } +} + +impl FromStr for X509PublicKeyAlgo { + type Err = Box; + + fn from_str(algo_str: &str) -> Result { + match algo_str { + "ec256" | "p256" => Ok(Self::EcKey(X509EcPublicKeyAlgo::Secp256r1)), + "ec384" | "p384" => Ok(Self::EcKey(X509EcPublicKeyAlgo::Secp384r1)), + "ec521" | "p521" => Ok(Self::EcKey(X509EcPublicKeyAlgo::Secp521r1)), + "rsa2048" => Ok(Self::Rsa(2048)), + "rsa3072" => Ok(Self::Rsa(3072)), + "rsa4096" => Ok(Self::Rsa(4096)), + _ => simple_error!("Unknown public key algo: {}", algo_str), + } + } +} + impl X509PublicKeyAlgo { pub fn parse<'a>(pem_id: &str, public_key_info: &SubjectPublicKeyInfo<'a>) -> XResult { let algorithm = &public_key_info.algorithm;