feat: ref
This commit is contained in:
@@ -5,40 +5,12 @@ use acme_lib::DirectoryUrl;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use crate::x509;
|
use crate::x509;
|
||||||
use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo, X509Certificate};
|
use crate::x509::{X509PublicKeyAlgo, X509Certificate};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
pub const CERT_NAME: &str = "cert.pem";
|
pub const CERT_NAME: &str = "cert.pem";
|
||||||
pub const KEY_NAME: &str = "key.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<AcmeAlgo> {
|
|
||||||
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum AcmeMode {
|
pub enum AcmeMode {
|
||||||
Prod,
|
Prod,
|
||||||
@@ -112,7 +84,7 @@ impl CertConfig {
|
|||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
if fs::read_dir(&item.path).is_err() {
|
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();
|
fs::create_dir_all(&item.path).ok();
|
||||||
}
|
}
|
||||||
filtered_cert_items.push(item2);
|
filtered_cert_items.push(item2);
|
||||||
@@ -150,11 +122,8 @@ impl CertConfigItem {
|
|||||||
if self.public_key_algo.is_none() {
|
if self.public_key_algo.is_none() {
|
||||||
self.public_key_algo = match &self.algo {
|
self.public_key_algo = match &self.algo {
|
||||||
None => Some(X509PublicKeyAlgo::Rsa(2048)),
|
None => Some(X509PublicKeyAlgo::Rsa(2048)),
|
||||||
Some(algo) => match AcmeAlgo::parse(&algo) {
|
Some(algo) => match X509PublicKeyAlgo::from_str(&algo) {
|
||||||
Ok(AcmeAlgo::Rsa(bit_length)) => Some(X509PublicKeyAlgo::Rsa(bit_length)),
|
Ok(algo) => Some(algo),
|
||||||
Ok(AcmeAlgo::Ec256) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1)),
|
|
||||||
Ok(AcmeAlgo::Ec384) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1)),
|
|
||||||
Ok(AcmeAlgo::Ec521) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1)),
|
|
||||||
Err(_) => return simple_error!("Unknown algo: {}", algo),
|
Err(_) => return simple_error!("Unknown algo: {}", algo),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
59
src/main.rs
59
src/main.rs
@@ -7,12 +7,14 @@ mod config;
|
|||||||
mod x509;
|
mod x509;
|
||||||
// mod simple_thread_pool;
|
// mod simple_thread_pool;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use acme_lib::Directory;
|
use acme_lib::Directory;
|
||||||
use acme_lib::{create_p384_key, create_p256_key, create_rsa_key};
|
use acme_lib::{create_p384_key, create_p256_key, create_rsa_key};
|
||||||
use acme_lib::persist::FilePersist;
|
use acme_lib::persist::FilePersist;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use tide::Request;
|
use tide::Request;
|
||||||
@@ -21,9 +23,9 @@ use std::time::Duration;
|
|||||||
use async_std::task;
|
use async_std::task;
|
||||||
use async_std::channel;
|
use async_std::channel;
|
||||||
use async_std::channel::Sender;
|
use async_std::channel::Sender;
|
||||||
use config::AcmeAlgo;
|
|
||||||
use config::AcmeMode;
|
use config::AcmeMode;
|
||||||
use crate::config::{CertConfig, CERT_NAME, KEY_NAME};
|
use crate::config::{CertConfig, CERT_NAME, KEY_NAME};
|
||||||
|
use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo};
|
||||||
|
|
||||||
const NAME: &str = env!("CARGO_PKG_NAME");
|
const NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
@@ -39,7 +41,7 @@ struct AcmeRequest<'a> {
|
|||||||
contract_email: &'a str,
|
contract_email: &'a str,
|
||||||
primary_name: &'a str,
|
primary_name: &'a str,
|
||||||
alt_names: &'a [&'a str],
|
alt_names: &'a [&'a str],
|
||||||
algo: AcmeAlgo,
|
algo: X509PublicKeyAlgo,
|
||||||
mode: AcmeMode,
|
mode: AcmeMode,
|
||||||
account_dir: &'a str,
|
account_dir: &'a str,
|
||||||
timeout: u64,
|
timeout: u64,
|
||||||
@@ -49,8 +51,6 @@ struct AcmeRequest<'a> {
|
|||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> tide::Result<()> {
|
async fn main() -> tide::Result<()> {
|
||||||
println!("{}", include_str!("logo.txt"));
|
|
||||||
|
|
||||||
let matches = App::new(NAME)
|
let matches = App::new(NAME)
|
||||||
.version(VERSION)
|
.version(VERSION)
|
||||||
.about(DESCRIPTION)
|
.about(DESCRIPTION)
|
||||||
@@ -66,22 +66,37 @@ 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("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("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("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();
|
.get_matches();
|
||||||
|
|
||||||
|
if matches.is_present("verbose") {
|
||||||
|
env::set_var("LOGGER_LEVEL", "*");
|
||||||
|
}
|
||||||
|
|
||||||
if matches.is_present("version") {
|
if matches.is_present("version") {
|
||||||
|
println!("{}", include_str!("logo.txt"));
|
||||||
information!("{} v{}", NAME, VERSION);
|
information!("{} v{}", NAME, VERSION);
|
||||||
exit(1);
|
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(|| {
|
let email = matches.value_of("email").unwrap_or_else(|| {
|
||||||
failure!("Email is not assigned.");
|
failure!("Email is not assigned.");
|
||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let None = matches.value_of("type") {
|
match matches.value_of("type") {
|
||||||
failure!("Type is not assigned.");
|
Some("http") => {}
|
||||||
|
_ => {
|
||||||
|
failure!("Type is not assigned or must be http.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let port: u16 = match matches.value_of("port") {
|
let port: u16 = match matches.value_of("port") {
|
||||||
Some(p) => p.parse().unwrap_or_else(|e| {
|
Some(p) => p.parse().unwrap_or_else(|e| {
|
||||||
failure!("Parse port: {}, failed: {}", p, e);
|
failure!("Parse port: {}, failed: {}", p, e);
|
||||||
@@ -103,7 +118,7 @@ async fn main() -> tide::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let algo = match matches.value_of("algo") {
|
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);
|
failure!("{}", e);
|
||||||
exit(1);
|
exit(1);
|
||||||
}),
|
}),
|
||||||
@@ -150,7 +165,7 @@ async fn main() -> tide::Result<()> {
|
|||||||
timeout,
|
timeout,
|
||||||
..Default::default()
|
..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);
|
failure!("Request certificate by acme failed: {}", e);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -179,7 +194,7 @@ async fn main() -> tide::Result<()> {
|
|||||||
cert_file: Some(format!("{}/{}", item.path, CERT_NAME)),
|
cert_file: Some(format!("{}/{}", item.path, CERT_NAME)),
|
||||||
key_file: Some(format!("{}/{}", item.path, KEY_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);
|
failure!("Request certificate: {}, by acme failed: {}", item.path, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +205,7 @@ async fn main() -> tide::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_domains(acme_request: AcmeRequest) -> XResult<()> {
|
fn request_acme_certificate(acme_request: AcmeRequest) -> XResult<()> {
|
||||||
information!("Acme mode: {:?}", acme_request.mode);
|
information!("Acme mode: {:?}", acme_request.mode);
|
||||||
let url = acme_request.mode.directory_url();
|
let url = acme_request.mode.directory_url();
|
||||||
information!("Acme dir: {}", acme_request.account_dir);
|
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 mut order_csr_index = 0;
|
||||||
let ord_csr = loop {
|
let ord_csr = loop {
|
||||||
if let Some(ord_csr) = ord_new.confirm_validations() {
|
if let Some(ord_csr) = ord_new.confirm_validations() {
|
||||||
|
debugging!("Valid acme certificate http challenge success");
|
||||||
break ord_csr;
|
break ord_csr;
|
||||||
}
|
}
|
||||||
|
|
||||||
information!("Loop for acme challenge auth, #{}", order_csr_index);
|
information!("Loop for acme challenge auth, #{}", order_csr_index);
|
||||||
order_csr_index += 1;
|
order_csr_index += 1;
|
||||||
|
|
||||||
|
debugging!("Start acme certificate http challenge");
|
||||||
let auths = opt_result!(ord_new.authorizations(), "Order auth failed: {}");
|
let auths = opt_result!(ord_new.authorizations(), "Order auth failed: {}");
|
||||||
for auth in &auths {
|
for auth in &auths {
|
||||||
let chall = auth.http_challenge();
|
let chall = auth.http_challenge();
|
||||||
let token = chall.http_token();
|
let token = chall.http_token();
|
||||||
// let path = format!(".well-known/acme-challenge/{}", token);
|
|
||||||
let proof = chall.http_proof();
|
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);
|
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: {}");
|
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 {
|
let pkey_pri = match acme_request.algo {
|
||||||
AcmeAlgo::Ec256 => create_p256_key(),
|
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1) => create_p256_key(),
|
||||||
AcmeAlgo::Ec384 => create_p384_key(),
|
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1) => create_p384_key(),
|
||||||
AcmeAlgo::Ec521 => return simple_error!("Algo ec521 is not supported"),
|
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1) => return simple_error!("Algo ec521 is not supported"),
|
||||||
AcmeAlgo::Rsa(bits) => create_rsa_key(bits),
|
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: {}");
|
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: {}");
|
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) {
|
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);
|
information!("Write file: {}", cert_file);
|
||||||
if let Err(e) = fs::write(cert_file, cert.certificate()) {
|
if let Err(e) = fs::write(cert_file, cert.certificate()) {
|
||||||
failure!("Write file: {}, failed: {}", cert_file, e);
|
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()) {
|
if let Err(e) = fs::write(key_file, cert.private_key()) {
|
||||||
failure!("Write file: {}, failed: {}", key_file, e);
|
failure!("Write file: {}, failed: {}", key_file, e);
|
||||||
}
|
}
|
||||||
|
success!("Write files success: {} and {}", cert_file, key_file);
|
||||||
} else {
|
} else {
|
||||||
information!("Certificate key: {}", cert.private_key());
|
information!("Certificate key: {}", cert.private_key());
|
||||||
information!("Certificate pem: {}", cert.certificate());
|
information!("Certificate pem: {}", cert.certificate());
|
||||||
|
|||||||
38
src/x509.rs
38
src/x509.rs
@@ -8,6 +8,7 @@ use rust_util::XResult;
|
|||||||
use x509_parser::der_parser::parse_der;
|
use x509_parser::der_parser::parse_der;
|
||||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||||
use x509_parser::der_parser::ber::BerObjectContent;
|
use x509_parser::der_parser::ber::BerObjectContent;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref OID_COMMON_NAME: Oid<'static> = Oid::from_str("2.5.4.3").unwrap();
|
static ref OID_COMMON_NAME: Oid<'static> = Oid::from_str("2.5.4.3").unwrap();
|
||||||
@@ -28,19 +29,52 @@ pub enum X509IssuerAlgo {
|
|||||||
EcdsaWithSha256,
|
EcdsaWithSha256,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum X509EcPublicKeyAlgo {
|
pub enum X509EcPublicKeyAlgo {
|
||||||
Secp256r1,
|
Secp256r1,
|
||||||
Secp384r1,
|
Secp384r1,
|
||||||
Secp521r1,
|
Secp521r1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum X509PublicKeyAlgo {
|
pub enum X509PublicKeyAlgo {
|
||||||
EcKey(X509EcPublicKeyAlgo),
|
EcKey(X509EcPublicKeyAlgo),
|
||||||
Rsa(u32),
|
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<dyn Error>;
|
||||||
|
|
||||||
|
fn from_str(algo_str: &str) -> Result<Self, Self::Err> {
|
||||||
|
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 {
|
impl X509PublicKeyAlgo {
|
||||||
pub fn parse<'a>(pem_id: &str, public_key_info: &SubjectPublicKeyInfo<'a>) -> XResult<Self> {
|
pub fn parse<'a>(pem_id: &str, public_key_info: &SubjectPublicKeyInfo<'a>) -> XResult<Self> {
|
||||||
let algorithm = &public_key_info.algorithm;
|
let algorithm = &public_key_info.algorithm;
|
||||||
|
|||||||
Reference in New Issue
Block a user