Browse Source

Add tls-alpn-01 challenge support

Although the standardization is still a draft, this challenge is already
supported by Let's Encrypt.

https://datatracker.ietf.org/doc/draft-ietf-acme-tls-alpn/
pull/5/head
Rodolphe Breard 6 years ago
parent
commit
0a7deb4cdc
  1. 1
      CHANGELOG.md
  2. 6
      README.md
  3. 4
      acmed/src/acme_proto.rs
  4. 33
      acmed/src/acme_proto/structs/authorization.rs

1
CHANGELOG.md

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- An account object has been added in the configuration.
- Failure recovery: HTTPS requests rejected by the server that are recoverable, like the badNonce error, are now retried several times before being considered a hard failure.
- The TLS-ALPN-01 challenge is now supported. The proof is a string representation of the acmeIdentifier extension. The self-signed certificate itself has to be built by a hook.
### Changed
- In the configuration, the `email` certificate field has been replaced by the `account` field which matches an account object.

6
README.md

@ -10,7 +10,7 @@ The Automatic Certificate Management Environment (ACME), is an internet standard
## Key features
- HTTP-01 and DNS-01 challenges
- http-01, dns-01 and tls-alpn-01 challenges
- RSA 2048, RSA 4096, ECDSA P-256 and ECDSA P-384 certificates
- Fully customizable challenge validation action
- Fully customizable archiving method (yes, you can use git or anything else)
@ -22,8 +22,8 @@ The Automatic Certificate Management Environment (ACME), is an internet standard
## Planned features
- TLS-ALPN challenges
- daemon and certificates management via the `acmectl` tool
- A standalone server dedicated to the tls-alpn-01 challenge validation
- Daemon and certificates management via the `acmectl` tool
## Build from source

4
acmed/src/acme_proto.rs

@ -19,6 +19,7 @@ pub mod structs;
pub enum Challenge {
Http01,
Dns01,
TlsAlpn01,
}
impl Challenge {
@ -26,6 +27,7 @@ impl Challenge {
match s.to_lowercase().as_str() {
"http-01" => Ok(Challenge::Http01),
"dns-01" => Ok(Challenge::Dns01),
"tls-alpn-01" => Ok(Challenge::TlsAlpn01),
_ => Err(format!("{}: unknown challenge.", s).into()),
}
}
@ -36,6 +38,7 @@ impl fmt::Display for Challenge {
let s = match self {
Challenge::Http01 => "http-01",
Challenge::Dns01 => "dns-01",
Challenge::TlsAlpn01 => "tls-alpn-01",
};
write!(f, "{}", s)
}
@ -46,6 +49,7 @@ impl PartialEq<structs::Challenge> for Challenge {
match (self, other) {
(Challenge::Http01, structs::Challenge::Http01(_)) => true,
(Challenge::Dns01, structs::Challenge::Dns01(_)) => true,
(Challenge::TlsAlpn01, structs::Challenge::TlsAlpn01(_)) => true,
_ => false,
}
}

33
acmed/src/acme_proto/structs/authorization.rs

@ -8,6 +8,11 @@ use serde::Deserialize;
use std::fmt;
use std::str::FromStr;
const ACME_OID: &str = "1.3.6.1.5.5.7.1";
const ID_PE_ACME_ID: usize = 31;
const DER_OCTET_STRING_ID: usize = 0x04;
const DER_STRUCT_NAME: &str = "DER";
#[derive(Deserialize)]
pub struct Authorization {
pub identifier: Identifier,
@ -59,7 +64,8 @@ pub enum Challenge {
Http01(TokenChallenge),
#[serde(rename = "dns-01")]
Dns01(TokenChallenge),
// TODO: tls-alpn-01
#[serde(rename = "tls-alpn-01")]
TlsAlpn01(TokenChallenge),
#[serde(other)]
Unknown,
}
@ -69,7 +75,9 @@ deserialize_from_str!(Challenge);
impl Challenge {
pub fn get_url(&self) -> String {
match self {
Challenge::Http01(tc) | Challenge::Dns01(tc) => tc.url.to_owned(),
Challenge::Http01(tc) | Challenge::Dns01(tc) | Challenge::TlsAlpn01(tc) => {
tc.url.to_owned()
}
Challenge::Unknown => String::new(),
}
}
@ -83,6 +91,25 @@ impl Challenge {
let a = b64_encode(&a);
Ok(a)
}
Challenge::TlsAlpn01(tc) => {
let acme_ext_name = format!("{}.{}", ACME_OID, ID_PE_ACME_ID);
let ka = tc.key_authorization(private_key)?;
let proof = sha256(ka.as_bytes());
let proof_str = proof
.iter()
.map(|e| format!("{:02x}", e))
.collect::<Vec<String>>()
.join(":");
let value = format!(
"critical,{}:{:02x}:{:02x}:{}",
DER_STRUCT_NAME,
DER_OCTET_STRING_ID,
proof.len(),
proof_str
);
let acme_ext = format!("{}={}", acme_ext_name, value);
Ok(acme_ext)
}
Challenge::Unknown => Ok(String::new()),
}
}
@ -90,7 +117,7 @@ impl Challenge {
pub fn get_file_name(&self) -> String {
match self {
Challenge::Http01(tc) => tc.token.to_owned(),
Challenge::Dns01(_) => String::new(),
Challenge::Dns01(_) | Challenge::TlsAlpn01(_) => String::new(),
Challenge::Unknown => String::new(),
}
}

Loading…
Cancel
Save