This commit is contained in:
Nicholas Rempel 2022-11-30 10:47:34 -08:00
parent 583e181140
commit d47be239ff
4 changed files with 195 additions and 1 deletions

View File

@ -15,6 +15,7 @@ base64 = "0.13.0"
hyper = { version = "0.14.18", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.23.0", default-features = false, features = ["http1", "http2", "native-tokio", "tls12"] }
ring = { version = "0.16.20", features = ["std"] }
rustls = "0.20.7"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.78"
thiserror = "1.0.30"
@ -23,6 +24,7 @@ thiserror = "1.0.30"
anyhow = "1.0.66"
clap = { version = "4.0.29", features = ["derive"] }
rcgen = "0.10.0"
rustls-pemfile = "1.0.1"
tokio = { version = "1.22.0", features = ["rt", "macros", "rt-multi-thread"] }
tracing = "0.1.37"
tracing-subscriber = "0.3.16"

138
examples/revoke.rs Normal file
View File

@ -0,0 +1,138 @@
use std::{io, time::Duration};
use clap::Parser;
use rustls::Certificate;
use tokio::time::sleep;
use tracing::{error, info};
use instant_acme::{
Account, AuthorizationStatus, ChallengeType, Identifier, LetsEncrypt, NewAccount, NewOrder,
OrderStatus,
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
// let opts = Options::parse();
// Create a new account. This will generate a fresh ECDSA key for you.
// Alternatively, restore an account from serialized credentials by
// using `Account::from_credentials()`.
// let account = Account::create(
// &NewAccount {
// contact: &[],
// terms_of_service_agreed: true,
// only_return_existing: false,
// },
// LetsEncrypt::Staging.url(),
// )
// .await?;
let creds = serde_json::from_str(ACCOUNT).unwrap();
let account = Account::from_credentials(creds).unwrap();
let certs = rustls_pemfile::certs(&mut CERT).unwrap();
let cert = certs[0].clone();
account.revoke(&Certificate(cert)).await.unwrap();
// info!(
// "account credentials:\n\n{}",
// serde_json::to_string_pretty(&ACCOUNT.credentials()).unwrap()
// );
Ok(())
}
/// SMTP server
#[derive(Parser)]
struct Options {
// #[clap(long)]
// name: String,
}
const CERT: &[u8] = b"-----BEGIN CERTIFICATE-----
MIID8TCCA3igAwIBAgITAPrsbmSSkWU08+AFKwWnHLCgCjAKBggqhkjOPQQDAzBV
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXKFNUQUdJTkcpIExldCdzIEVuY3J5cHQx
JDAiBgNVBAMTGyhTVEFHSU5HKSBFcnNhdHogRWRhbWFtZSBFMTAeFw0yMjExMzAx
NzM2NTlaFw0yMzAyMjgxNzM2NThaMCExHzAdBgNVBAMTFnNhZ2U4OS5tZXNzd2l0
aGRucy5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS9Vx9GxJCvGffEZqgW
mEtBx6s8kUTGQp+K7afTae1sz/GrOnRnNYwGkQaRRaRkNiU6SoPSpaIEF7SppfLE
VQ+Ro4ICWTCCAlUwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMB
BggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTFI9bBN5i1NTThtA//
R7C3wt5ooDAfBgNVHSMEGDAWgBTr+SXCgChm4m0IkjLzwuGtw/81RTBdBggrBgEF
BQcBAQRRME8wJQYIKwYBBQUHMAGGGWh0dHA6Ly9zdGctZTEuby5sZW5jci5vcmcw
JgYIKwYBBQUHMAKGGmh0dHA6Ly9zdGctZTEuaS5sZW5jci5vcmcvMCEGA1UdEQQa
MBiCFnNhZ2U4OS5tZXNzd2l0aGRucy5jb20wTAYDVR0gBEUwQzAIBgZngQwBAgEw
NwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5j
cnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQDBgyQL8aRQx2+7AHJp
3Kw74ipIBdTb4Elmw8irxEewDAAAAYTJ0+x7AAAEAwBGMEQCIG9fm12nxSRo6Gik
ctP/4ee/xGivaY9YXO3Fj0gbMe1LAiBRBKCBBJ7INzVY2YFcfva0Uz373huQWmmP
7Cac9jPSRQB3ALDMg+Wl+X1rr3wJzChJBIcqx+iLEyxjULfG/SbhbGx3AAABhMnT
7GcAAAQDAEgwRgIhAPS0KdNvGSq1DCHRf9Uvp31YMkyFGInt6tMGVdJBqjVeAiEA
z812Z02g52+1vB3SbtT/Oo7Lgoal3GqvlsWf7JexST0wCgYIKoZIzj0EAwMDZwAw
ZAIwMGkJw9GmlG1qYrelKfeSnknSFphAoLHQCpnrhOjkYozDXk73tt/jl+5bo9dG
oPTQAjAh39Ocli9GwFDC+UP4aZBr1WDLGGodGpGMlPGBfqLLNMCqNiCf5BZC2xSC
TINoHwU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDCzCCApGgAwIBAgIRALRY4992FVxZJKOJ3bpffWIwCgYIKoZIzj0EAwMwaDEL
MAkGA1UEBhMCVVMxMzAxBgNVBAoTKihTVEFHSU5HKSBJbnRlcm5ldCBTZWN1cml0
eSBSZXNlYXJjaCBHcm91cDEkMCIGA1UEAxMbKFNUQUdJTkcpIEJvZ3VzIEJyb2Nj
b2xpIFgyMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2MDAwMFowVTELMAkGA1UE
BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSQwIgYDVQQD
ExsoU1RBR0lORykgRXJzYXR6IEVkYW1hbWUgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAT9v/PJUtHOTk28nXCXrpP665vI4Z094h8o7R+5E6yNajZa0UubqjpZFoGq
u785/vGXj6mdfIzc9boITGusZCSWeMj5ySMZGZkS+VSvf8VQqj+3YdEu4PLZEjBA
ivRFpEejggEQMIIBDDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH
AwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFOv5JcKA
KGbibQiSMvPC4a3D/zVFMB8GA1UdIwQYMBaAFN7Ro1lkDsGaNqNG7rAQdu+ul5Vm
MDYGCCsGAQUFBwEBBCowKDAmBggrBgEFBQcwAoYaaHR0cDovL3N0Zy14Mi5pLmxl
bmNyLm9yZy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3N0Zy14Mi5jLmxlbmNy
Lm9yZy8wIgYDVR0gBBswGTAIBgZngQwBAgEwDQYLKwYBBAGC3xMBAQEwCgYIKoZI
zj0EAwMDaAAwZQIwXcZbdgxcGH9rTErfSTkXfBKKygU0yO7OpbuNeY1id0FZ/hRY
N5fdLOGuc+aHfCsMAjEA0P/xwKr6NQ9MN7vrfGAzO397PApdqfM7VdFK18aEu1xm
3HMFKzIR8eEPsMx4smMl
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEmTCCAoGgAwIBAgIRAJJVIr2Em/sOzhBD2bEnEJwwDQYJKoZIhvcNAQELBQAw
ZjELMAkGA1UEBhMCVVMxMzAxBgNVBAoTKihTVEFHSU5HKSBJbnRlcm5ldCBTZWN1
cml0eSBSZXNlYXJjaCBHcm91cDEiMCAGA1UEAxMZKFNUQUdJTkcpIFByZXRlbmQg
UGVhciBYMTAeFw0yMDA5MDQwMDAwMDBaFw0yNTA5MTUxNjAwMDBaMGgxCzAJBgNV
BAYTAlVTMTMwMQYDVQQKEyooU1RBR0lORykgSW50ZXJuZXQgU2VjdXJpdHkgUmVz
ZWFyY2ggR3JvdXAxJDAiBgNVBAMTGyhTVEFHSU5HKSBCb2d1cyBCcm9jY29saSBY
MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABDr0vsNZAswMWDiWwNOgMNBxT9rSwSyj
6BUKkfQDLJJdZwtve+XkKsnEfgAr2HpQPK38BVzmzB2Fydt1ywfnQIzyVTidjnLI
01ajuHXA1rvq0NlSC3ZyUWMqZ1dTDE4VcaOB7TCB6jAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU3tGjWWQOwZo2o0busBB2766XlWYw
HwYDVR0jBBgwFoAUtfNl8v6wCpIf+zx980SgrGMlwxQwNgYIKwYBBQUHAQEEKjAo
MCYGCCsGAQUFBzAChhpodHRwOi8vc3RnLXgxLmkubGVuY3Iub3JnLzArBgNVHR8E
JDAiMCCgHqAchhpodHRwOi8vc3RnLXgxLmMubGVuY3Iub3JnLzAiBgNVHSAEGzAZ
MAgGBmeBDAECATANBgsrBgEEAYLfEwEBATANBgkqhkiG9w0BAQsFAAOCAgEAMkp5
etLOxM4+a6EqX2hmAd+yNUSNCA7+MYn/VrwJnpkWe8zuC+fILYMYRuByWs/zeFmo
56Jc7td5N9I+QN0rYSeEbgdTAMeaBjZ3P6eJxM1Aa76Abrj5ULfq8XhOE37SYgFb
ZS9YPOQ4wuisCXHrrmu4ZdZJmzXIQX562xBeJxf0o4LBqS2C3SmpkPY+f8lTtmFO
/I6qSSl8T5XyNE385zNXaRd8rMJqNC9fIHDjPeJMIaou0TZYT0uNb9OZ7ZhT7smQ
SaHcGxtK0SVmJvGNagc6RldrHFbemLbwVpeI4NopRHynQqzkVtsfAlK8VD92SYbp
olFsJZWuHVkHgccuI1Hx0+RUp1VGj1PPV+0JmGZeG2ybLloU2rjjMbRmkNjTxub2
U1vzCGpBSaBfYQLjLHDwQk1AqRENlZxDqCkXFro8eqT6TFHdtw27KIT+ov1Qyofi
q3Uj1w7tPpcFMSDfiWNRE0XGYCjELDo19oPqQthIMQ5X+/3YpCqZceR4vMR6n9ol
Lp/0KmjAzqU+LqD2fmFLttKvZUxW8aECTGIcDHGCPJDklwDW3l7DUQ08Wj5Fh/KE
f5c9fF3u87WUAJu4Vh9C+ewXZtzL0LD46lYgpn7fv5w9sLS4zQ3CIC3udjJ5Gc/v
8VhPQaU1Enn7NW+4IHnfSeP6G5rzLEtl0PreC4k=
-----END CERTIFICATE-----";
const ACCOUNT: &str = "
{
\"id\": \"https://acme-staging-v02.api.letsencrypt.org/acme/acct/78060154\",
\"key_pkcs8\": \"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgvjyueuqwXkRd0jjqRsYO6cIwvIctiPt1gqlLgMppwiehRANCAATUfP9I90-kJqSfW0ocUSG8Rmt6Du4U6goTdfx7as2ThDAsCYnFJlqOqe2RKJfeHfjMD_0Ks0N_PAFey8agM41h\",
\"urls\": {
\"newNonce\": \"https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce\",
\"newAccount\": \"https://acme-staging-v02.api.letsencrypt.org/acme/new-acct\",
\"newOrder\": \"https://acme-staging-v02.api.letsencrypt.org/acme/new-order\",
\"revokeCert\": \"https://acme-staging-v02.api.letsencrypt.org/acme/revoke-cert\"
}
}
";

