module OpenSSL.X509
(
X509
, X509_
, newX509
, wrapX509
, withX509Ptr
, withX509Stack
, unsafeX509ToPtr
, touchX509
, writeDerX509
, readDerX509
, compareX509
, signX509
, verifyX509
, printX509
, getVersion
, setVersion
, getSerialNumber
, setSerialNumber
, getIssuerName
, setIssuerName
, getSubjectName
, setSubjectName
, getNotBefore
, setNotBefore
, getNotAfter
, setNotAfter
, getPublicKey
, setPublicKey
, getSubjectEmail
)
where
import Control.Monad
import Data.Time.Clock
import Data.Maybe
import Foreign.ForeignPtr
#if MIN_VERSION_base(4,4,0)
import Foreign.ForeignPtr.Unsafe as Unsafe
#else
import Foreign.ForeignPtr as Unsafe
#endif
import Foreign.Ptr
import Foreign.C
import OpenSSL.ASN1
import OpenSSL.BIO
import OpenSSL.EVP.Digest
import OpenSSL.EVP.PKey
import OpenSSL.EVP.Verify
import OpenSSL.EVP.Internal
import OpenSSL.Utils
import OpenSSL.Stack
import OpenSSL.X509.Name
import Data.ByteString.Lazy (ByteString)
newtype X509 = X509 (ForeignPtr X509_)
data X509_
foreign import ccall unsafe "X509_new"
_new :: IO (Ptr X509_)
foreign import ccall unsafe "&X509_free"
_free :: FunPtr (Ptr X509_ -> IO ())
foreign import ccall unsafe "X509_print"
_print :: Ptr BIO_ -> Ptr X509_ -> IO CInt
foreign import ccall unsafe "X509_cmp"
_cmp :: Ptr X509_ -> Ptr X509_ -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_get_version"
_get_version :: Ptr X509_ -> IO CLong
foreign import ccall unsafe "X509_set_version"
_set_version :: Ptr X509_ -> CLong -> IO CInt
foreign import ccall unsafe "X509_get_serialNumber"
_get_serialNumber :: Ptr X509_ -> IO (Ptr ASN1_INTEGER)
foreign import ccall unsafe "X509_set_serialNumber"
_set_serialNumber :: Ptr X509_ -> Ptr ASN1_INTEGER -> IO CInt
foreign import ccall unsafe "X509_get_issuer_name"
_get_issuer_name :: Ptr X509_ -> IO (Ptr X509_NAME)
foreign import ccall unsafe "X509_set_issuer_name"
_set_issuer_name :: Ptr X509_ -> Ptr X509_NAME -> IO CInt
foreign import ccall unsafe "X509_get_subject_name"
_get_subject_name :: Ptr X509_ -> IO (Ptr X509_NAME)
foreign import ccall unsafe "X509_set_subject_name"
_set_subject_name :: Ptr X509_ -> Ptr X509_NAME -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_get_notBefore"
_get_notBefore :: Ptr X509_ -> IO (Ptr ASN1_TIME)
foreign import ccall unsafe "X509_set_notBefore"
_set_notBefore :: Ptr X509_ -> Ptr ASN1_TIME -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_get_notAfter"
_get_notAfter :: Ptr X509_ -> IO (Ptr ASN1_TIME)
foreign import ccall unsafe "X509_set_notAfter"
_set_notAfter :: Ptr X509_ -> Ptr ASN1_TIME -> IO CInt
foreign import ccall unsafe "X509_get_pubkey"
_get_pubkey :: Ptr X509_ -> IO (Ptr EVP_PKEY)
foreign import ccall unsafe "X509_set_pubkey"
_set_pubkey :: Ptr X509_ -> Ptr EVP_PKEY -> IO CInt
foreign import ccall unsafe "X509_get1_email"
_get1_email :: Ptr X509_ -> IO (Ptr STACK)
foreign import ccall unsafe "X509_email_free"
_email_free :: Ptr STACK -> IO ()
foreign import ccall unsafe "X509_sign"
_sign :: Ptr X509_ -> Ptr EVP_PKEY -> Ptr EVP_MD -> IO CInt
foreign import ccall unsafe "X509_verify"
_verify :: Ptr X509_ -> Ptr EVP_PKEY -> IO CInt
foreign import ccall safe "i2d_X509_bio"
_write_bio_X509 :: Ptr BIO_
-> Ptr X509_
-> IO CInt
foreign import ccall safe "d2i_X509_bio"
_read_bio_X509 :: Ptr BIO_
-> Ptr (Ptr X509_)
-> IO (Ptr X509_)
newX509 :: IO X509
newX509 = _new >>= failIfNull >>= wrapX509
wrapX509 :: Ptr X509_ -> IO X509
wrapX509 = fmap X509 . newForeignPtr _free
withX509Ptr :: X509 -> (Ptr X509_ -> IO a) -> IO a
withX509Ptr (X509 x509) = withForeignPtr x509
withX509Stack :: [X509] -> (Ptr STACK -> IO a) -> IO a
withX509Stack = withForeignStack unsafeX509ToPtr touchX509
unsafeX509ToPtr :: X509 -> Ptr X509_
unsafeX509ToPtr (X509 x509) = Unsafe.unsafeForeignPtrToPtr x509
touchX509 :: X509 -> IO ()
touchX509 (X509 x509) = touchForeignPtr x509
writeX509' :: BIO -> X509 -> IO ()
writeX509' bio x509
= withBioPtr bio $ \ bioPtr ->
withX509Ptr x509 $ \ x509Ptr ->
_write_bio_X509 bioPtr x509Ptr
>>= failIf (< 0)
>> return ()
writeDerX509 :: X509 -> IO ByteString
writeDerX509 x509
= do mem <- newMem
writeX509' mem x509
bioReadLBS mem
readX509' :: BIO -> IO X509
readX509' bio
= withBioPtr bio $ \ bioPtr ->
_read_bio_X509 bioPtr nullPtr
>>= failIfNull
>>= wrapX509
readDerX509 :: ByteString -> IO X509
readDerX509 derStr
= newConstMemLBS derStr >>= readX509'
compareX509 :: X509 -> X509 -> IO Ordering
compareX509 cert1 cert2
= withX509Ptr cert1 $ \ cert1Ptr ->
withX509Ptr cert2 $ \ cert2Ptr ->
fmap interpret (_cmp cert1Ptr cert2Ptr)
where
interpret :: CInt -> Ordering
interpret n
| n > 0 = GT
| n < 0 = LT
| otherwise = EQ
signX509 :: KeyPair key =>
X509
-> key
-> Maybe Digest
-> IO ()
signX509 x509 key mDigest
= withX509Ptr x509 $ \ x509Ptr ->
withPKeyPtr' key $ \ pkeyPtr ->
do dig <- case mDigest of
Just md -> return md
Nothing -> pkeyDefaultMD key
withMDPtr dig $ \ digestPtr ->
_sign x509Ptr pkeyPtr digestPtr
>>= failIf_ (== 0)
return ()
verifyX509 :: PublicKey key =>
X509
-> key
-> IO VerifyStatus
verifyX509 x509 key
= withX509Ptr x509 $ \ x509Ptr ->
withPKeyPtr' key $ \ pkeyPtr ->
_verify x509Ptr pkeyPtr
>>= interpret
where
interpret :: CInt -> IO VerifyStatus
interpret 1 = return VerifySuccess
interpret 0 = return VerifyFailure
interpret _ = raiseOpenSSLError
printX509 :: X509 -> IO String
printX509 x509
= do mem <- newMem
withX509Ptr x509 $ \ x509Ptr ->
withBioPtr mem $ \ memPtr ->
_print memPtr x509Ptr
>>= failIf_ (/= 1)
bioRead mem
getVersion :: X509 -> IO Int
getVersion x509
= withX509Ptr x509 $ \ x509Ptr ->
liftM fromIntegral $ _get_version x509Ptr
setVersion :: X509 -> Int -> IO ()
setVersion x509 ver
= withX509Ptr x509 $ \ x509Ptr ->
_set_version x509Ptr (fromIntegral ver)
>>= failIf (/= 1)
>> return ()
getSerialNumber :: X509 -> IO Integer
getSerialNumber x509
= withX509Ptr x509 $ \ x509Ptr ->
_get_serialNumber x509Ptr
>>= peekASN1Integer
setSerialNumber :: X509 -> Integer -> IO ()
setSerialNumber x509 serial
= withX509Ptr x509 $ \ x509Ptr ->
withASN1Integer serial $ \ serialPtr ->
_set_serialNumber x509Ptr serialPtr
>>= failIf (/= 1)
>> return ()
getIssuerName :: X509
-> Bool
-> IO [(String, String)]
getIssuerName x509 wantLongName
= withX509Ptr x509 $ \ x509Ptr ->
do namePtr <- _get_issuer_name x509Ptr
peekX509Name namePtr wantLongName
setIssuerName :: X509 -> [(String, String)] -> IO ()
setIssuerName x509 issuer
= withX509Ptr x509 $ \ x509Ptr ->
withX509Name issuer $ \ namePtr ->
_set_issuer_name x509Ptr namePtr
>>= failIf (/= 1)
>> return ()
getSubjectName :: X509 -> Bool -> IO [(String, String)]
getSubjectName x509 wantLongName
= withX509Ptr x509 $ \ x509Ptr ->
do namePtr <- _get_subject_name x509Ptr
peekX509Name namePtr wantLongName
setSubjectName :: X509 -> [(String, String)] -> IO ()
setSubjectName x509 subject
= withX509Ptr x509 $ \ x509Ptr ->
withX509Name subject $ \ namePtr ->
_set_subject_name x509Ptr namePtr
>>= failIf (/= 1)
>> return ()
getNotBefore :: X509 -> IO UTCTime
getNotBefore x509
= withX509Ptr x509 $ \ x509Ptr ->
_get_notBefore x509Ptr
>>= peekASN1Time
setNotBefore :: X509 -> UTCTime -> IO ()
setNotBefore x509 utc
= withX509Ptr x509 $ \ x509Ptr ->
withASN1Time utc $ \ time ->
_set_notBefore x509Ptr time
>>= failIf (/= 1)
>> return ()
getNotAfter :: X509 -> IO UTCTime
getNotAfter x509
= withX509Ptr x509 $ \ x509Ptr ->
_get_notAfter x509Ptr
>>= peekASN1Time
setNotAfter :: X509 -> UTCTime -> IO ()
setNotAfter x509 utc
= withX509Ptr x509 $ \ x509Ptr ->
withASN1Time utc $ \ time ->
_set_notAfter x509Ptr time
>>= failIf (/= 1)
>> return ()
getPublicKey :: X509 -> IO SomePublicKey
getPublicKey x509
= withX509Ptr x509 $ \ x509Ptr ->
fmap fromJust ( _get_pubkey x509Ptr
>>= failIfNull
>>= wrapPKeyPtr
>>= fromPKey
)
setPublicKey :: PublicKey key => X509 -> key -> IO ()
setPublicKey x509 key
= withX509Ptr x509 $ \ x509Ptr ->
withPKeyPtr' key $ \ pkeyPtr ->
_set_pubkey x509Ptr pkeyPtr
>>= failIf (/= 1)
>> return ()
getSubjectEmail :: X509 -> IO [String]
getSubjectEmail x509
= withX509Ptr x509 $ \ x509Ptr ->
do st <- _get1_email x509Ptr
list <- mapStack peekCString st
_email_free st
return list