module Network.AWS.CloudFront.SignedCookies.Crypto ( -- * Reading the private key readPrivateKeyPemFile -- * Generating signatures , sign -- * Types , PrivateKey , ByteString ) where import Network.AWS.CloudFront.SignedCookies.Crypto.Internal (rsaPrivateKeyFromASN1) import Network.AWS.CloudFront.SignedCookies.Types (ByteString, PemFilePath (..), PrivateKey) -- asn1-encoding import Data.ASN1.BinaryEncoding (DER (DER)) import Data.ASN1.Encoding (decodeASN1') -- asn1-types import Data.ASN1.Types (ASN1) -- bytestring import qualified Data.ByteString as BS -- cryptonite import Crypto.Hash.Algorithms (SHA1 (SHA1)) import qualified Crypto.PubKey.RSA.PKCS15 as RSA -- pem import qualified Data.PEM as PEM -- text import qualified Data.Text as Text -- | Construct the signature that will go into the -- @CloudFront-Signature@ cookie. sign :: PrivateKey -- ^ The RSA private key that you read from the @.pem@ file -> ByteString -- ^ The JSON representation of the 'Policy' -- (see "Network.AWS.CloudFront.SignedCookies.Policy") -> IO ByteString sign key bs = RSA.signSafer (Just SHA1) key bs >>= either (fail . show) pure -- | Read an RSA private key from a @.pem@ file you downloaded from AWS. readPrivateKeyPemFile :: PemFilePath -- ^ The filesystem path of the @.pem@ file -> IO PrivateKey readPrivateKeyPemFile (PemFilePath path) = do lbs <- BS.readFile (Text.unpack path) pemSections <- either fail pure (PEM.pemParseBS lbs) pemBs <- PEM.pemContent <$> case pemSections of [x] -> pure x xs -> let msg = "Expected exactly 1 PEM section but found " ++ show @Int (length xs) in fail msg asn1s :: [ASN1] <- either (fail . show) pure (decodeASN1' DER pemBs) either fail pure (rsaPrivateKeyFromASN1 asn1s)