acme-not-a-joke-0.2.0.0: src/Acme/NotAJoke/Api/Certificate.hs
{- | Downloading the certificate of a valid order (RFC-8555 section 7.4.2).
-}
module Acme.NotAJoke.Api.Certificate where
import Data.Aeson (encode)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as ByteString
import Data.Coerce (coerce)
import qualified Network.HTTP.Client as HTTP
import qualified Crypto.JOSE.JWS as JWS
import Acme.NotAJoke.Api.Endpoint
import Acme.NotAJoke.Api.JWS
import Acme.NotAJoke.Api.Nonce
{- | The goal of the whole ACME dance is to retrieve such Certificate.
You should be able to figure out most of the ACME flow by looking how you
build a Certificate and pulling all the dependencies.
-}
newtype Certificate = Certificate Response
deriving (Show)
{- | A PEM-file representation of a certificate: the certificate followed by
the intermediate certificates of the chain (see
'Acme.NotAJoke.CertManagement.decodeCertificateChain').
-}
newtype PEM = PEM ByteString
-- | Writes the certificate chain to a PEM file (overwriting the file).
storeCert :: FilePath -> Certificate -> IO ()
storeCert path cert = ByteString.writeFile path (coerce $ readPEM cert)
-- | Lookup a PEM from a certificate.
readPEM :: Certificate -> PEM
readPEM (Certificate rsp) = PEM $ HTTP.responseBody rsp
{- | Retrieves a certificate from an URL (see
'Acme.NotAJoke.Api.Order.readCertificateUrl').
-}
postGetCertificate :: JWS.JWK -> KID -> Url "certificate" -> Nonce -> IO (Either AcmeError Certificate)
postGetCertificate jwk kid certificateUrl nonce = do
let headers = [("Accept", "application/pem-certificate-chain")]
ebody <- (kidSign jwk ep kid nonce "")
case ebody of
Right body -> do
e <- postJoseWith headers ep $ encode body
pure $ fmap Certificate e
Left err -> do
pure $ Left $ SigningFailed err
where
ep :: Endpoint "certificate"
ep = coerce certificateUrl