Transport-neutral ACME certificate automation for MoonBit
Dependencies
flowchart LR
App[MoonBit application] --> Session[AcmeSession]
Session --> JWS[JWS and nonce engine]
Session --> Flow[order workflow]
Flow --> Challenge[HTTP-01 / DNS-01 values]
Flow --> CSR[PKCS#10 builder]
App --> Transport[host HTTP adapter]
App --> Signer[HSM / keystore / signer]
Transport --> CA[ACME server]
Signer --> JWS
Signer --> CSRgit clone https://github.com/apoloe4/moonacme.git
cd moonacme
moon update
moon test --deny-warn --target wasmmoon run cmd/main -- dns01-name '*.example.com'
moon run cmd/main -- dns01-value TOKEN ACCOUNT_JWK_THUMBPRINT
moon run cmd/main -- http01-path TOKEN
moon run cmd/main -- http01-body TOKEN ACCOUNT_JWK_THUMBPRINT///|
let record = @moonacme.dns01_record_name("*.example.com")
///|
let value = @moonacme.dns01_txt_value(token, account_thumbprint)
///|
let resource = @moonacme.Http01Resource::new(token, account_thumbprint)let session = @moonacme.AcmeSession::new(
directory~,
algorithm="ES256",
public_jwk=canonical_public_jwk,
)
session.offer_nonce(replay_nonce) |> ignore
let draft = session.prepare_new_account(["mailto:ops@example.com"], true)
let signature = account_signer(draft.draft.signing_input)
let request = draft.finish(signature[:])pub(all) suberror AcmeError {
Malformed(stage~ : String, detail~ : String)
Protocol(problem~ : AcmeProblem)
MissingNonce
RetryExhausted(attempts~ : Int)
InvalidState(expected~ : String, actual~ : String)
Unsupported(feature~ : String)
} derive(Eq, Debug)pub(all) struct AcmeProblem {
kind : String
title : String?
detail : String
status : Int?
instance : String?
subproblems : Array[AcmeSubproblem]
} derive(Eq, Debug)fn AcmeSession::new(directory~ : Directory, algorithm~ : String, public_jwk~ : String) -> AcmeSession raise AcmeErrorfn AcmeSession::prepare_challenge_ack(self : AcmeSession, challenge_url : String) -> PreparedRequest raise AcmeErrorfn AcmeSession::prepare_download(self : AcmeSession, certificate_url : String) -> PreparedRequest raise AcmeErrorfn AcmeSession::prepare_finalize(self : AcmeSession, finalize_url : String, csr_der : BytesView) -> PreparedRequest raise AcmeErrorfn AcmeSession::prepare_new_account(self : AcmeSession, contacts : Array[String], terms_agreed : Bool) -> PreparedRequest raise AcmeErrorfn AcmeSession::prepare_new_order(self : AcmeSession, identifiers : Array[Identifier]) -> PreparedRequest raise AcmeErrorfn AcmeSession::prepare_post_as_get(self : AcmeSession, url : String, accept? : String) -> PreparedRequest raise AcmeErrorfn AcmeSession::prepare_revoke(self : AcmeSession, certificate_der : BytesView) -> PreparedRequest raise AcmeErrorpub(all) struct AcmeSubproblem {
kind : String
detail : String
identifier : Identifier?
} derive(Eq, Debug)pub(all) struct Authorization {
url : String
identifier : Identifier
status : ResourceStatus
expires : String?
wildcard : Bool
challenges : Array[Challenge]
} derive(Eq, Debug)pub(all) enum AuthorizationAction {
Provision(Array[Challenge])
PollAuthorization(String)
AuthorizationComplete
AuthorizationStopped(AcmeProblem?)
} derive(Eq, Debug)pub(all) struct Challenge {
kind : ChallengeKind
url : String
status : ResourceStatus
token : String
validated : String?
problem : AcmeProblem?
} derive(Eq, Debug)fn Http01Resource::new(token : String, account_thumbprint : String) -> Http01Resource raise AcmeErrorfn JwsDraft::new(algorithm~ : String, nonce~ : String, url~ : String, payload~ : String, binding~ : AccountBinding) -> JwsDraft raise AcmeErrorpub(all) struct Order {
url : String
status : ResourceStatus
identifiers : Array[Identifier]
authorization_urls : Array[String]
finalize_url : String
certificate_url : String?
expires : String?
problem : AcmeProblem?
} derive(Eq, Debug)pub(all) enum OrderAction {
FetchAuthorizations(Array[String])
Finalize(String)
Poll(String)
DownloadCertificate(String)
Stop(AcmeProblem?)
} derive(Eq, Debug)fn PreparedRequest::finish(self : PreparedRequest, signature : BytesView) -> HttpRequest raise AcmeErrorfn decide_bad_nonce_retry(problem : AcmeProblem, response_nonce : String?, attempt : Int, policy : RetryPolicy) -> RetryDecisionfn jwk_thumbprint(canonical_public_jwk : String) -> Stringfn plan_renewal(now : Int64, not_before : Int64, not_after : Int64, policy : RenewalPolicy, suggested? : RenewalWindow) -> RenewalDecisionfn validate_challenge_token(token : String) -> BoolInstall
Download zipTransport-neutral ACME certificate automation for MoonBit
Dependencies