From bea33179d7bb774e8cce934ec24e27329a567c0a Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Wed, 26 Aug 2020 19:16:04 +0200 Subject: [PATCH] Allow to specify the CSR's digest algorithm --- acme_common/src/crypto/openssl_certificate.rs | 41 +++++++++++++------ acmed/src/acme_proto.rs | 9 +++- acmed/src/certificate.rs | 3 +- acmed/src/config.rs | 9 ++++ acmed/src/main.rs | 2 + acmed/src/main_event_loop.rs | 1 + man/en/acmed.toml.5 | 11 +++++ 7 files changed, 62 insertions(+), 14 deletions(-) diff --git a/acme_common/src/crypto/openssl_certificate.rs b/acme_common/src/crypto/openssl_certificate.rs index bfac7cc..5d51717 100644 --- a/acme_common/src/crypto/openssl_certificate.rs +++ b/acme_common/src/crypto/openssl_certificate.rs @@ -19,12 +19,32 @@ const CRT_SERIAL_NB_BITS: i32 = 32; const CRT_NB_DAYS_VALIDITY: u32 = 7; const INVALID_EXT_MSG: &str = "Invalid acmeIdentifier extension."; +fn get_digest(digest: HashFunction, key_pair: &KeyPair) -> MessageDigest { + #[cfg(not(any(ed25519, ed448)))] + let digest = digest.native_digest(); + let _ = key_pair; + #[cfg(any(ed25519, ed448))] + let digest = match key_pair.key_type { + #[cfg(ed25519)] + KeyType::Ed25519 => MessageDigest::null(), + #[cfg(ed448)] + KeyType::Ed448 => MessageDigest::null(), + _ => digest.native_digest(), + }; + digest +} + pub struct Csr { inner_csr: X509Req, } impl Csr { - pub fn new(key_pair: &KeyPair, domains: &[String], ips: &[String]) -> Result { + pub fn new( + key_pair: &KeyPair, + digest: HashFunction, + domains: &[String], + ips: &[String], + ) -> Result { let mut builder = X509ReqBuilder::new()?; builder.set_pubkey(&key_pair.inner_key)?; let ctx = builder.x509v3_context(None); @@ -39,7 +59,8 @@ impl Csr { let mut ext_stack = Stack::new()?; ext_stack.push(san)?; builder.add_extensions(&ext_stack)?; - builder.sign(&key_pair.inner_key, MessageDigest::sha256())?; + let digest = get_digest(digest, key_pair); + builder.sign(&key_pair.inner_key, digest)?; Ok(Csr { inner_csr: builder.build(), }) @@ -50,6 +71,11 @@ impl Csr { let csr = b64_encode(&csr); Ok(csr) } + + pub fn to_pem(&self) -> Result { + let csr = self.inner_csr.to_pem()?; + Ok(String::from_utf8(csr)?) + } } pub struct X509Certificate { @@ -74,16 +100,7 @@ impl X509Certificate { digest: HashFunction, ) -> Result<(KeyPair, Self), Error> { let key_pair = gen_keypair(key_type)?; - #[cfg(not(any(ed25519, ed448)))] - let digest = digest.native_digest(); - #[cfg(any(ed25519, ed448))] - let digest = match key_pair.key_type { - #[cfg(ed25519)] - KeyType::Ed25519 => MessageDigest::null(), - #[cfg(ed448)] - KeyType::Ed448 => MessageDigest::null(), - _ => digest.native_digest(), - }; + let digest = get_digest(digest, &key_pair); let inner_cert = gen_certificate(domain, &key_pair, &digest, acme_ext)?; let cert = X509Certificate { inner_cert }; Ok((key_pair, cert)) diff --git a/acmed/src/acme_proto.rs b/acmed/src/acme_proto.rs index 524e19a..b598ad8 100644 --- a/acmed/src/acme_proto.rs +++ b/acmed/src/acme_proto.rs @@ -171,8 +171,15 @@ pub fn request_certificate( .filter(|e| e.id_type == IdentifierType::Ip) .map(|e| e.value.to_owned()) .collect(); + let csr = Csr::new( + &key_pair, + cert.csr_digest, + domains.as_slice(), + ips.as_slice(), + )?; + cert.trace(&format!("New CSR:\n{}", csr.to_pem()?)); let csr = json!({ - "csr": Csr::new(&key_pair, domains.as_slice(), ips.as_slice())?.to_der_base64()?, + "csr": csr.to_der_base64()?, }); let csr = csr.to_string(); let data_builder = set_data_builder!(account, csr.as_bytes()); diff --git a/acmed/src/certificate.rs b/acmed/src/certificate.rs index bd1e72e..ec97924 100644 --- a/acmed/src/certificate.rs +++ b/acmed/src/certificate.rs @@ -3,7 +3,7 @@ use crate::acme_proto::Challenge; use crate::hooks::{self, ChallengeHookData, Hook, HookEnvData, HookType, PostOperationHookData}; use crate::identifier::{Identifier, IdentifierType}; use crate::storage::{certificate_files_exists, get_certificate}; -use acme_common::crypto::X509Certificate; +use acme_common::crypto::{HashFunction, X509Certificate}; use acme_common::error::Error; use log::{debug, info, trace, warn}; use std::collections::{HashMap, HashSet}; @@ -47,6 +47,7 @@ pub struct Certificate { pub account: Account, pub identifiers: Vec, pub algo: Algorithm, + pub csr_digest: HashFunction, pub kp_reuse: bool, pub endpoint_name: String, pub hooks: Vec, diff --git a/acmed/src/config.rs b/acmed/src/config.rs index 0064fd8..585e925 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -2,6 +2,7 @@ use crate::certificate::Algorithm; use crate::duration::parse_duration; use crate::hooks; use crate::identifier::IdentifierType; +use acme_common::crypto::HashFunction; use acme_common::error::Error; use glob::glob; use log::info; @@ -298,6 +299,7 @@ pub struct Certificate { pub endpoint: String, pub identifiers: Vec, pub algorithm: Option, + pub csr_digest: Option, pub kp_reuse: Option, pub directory: Option, pub name: Option, @@ -328,6 +330,13 @@ impl Certificate { Algorithm::from_str(algo) } + pub fn get_csr_digest(&self) -> Result { + match &self.csr_digest { + Some(d) => d.parse(), + None => Ok(crate::DEFAULT_CSR_DIGEST), + } + } + pub fn get_identifiers(&self) -> Result, Error> { let mut ret = vec![]; for id in self.identifiers.iter() { diff --git a/acmed/src/main.rs b/acmed/src/main.rs index bfb26ae..7d38758 100644 --- a/acmed/src/main.rs +++ b/acmed/src/main.rs @@ -1,4 +1,5 @@ use crate::main_event_loop::MainEventLoop; +use acme_common::crypto::HashFunction; use acme_common::logs::{set_log_system, DEFAULT_LOG_LEVEL}; use acme_common::{clean_pid_file, crypto, init_server}; use clap::{App, Arg}; @@ -26,6 +27,7 @@ pub const DEFAULT_CERT_DIR: &str = "/etc/acmed/certs"; pub const DEFAULT_CERT_FORMAT: &str = "{{name}}_{{algo}}.{{file_type}}.{{ext}}"; pub const DEFAULT_SLEEP_TIME: u64 = 3600; pub const DEFAULT_POOL_TIME: u64 = 5000; +pub const DEFAULT_CSR_DIGEST: HashFunction = HashFunction::Sha256; pub const DEFAULT_CERT_FILE_MODE: u32 = 0o644; pub const DEFAULT_CERT_RENEW_DELAY: u64 = 1_814_400; // 1_814_400 is 3 weeks (3 * 7 * 24 * 60 * 60) pub const DEFAULT_PK_FILE_MODE: u32 = 0o600; diff --git a/acmed/src/main_event_loop.rs b/acmed/src/main_event_loop.rs index 76f0d6d..5e1a661 100644 --- a/acmed/src/main_event_loop.rs +++ b/acmed/src/main_event_loop.rs @@ -48,6 +48,7 @@ impl MainEventLoop { account: crt.get_account(&cnf)?, identifiers: crt.get_identifiers()?, algo: crt.get_algorithm()?, + csr_digest: crt.get_csr_digest()?, kp_reuse: crt.get_kp_reuse(), endpoint_name: endpoint_name.clone(), hooks: crt.get_hooks(&cnf)?, diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index 56bdc2c..7b7e55a 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -239,6 +239,17 @@ ecdsa_p256 .It ecdsa_p384 .El +.It Ic csr_digest Ar string +Name of the certificate's signing request digest algorithm. Possible values are : +.Bl -dash -compact +.It +sha256 +.Aq default +.It +sha384 +.It +sha512 +.El .It Ic kp_reuse Ar boolean Set whether or not the private key should be reused when renewing the certificate. Default is false. .It Ic directory Ar string