Add certificate revocation (#38)
This commit is contained in:
parent
a800b1bff7
commit
8290a95649
|
@ -19,6 +19,7 @@ base64 = "0.21.0"
|
||||||
hyper = { version = "0.14.18", features = ["client", "http1", "http2"] }
|
hyper = { version = "0.14.18", features = ["client", "http1", "http2"] }
|
||||||
hyper-rustls = { version = "0.24", default-features = false, features = ["http1", "http2", "native-tokio", "tls12"], optional = true }
|
hyper-rustls = { version = "0.24", default-features = false, features = ["http1", "http2", "native-tokio", "tls12"], optional = true }
|
||||||
ring = { version = "0.17", features = ["std"] }
|
ring = { version = "0.17", features = ["std"] }
|
||||||
|
rustls-pki-types = "1.1.0"
|
||||||
serde = { version = "1.0.104", features = ["derive"] }
|
serde = { version = "1.0.104", features = ["derive"] }
|
||||||
serde_json = "1.0.78"
|
serde_json = "1.0.78"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
|
|
16
src/lib.rs
16
src/lib.rs
|
@ -24,7 +24,8 @@ use serde::Serialize;
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
AccountCredentials, Authorization, AuthorizationStatus, Challenge, ChallengeType, Error,
|
AccountCredentials, Authorization, AuthorizationStatus, Challenge, ChallengeType, Error,
|
||||||
Identifier, LetsEncrypt, NewAccount, NewOrder, OrderState, OrderStatus, Problem, ZeroSsl,
|
Identifier, LetsEncrypt, NewAccount, NewOrder, OrderState, OrderStatus, Problem,
|
||||||
|
RevocationReason, RevocationRequest, ZeroSsl,
|
||||||
};
|
};
|
||||||
use types::{
|
use types::{
|
||||||
DirectoryUrls, Empty, FinalizeRequest, Header, JoseJson, Jwk, KeyOrKeyId, NewAccountPayload,
|
DirectoryUrls, Empty, FinalizeRequest, Header, JoseJson, Jwk, KeyOrKeyId, NewAccountPayload,
|
||||||
|
@ -362,6 +363,17 @@ impl Account {
|
||||||
url: order_url.ok_or("no order URL found")?,
|
url: order_url.ok_or("no order URL found")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Revokes a previously issued certificate
|
||||||
|
pub async fn revoke<'a>(&'a self, payload: &RevocationRequest<'a>) -> Result<(), Error> {
|
||||||
|
let rsp = self
|
||||||
|
.inner
|
||||||
|
.post(Some(payload), None, &self.inner.client.urls.revoke_cert)
|
||||||
|
.await?;
|
||||||
|
// The body is empty if the request was successful
|
||||||
|
let _ = Problem::from_response(rsp).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AccountInner {
|
struct AccountInner {
|
||||||
|
@ -697,7 +709,7 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn deserialize_old_credentials() -> Result<(), Error> {
|
async fn deserialize_old_credentials() -> Result<(), Error> {
|
||||||
const CREDENTIALS: &str = r#"{"id":"id","key_pkcs8":"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJVWC_QzOTCS5vtsJp2IG-UDc8cdDfeoKtxSZxaznM-mhRANCAAQenCPoGgPFTdPJ7VLLKt56RxPlYT1wNXnHc54PEyBg3LxKaH0-sJkX0mL8LyPEdsfL_Oz4TxHkWLJGrXVtNhfH","urls":{"newNonce":"new-nonce","newAccount":"new-acct","newOrder":"new-order"}}"#;
|
const CREDENTIALS: &str = r#"{"id":"id","key_pkcs8":"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJVWC_QzOTCS5vtsJp2IG-UDc8cdDfeoKtxSZxaznM-mhRANCAAQenCPoGgPFTdPJ7VLLKt56RxPlYT1wNXnHc54PEyBg3LxKaH0-sJkX0mL8LyPEdsfL_Oz4TxHkWLJGrXVtNhfH","urls":{"newNonce":"new-nonce","newAccount":"new-acct","newOrder":"new-order", "revokeCert": "revoke-cert"}}"#;
|
||||||
Account::from_credentials(serde_json::from_str::<AccountCredentials>(CREDENTIALS)?).await?;
|
Account::from_credentials(serde_json::from_str::<AccountCredentials>(CREDENTIALS)?).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
49
src/types.rs
49
src/types.rs
|
@ -4,7 +4,9 @@ use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
|
||||||
use hyper::{Body, Response};
|
use hyper::{Body, Response};
|
||||||
use ring::digest::{digest, Digest, SHA256};
|
use ring::digest::{digest, Digest, SHA256};
|
||||||
use ring::signature::{EcdsaKeyPair, KeyPair};
|
use ring::signature::{EcdsaKeyPair, KeyPair};
|
||||||
|
use rustls_pki_types::CertificateDer;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde::ser::SerializeMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -271,6 +273,52 @@ pub struct NewOrder<'a> {
|
||||||
pub identifiers: &'a [Identifier],
|
pub identifiers: &'a [Identifier],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Payload for a certificate revocation request
|
||||||
|
/// Defined in <https://datatracker.ietf.org/doc/html/rfc8555#section-7.6>
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RevocationRequest<'a> {
|
||||||
|
/// The certificate to revoke
|
||||||
|
pub certificate: &'a CertificateDer<'a>,
|
||||||
|
/// Reason for revocation
|
||||||
|
pub reason: Option<RevocationReason>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for RevocationRequest<'a> {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
let base64 = BASE64_URL_SAFE_NO_PAD.encode(self.certificate);
|
||||||
|
let mut map = serializer.serialize_map(Some(2))?;
|
||||||
|
map.serialize_entry("certificate", &base64)?;
|
||||||
|
if let Some(reason) = &self.reason {
|
||||||
|
map.serialize_entry("reason", reason)?;
|
||||||
|
}
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The reason for a certificate revocation
|
||||||
|
/// Defined in <https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1>
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum RevocationReason {
|
||||||
|
Unspecified = 0,
|
||||||
|
KeyCompromise = 1,
|
||||||
|
CaCompromise = 2,
|
||||||
|
AffiliationChanged = 3,
|
||||||
|
Superseded = 4,
|
||||||
|
CessationOfOperation = 5,
|
||||||
|
CertificateHold = 6,
|
||||||
|
RemoveFromCrl = 8,
|
||||||
|
PrivilegeWithdrawn = 9,
|
||||||
|
AaCompromise = 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for RevocationReason {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serializer.serialize_u8(self.clone() as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct NewAccountPayload<'a> {
|
pub(crate) struct NewAccountPayload<'a> {
|
||||||
|
@ -302,6 +350,7 @@ pub(crate) struct DirectoryUrls {
|
||||||
pub(crate) new_nonce: String,
|
pub(crate) new_nonce: String,
|
||||||
pub(crate) new_account: String,
|
pub(crate) new_account: String,
|
||||||
pub(crate) new_order: String,
|
pub(crate) new_order: String,
|
||||||
|
pub(crate) revoke_cert: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
Loading…
Reference in New Issue