packages feed

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