Browse Source

Allow to specify the account key type and signature alg in the config

pull/39/head
Rodolphe Breard 5 years ago
parent
commit
582593de29
  1. 3
      CHANGELOG.md
  2. 6
      README.md
  3. 2
      acme_common/src/crypto/key_type.rs
  4. 11
      acmed/src/acme_proto.rs
  5. 26
      acmed/src/acme_proto/account.rs
  6. 11
      acmed/src/config.rs
  7. 17
      acmed/src/endpoint.rs
  8. 10
      acmed/src/jws.rs
  9. 2
      acmed/src/main_event_loop.rs
  10. 32
      man/en/acmed.toml.5

3
CHANGELOG.md

@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- The account key type and signature algorithm can now be specified in the configuration.
### Fixed
- The Makefile now works on FreeBSD. It should also work on other BSD although it has not been tested.

6
README.md

@ -117,7 +117,7 @@ An example service file is provided (see `contrib/acmed.service.example`). The f
It depends on your definition of a beginner. This software is intended to be used by system administrator with a certain knowledge of their environment. Furthermore, it is also expected to know the bases of the ACME protocol. Let's Encrypt wrote a nice article about [how it works](https://letsencrypt.org/how-it-works/).
### Why is RSA 2048 the default?
### Why is RSA 2048 the default certificate key type?
Short answer: it is sufficiently secured, has good performances and is wildly supported.
@ -128,3 +128,7 @@ Before choosing a different algorithm for your certificate's signature, you migh
* Nowadays, every client support ECDSA support. So, unless you have very specific requirements, you can safely use it. At time of writing, EdDSA certificates are not yet supported, but it might become a thing in the future.
Currently, security and client support aren't the main concerns since every possible type of certificates is good enough on those two points. The performances clearly favors ECDSA P-256, Ed25519 and RSA 2048. The later has been chosen as the default because it's the most wildly used as Certification Authorities root and intermediate certificates. This choice could change in favor of ECDSA once Let's Encrypt issues [a full ECDSA certificates chain](https://community.letsencrypt.org/t/lets-encrypt-new-hierarchy-plans/125517).
### Why is ECDSA P-256 the default account key type?
RFC 8555 section 6.2 defines ECDSA P-256 as the only account key type that any ACME servers must implement. It is therefore the best choice for the default value.

2
acme_common/src/crypto/key_type.rs