View File

@ -14,6 +14,7 @@ use hyper::{Body, Method, Request, Response};
use ring::digest::{digest, SHA256};
use ring::rand::SystemRandom;
use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING};
use rustls::Certificate;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -23,7 +24,8 @@ pub use types::{
Identifier, LetsEncrypt, NewAccount, NewOrder, OrderState, OrderStatus, Problem,
};
use types::{
DirectoryUrls, Empty, FinalizeRequest, Header, JoseJson, Jwk, KeyOrKeyId, SigningAlgorithm,
DirectoryUrls, Empty, FinalizeRequest, Header, JoseJson, Jwk, KeyOrKeyId, RevocationRequest,
SigningAlgorithm,
};
/// An ACME order as described in RFC 8555 (section 7.1.3)
@ -219,6 +221,21 @@ impl Account {
pub fn credentials(&self) -> AccountCredentials<'_> {
self.inner.credentials()
}
/// Revoke a certificate
pub async fn revoke(&self, certificate: &Certificate) -> Result<(), Error> {
let rsp = self
.inner
.post(
Some(&RevocationRequest::new(certificate)),
None,
&self.inner.client.urls.revoke_cert,
)
.await?;
let _ = Problem::from_response(rsp).await?;
Ok(())
}
}
struct AccountInner {
@ -285,6 +302,28 @@ impl Signer for AccountInner {
}
}
// struct Revocation {
// account: Arc<AccountInner>,
// certificate: Certificate,
// }
// impl Revocation {
// async fn revoke(&self) -> Result<(), Error> {
// let rsp = self
// .account
// .post(
// Some(&RevocationRequest {
// certificate: &self.certificate,
// }),
// None,
// &self.account.client.urls.revoke_cert,
// )
// .await?;
// Problem::check(rsp).await?;
// Ok(())
// }
// }
#[derive(Debug)]
struct Client {
client: hyper::Client<hyper_rustls::HttpsConnector<HttpConnector>>,

View File

@ -5,6 +5,7 @@ use base64::URL_SAFE_NO_PAD;
use hyper::{Body, Response};
use ring::digest::{digest, Digest, SHA256};
use ring::signature::{EcdsaKeyPair, KeyPair};
use rustls::Certificate;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -252,6 +253,7 @@ pub(crate) struct DirectoryUrls {
pub(crate) new_nonce: String,
pub(crate) new_account: String,
pub(crate) new_order: String,
pub(crate) revoke_cert: String,
}
#[derive(Serialize)]
@ -353,3 +355,16 @@ pub(crate) enum SigningAlgorithm {
#[derive(Debug, Serialize)]
pub(crate) struct Empty {}
#[derive(Debug, Serialize)]
pub(crate) struct RevocationRequest {
certificate: String,
}
impl RevocationRequest {
pub(crate) fn new(certificate: &Certificate) -> Self {
Self {
certificate: base64::encode_config(&certificate, base64::URL_SAFE_NO_PAD),
}
}
}