crypton-x509-1.8.0: Data/X509/Cert.hs
{-# LANGUAGE FlexibleContexts #-}
-- |
-- Module : Data.X509.Cert
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : unknown
--
-- X.509 Certificate types and functions
module Data.X509.Cert (Certificate (..)) where
import Control.Applicative ((<$>), (<*>))
import Data.ASN1.Types
import Data.Hourglass
import Data.X509.AlgorithmIdentifier
import Data.X509.DistinguishedName
import Data.X509.ExtensionRaw
import Data.X509.Internal
import Data.X509.PublicKey
data CertKeyUsage
= CertKeyUsageDigitalSignature
| CertKeyUsageNonRepudiation
| CertKeyUsageKeyEncipherment
| CertKeyUsageDataEncipherment
| CertKeyUsageKeyAgreement
| CertKeyUsageKeyCertSign
| CertKeyUsageCRLSign
| CertKeyUsageEncipherOnly
| CertKeyUsageDecipherOnly
deriving (Show, Eq)
-- | X.509 Certificate type.
--
-- This type doesn't include the signature, it's describe in the RFC
-- as tbsCertificate.
data Certificate = Certificate
{ certVersion :: Int
-- ^ Version
, certSerial :: Integer
-- ^ Serial number
, certSignatureAlg :: SignatureALG
-- ^ Signature algorithm
, certIssuerDN :: DistinguishedName
-- ^ Issuer DN
, certValidity :: (DateTime, DateTime)
-- ^ Validity period (UTC)
, certSubjectDN :: DistinguishedName
-- ^ Subject DN
, certPubKey :: PubKey
-- ^ Public key
, certExtensions :: Extensions
-- ^ Extensions
}
deriving (Show, Eq)
instance ASN1Object Certificate where
toASN1 certificate = \xs -> encodeCertificateHeader certificate ++ xs
fromASN1 s = runParseASN1State parseCertificate s
parseCertHeaderVersion :: ParseASN1 Int
parseCertHeaderVersion =
maybe 0 id <$> onNextContainerMaybe (Container Context 0) (getNext >>= getVer)
where
getVer (IntVal v) = return $ fromIntegral v
getVer _ = throwParseError "unexpected type for version"
parseCertHeaderSerial :: ParseASN1 Integer
parseCertHeaderSerial = do
n <- getNext
case n of
IntVal v -> return v
_ -> throwParseError ("missing serial" ++ show n)
parseCertHeaderValidity :: ParseASN1 (DateTime, DateTime)
parseCertHeaderValidity = getNextContainer Sequence >>= toTimeBound
where
toTimeBound [ASN1Time _ t1 _, ASN1Time _ t2 _] = return (t1, t2)
toTimeBound _ = throwParseError "bad validity format"
-- | parse header structure of a x509 certificate. the structure is the following:
-- Version
-- Serial Number
-- Algorithm ID
-- Issuer
-- Validity
-- Not Before
-- Not After
-- Subject
-- Subject Public Key Info
-- Public Key Algorithm
-- Subject Public Key
-- Issuer Unique Identifier (Optional) (>= 2)
-- Subject Unique Identifier (Optional) (>= 2)
-- Extensions (Optional) (>= v3)
parseExtensions :: ParseASN1 Extensions
parseExtensions = fmap adapt $ onNextContainerMaybe (Container Context 3) $ getObject
where
adapt (Just e) = e
adapt Nothing = Extensions Nothing
parseCertificate :: ParseASN1 Certificate
parseCertificate =
Certificate
<$> parseCertHeaderVersion
<*> parseCertHeaderSerial
<*> getObject
<*> getObject
<*> parseCertHeaderValidity
<*> getObject
<*> getObject
<*> parseExtensions
encodeCertificateHeader :: Certificate -> [ASN1]
encodeCertificateHeader cert =
eVer
++ eSerial
++ eAlgId
++ eIssuer
++ eValidity
++ eSubject
++ epkinfo
++ eexts
where
eVer =
asn1Container (Container Context 0) [IntVal (fromIntegral $ certVersion cert)]
eSerial = [IntVal $ certSerial cert]
eAlgId = toASN1 (certSignatureAlg cert) []
eIssuer = toASN1 (certIssuerDN cert) []
(t1, t2) = certValidity cert
eValidity =
asn1Container
Sequence
[ ASN1Time (timeType t1) t1 (Just (TimezoneOffset 0))
, ASN1Time (timeType t2) t2 (Just (TimezoneOffset 0))
]
eSubject = toASN1 (certSubjectDN cert) []
epkinfo = toASN1 (certPubKey cert) []
eexts = case certExtensions cert of
Extensions Nothing -> []
exts -> asn1Container (Container Context 3) $ toASN1 exts []
timeType t =
if t >= timeConvert (Date 2050 January 1)
then TimeGeneralized
else TimeUTC