@ -3,7 +3,7 @@ use crate::error::Error;
use std::fmt;
use std::str::FromStr;
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum KeyType {
Rsa2048,
Rsa4096,

11
acmed/src/acme_proto.rs

@ -58,7 +58,16 @@ impl PartialEq<structs::Challenge> for Challenge {
macro_rules! set_data_builder {
($account: ident, $data: expr) => {
|n: &str, url: &str| encode_kid(&$account.key_pair, &$account.account_url, $data, url, n)
|n: &str, url: &str| {
encode_kid(
&$account.key_pair,
&$account.signature_algorithm,
&$account.account_url,
$data,
url,
n,
)
}
};
}
macro_rules! set_empty_data_builder {

26
acmed/src/acme_proto/account.rs

@ -4,11 +4,12 @@ use crate::certificate::Certificate;
use crate::endpoint::Endpoint;
use crate::jws::encode_jwk;
use crate::storage;
use acme_common::crypto::{gen_keypair, KeyPair};
use acme_common::crypto::{gen_keypair, JwsSignatureAlgorithm, KeyPair};
use acme_common::error::Error;
pub struct AccountManager {
pub key_pair: KeyPair,
pub signature_algorithm: JwsSignatureAlgorithm,
pub account_url: String,
pub orders_url: String,
}
@ -21,14 +22,18 @@ impl AccountManager {
) -> Result<Self, Error> {
// TODO: store the key id (account url)
let key_pair = storage::get_account_keypair(cert)?;
let signature_algorithm = endpoint.signature_algorithm;
let kp_ref = &key_pair;
let account = Account::new(cert, endpoint);
let account = serde_json::to_string(&account)?;
let acc_ref = &account;
let data_builder = |n: &str, url: &str| encode_jwk(kp_ref, acc_ref.as_bytes(), url, n);
let data_builder = |n: &str, url: &str| {
encode_jwk(kp_ref, &signature_algorithm, acc_ref.as_bytes(), url, n)
};
let (acc_rep, account_url) = http::new_account(endpoint, root_certs, &data_builder)?;
let ac = AccountManager {
key_pair,
signature_algorithm,
account_url,
orders_url: acc_rep.orders.unwrap_or_default(),
};
@ -37,16 +42,21 @@ impl AccountManager {
}
}
pub fn init_account(cert: &Certificate) -> Result<(), Error> {
pub fn init_account(cert: &Certificate, endpoint: &Endpoint) -> Result<(), Error> {
if !storage::account_files_exists(cert) {
// TODO: allow to change the account key type
let key_pair = gen_keypair(crate::DEFAULT_ACCOUNT_KEY_TYPE)?;
let key_pair = gen_keypair(endpoint.key_type)?;
storage::set_account_keypair(cert, &key_pair)?;
cert.info(&format!("Account {} created", &cert.account.name));
} else {
// TODO: check if the keys are suitable for the specified signature algorithm
// and, if not, initiate a key rollover.
cert.debug(&format!("Account {} already exists", &cert.account.name));
let key_pair = storage::get_account_keypair(cert)?;
if key_pair.key_type != endpoint.key_type {
cert.debug(&format!("Account {} has a key pair of type {} while {} was expected. Creating a new {} key pair.", &cert.account.name, key_pair.key_type, endpoint.key_type, endpoint.key_type));
// TODO: Do a propper key rollover?
let key_pair = gen_keypair(endpoint.key_type)?;
storage::set_account_keypair(cert, &key_pair)?;
} else {
cert.debug(&format!("Account {} already exists", &cert.account.name));
}
}
Ok(())
}

11
acmed/src/config.rs

@ -181,6 +181,8 @@ pub struct Endpoint {
pub tos_agreed: bool,
#[serde(default)]
pub rate_limits: Vec<String>,
pub key_type: Option<String>,
pub signature_algorithm: Option<String>,
}
impl Endpoint {
@ -190,7 +192,14 @@ impl Endpoint {
let (nb, timeframe) = cnf.get_rate_limit(&rl_name)?;
limits.push((nb, timeframe));
}
crate::endpoint::Endpoint::new(&self.name, &self.url, self.tos_agreed, &limits)
crate::endpoint::Endpoint::new(
&self.name,
&self.url,
self.tos_agreed,
&limits,
&self.key_type,
&self.signature_algorithm,
)
}
}

17
acmed/src/endpoint.rs

@ -1,4 +1,5 @@
use crate::acme_proto::structs::Directory;
use acme_common::crypto::{JwsSignatureAlgorithm, KeyType};
use acme_common::error::Error;
use nom::bytes::complete::take_while_m_n;
use nom::character::complete::digit1;
@ -6,6 +7,7 @@ use nom::combinator::map_res;
use nom::multi::fold_many1;
use nom::IResult;
use std::cmp;
use std::str::FromStr;
use std::thread;
use std::time::{Duration, Instant};
@ -17,6 +19,8 @@ pub struct Endpoint {
pub nonce: Option<String>,
pub rl: RateLimit,
pub dir: Directory,
pub key_type: KeyType,
pub signature_algorithm: JwsSignatureAlgorithm,
}
impl Endpoint {
@ -25,8 +29,19 @@ impl Endpoint {
url: &str,
tos_agreed: bool,
limits: &[(usize, String)],
key_type: &Option<String>,
signature_algorithm: &Option<String>,
) -> Result<Self, Error> {
let rl = RateLimit::new(limits)?;
let key_type = match key_type {
Some(kt) => KeyType::from_str(&kt)?,
None => crate::DEFAULT_ACCOUNT_KEY_TYPE,
};
let signature_algorithm = match signature_algorithm {
Some(sa) => JwsSignatureAlgorithm::from_str(&sa)?,
None => key_type.get_default_signature_alg(),
};
let _ = key_type.check_alg_compatibility(&signature_algorithm)?;
Ok(Self {
name: name.to_string(),
url: url.to_string(),
@ -42,6 +57,8 @@ impl Endpoint {
revoke_cert: String::new(),
key_change: String::new(),
},
key_type,
signature_algorithm,
})
}
}

10
acmed/src/jws.rs

@ -49,12 +49,11 @@ fn get_data(
pub fn encode_jwk(
key_pair: &KeyPair,
sign_alg: &JwsSignatureAlgorithm,
payload: &[u8],
url: &str,
nonce: &str,
) -> Result<String, Error> {
// TODO: allow to change the signature algo
let sign_alg = key_pair.key_type.get_default_signature_alg();
let protected = JwsProtectedHeaderJwk {
alg: sign_alg.to_string(),
jwk: key_pair.jwk_public_key()?,
@ -62,18 +61,17 @@ pub fn encode_jwk(
url: url.into(),
};
let protected = serde_json::to_string(&protected)?;
get_data(key_pair, &sign_alg, &protected, payload)
get_data(key_pair, sign_alg, &protected, payload)
}
pub fn encode_kid(
key_pair: &KeyPair,
sign_alg: &JwsSignatureAlgorithm,
key_id: &str,
payload: &[u8],
url: &str,
nonce: &str,
) -> Result<String, Error> {
// TODO: allow to change the signature algo
let sign_alg = key_pair.key_type.get_default_signature_alg();
let protected = JwsProtectedHeaderKid {
alg: sign_alg.to_string(),
kid: key_id.to_string(),
@ -81,7 +79,7 @@ pub fn encode_kid(
url: url.into(),
};
let protected = serde_json::to_string(&protected)?;
get_data(key_pair, &sign_alg, &protected, payload)
get_data(key_pair, sign_alg, &protected, payload)
}
#[cfg(test)]

2
acmed/src/main_event_loop.rs

@ -64,10 +64,10 @@ impl MainEventLoop {
env: crt.env.to_owned(),
id: i + 1,
};
init_account(&cert, &endpoint)?;
endpoints
.entry(endpoint_name)
.or_insert_with(|| Arc::new(RwLock::new(endpoint)));
init_account(&cert)?;
certs.push(cert);
}

32
man/en/acmed.toml.5

@ -89,6 +89,29 @@ Set whether or not the user agrees to the Terms Of Service
.Pq TOS .
.It Cm url Ar string
The endpoint's directory URL.
.It Cm key_type Ar string
Name of the asymmetric cryptography algorithm used to generate the client account's key pair. Possible values are :
.Bl -dash -compact
.It
rsa2048
.It
rsa4096
.It
ecdsa_p256
.Aq default
.It
ecdsa_p384
.El
.It Cm signature_algorithm Ar string
Name of the signature algorithm used to sign the messages sent to the endpoint. The default value is derived from the key type. Possible values are:
.Bl -dash -compact
.It
RS256
.It
ES256
.It
ES384
.El
.El
.It Ic hook
Array of table where each element defines a command that will be launched at a defined point. See section
@ -186,7 +209,7 @@ The domain name.
Table of environment variables that will be accessible from hooks.
.El
.It Ic algorithm Ar string
Name of the asymetric cryptography algorithm used to generate the certificate's key pair. Possible values are :
Name of the asymmetric cryptography algorithm used to generate the certificate's key pair. Possible values are :
.Bl -dash -compact
.It
rsa2048
@ -628,6 +651,13 @@ status: {{status}}"""
.%T Handlebars
.%U https://handlebarsjs.com/
.Re
.It
.Rs
.%A M. Jones
.%D May 2015
.%R RFC 7518
.%T JSON Web Algorithms (JWA)
.Re
.El
.Sh AUTHORS
.An Rodolphe Bréard

Loading…
Cancel
Save