acme-not-a-joke-0.1.0.0: src/Acme/NotAJoke/Api/Certificate.hs
module Acme.NotAJoke.Api.Certificate where
import Control.Lens hiding ((.=))
import Data.Aeson (encode)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as ByteString
import Data.Coerce (coerce)
import qualified Network.Wreq as Wreq
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 (Wreq.Response ByteString)
deriving (Show)
-- | A PEM-file representation of a certificate.
newtype PEM = PEM ByteString
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 $ rsp ^. Wreq.responseBody
-- | Retrieves a certificate from an URL.
postGetCertificate :: JWS.JWK -> KID -> Url "certificate" -> Nonce -> IO (Maybe Certificate)
postGetCertificate jwk kid certificateUrl nonce = do
let opts =
Wreq.defaults
& Wreq.header "Content-Type" .~ ["application/jose+json"]
& Wreq.header "Accept" .~ ["application/pem-certificate-chain"]
ebody <- (kidSign jwk ep kid nonce "")
case ebody of
Right body -> do
e <- Wreq.postWith opts (wrequrl ep) $ encode body
pure $ Just $ Certificate e
Left err -> do
print err
pure Nothing
where
ep :: Endpoint "certificate"
ep = coerce certificateUrl