1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
|
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CApiFFI #-}
{-# OPTIONS_HADDOCK prune #-}
-- |An interface to PKCS#10 certificate request.
module OpenSSL.X509.Request
( -- * Type
X509Req
, X509_REQ -- private
-- * Functions to manipulate request
, newX509Req
, wrapX509Req -- private
, withX509ReqPtr -- private
, signX509Req
, verifyX509Req
, printX509Req
, writeX509ReqDER
, makeX509FromReq
-- * Accessors
, getVersion
, setVersion
, getSubjectName
, setSubjectName
, getPublicKey
, setPublicKey
, addExtensions
, addExtensionToX509
)
where
import Control.Monad
import Data.Maybe
import Foreign
import Foreign.C
import OpenSSL.BIO
import OpenSSL.EVP.Digest hiding (digest)
import OpenSSL.EVP.PKey
import OpenSSL.EVP.Verify
import OpenSSL.EVP.Internal
import OpenSSL.Utils
import OpenSSL.X509 (X509)
import qualified OpenSSL.X509 as Cert
import OpenSSL.X509.Name
import Data.ByteString.Lazy (ByteString)
import OpenSSL.Stack
-- |@'X509Req'@ is an opaque object that represents PKCS#10
-- certificate request.
newtype X509Req = X509Req (ForeignPtr X509_REQ)
data {-# CTYPE "openssl/x509.h" "X509_REQ" #-} X509_REQ
data X509_EXT
foreign import capi unsafe "openssl/x509.h X509_REQ_new"
_new :: IO (Ptr X509_REQ)
foreign import capi unsafe "openssl/x509.h &X509_REQ_free"
_free :: FunPtr (Ptr X509_REQ -> IO ())
foreign import capi unsafe "openssl/x509.h X509_REQ_sign"
_sign :: Ptr X509_REQ -> Ptr EVP_PKEY -> Ptr EVP_MD -> IO CInt
foreign import capi unsafe "openssl/x509.h X509_REQ_verify"
_verify :: Ptr X509_REQ -> Ptr EVP_PKEY -> IO CInt
foreign import capi unsafe "openssl/x509.h X509_REQ_print"
_print :: Ptr BIO_ -> Ptr X509_REQ -> IO CInt
foreign import capi unsafe "openssl/x509.h i2d_X509_REQ_bio"
_req_to_der :: Ptr BIO_ -> Ptr X509_REQ -> IO CInt
foreign import capi unsafe "HsOpenSSL.h HsOpenSSL_X509_REQ_get_version"
_get_version :: Ptr X509_REQ -> IO CLong
foreign import capi unsafe "openssl/x509.h X509_REQ_set_version"
_set_version :: Ptr X509_REQ -> CLong -> IO CInt
foreign import capi unsafe "HsOpenSSL.h HsOpenSSL_X509_REQ_get_subject_name"
_get_subject_name :: Ptr X509_REQ -> IO (Ptr X509_NAME)
foreign import capi unsafe "openssl/x509.h X509_REQ_set_subject_name"
_set_subject_name :: Ptr X509_REQ -> Ptr X509_NAME -> IO CInt
foreign import capi unsafe "openssl/x509.h X509_REQ_get_pubkey"
_get_pubkey :: Ptr X509_REQ -> IO (Ptr EVP_PKEY)
foreign import capi unsafe "openssl/x509.h X509_REQ_set_pubkey"
_set_pubkey :: Ptr X509_REQ -> Ptr EVP_PKEY -> IO CInt
foreign import capi unsafe "openssl/x509v3.h X509V3_EXT_nconf_nid"
_ext_create :: Ptr a -> Ptr b -> CInt -> CString -> IO (Ptr X509_EXT)
foreign import capi unsafe "openssl/x509.h X509_REQ_add_extensions"
_req_add_extensions :: Ptr X509_REQ -> Ptr STACK -> IO CInt
foreign import capi unsafe "openssl/x509.h X509_add_ext"
_X509_add_ext :: Ptr Cert.X509_ -> Ptr X509_EXT -> CInt -> IO CInt
-- |@'newX509Req'@ creates an empty certificate request. You must set
-- the following properties to and sign it (see 'signX509Req') to
-- actually use the certificate request.
--
-- [/Version/] See 'setVersion'.
--
-- [/Subject Name/] See 'setSubjectName'.
--
-- [/Public Key/] See 'setPublicKey'.
--
newX509Req :: IO X509Req
newX509Req = _new >>= wrapX509Req
wrapX509Req :: Ptr X509_REQ -> IO X509Req
wrapX509Req = fmap X509Req . newForeignPtr _free
withX509ReqPtr :: X509Req -> (Ptr X509_REQ -> IO a) -> IO a
withX509ReqPtr (X509Req req) = withForeignPtr req
-- |@'signX509Req'@ signs a certificate request with a subject private
-- key.
signX509Req :: KeyPair key =>
X509Req -- ^ The request to be signed.
-> key -- ^ The private key to sign with.
-> Maybe Digest -- ^ A hashing algorithm to use. If
-- @Nothing@ the most suitable algorithm
-- for the key is automatically used.
-> IO ()
signX509Req req pkey mDigest
= withX509ReqPtr req $ \ reqPtr ->
withPKeyPtr' pkey $ \ pkeyPtr ->
do digest <- case mDigest of
Just md -> return md
Nothing -> pkeyDefaultMD pkey
withMDPtr digest $ \ digestPtr ->
_sign reqPtr pkeyPtr digestPtr
>>= failIf_ (== 0)
-- |@'verifyX509Req'@ verifies a signature of certificate request with
-- a subject public key.
verifyX509Req :: PublicKey key =>
X509Req -- ^ The request to be verified.
-> key -- ^ The public key to verify with.
-> IO VerifyStatus
verifyX509Req req pkey
= withX509ReqPtr req $ \ reqPtr ->
withPKeyPtr' pkey $ \ pkeyPtr ->
_verify reqPtr pkeyPtr
>>= interpret
where
interpret :: CInt -> IO VerifyStatus
interpret 1 = return VerifySuccess
interpret 0 = return VerifyFailure
interpret _ = raiseOpenSSLError
-- |@'printX509Req' req@ translates a certificate request into
-- human-readable format.
printX509Req :: X509Req -> IO String
printX509Req req
= do mem <- newMem
withBioPtr mem $ \ memPtr ->
withX509ReqPtr req $ \ reqPtr ->
_print memPtr reqPtr
>>= failIf_ (/= 1)
bioRead mem
{- DER encoding ------------------------------------------------------------- -}
-- |@'writeX509ReqDER' req@ writes a PKCS#10 certificate request to DER string.
writeX509ReqDER :: X509Req -> IO ByteString
writeX509ReqDER req
= do mem <- newMem
withBioPtr mem $ \ memPtr ->
withX509ReqPtr req $ \ reqPtr ->
_req_to_der memPtr reqPtr
>>= failIf_ (< 0)
bioReadLBS mem
-- |@'getVersion' req@ returns the version number of certificate
-- request.
getVersion :: X509Req -> IO Int
getVersion req
= withX509ReqPtr req $ \ reqPtr ->
liftM fromIntegral $ _get_version reqPtr
-- |@'setVersion' req ver@ updates the version number of certificate
-- request.
setVersion :: X509Req -> Int -> IO ()
setVersion req ver
= withX509ReqPtr req $ \ reqPtr ->
_set_version reqPtr (fromIntegral ver)
>>= failIf (/= 1)
>> return ()
-- |@'getSubjectName' req wantLongName@ returns the subject name of
-- certificate request. See 'OpenSSL.X509.getSubjectName' of
-- "OpenSSL.X509".
getSubjectName :: X509Req -> Bool -> IO [(String, String)]
getSubjectName req wantLongName
= withX509ReqPtr req $ \ reqPtr ->
do namePtr <- _get_subject_name reqPtr
peekX509Name namePtr wantLongName
-- |@'setSubjectName' req name@ updates the subject name of
-- certificate request. See 'OpenSSL.X509.setSubjectName' of
-- "OpenSSL.X509".
setSubjectName :: X509Req -> [(String, String)] -> IO ()
setSubjectName req subject
= withX509ReqPtr req $ \ reqPtr ->
withX509Name subject $ \ namePtr ->
_set_subject_name reqPtr namePtr
>>= failIf (/= 1)
>> return ()
-- |@'getPublicKey' req@ returns the public key of the subject of
-- certificate request.
getPublicKey :: X509Req -> IO SomePublicKey
getPublicKey req
= withX509ReqPtr req $ \ reqPtr ->
fmap fromJust
( _get_pubkey reqPtr
>>= failIfNull
>>= wrapPKeyPtr
>>= fromPKey
)
-- |@'setPublicKey' req@ updates the public key of the subject of
-- certificate request.
setPublicKey :: PublicKey key => X509Req -> key -> IO ()
setPublicKey req pkey
= withX509ReqPtr req $ \ reqPtr ->
withPKeyPtr' pkey $ \ pkeyPtr ->
_set_pubkey reqPtr pkeyPtr
>>= failIf (/= 1)
>> return ()
-- |@'addExtensions' req [(nid, str)]@
--
-- E.g., nid 85 = 'subjectAltName' http://osxr.org:8080/openssl/source/crypto/objects/objects.h#0476
--
-- (TODO: more docs; NID type)
addExtensions :: X509Req -> [(Int, String)] -> IO CInt
addExtensions req exts =
withX509ReqPtr req $ \reqPtr -> do
extPtrs <- forM exts make
withStack extPtrs $ _req_add_extensions reqPtr
where
make (nid, str) = withCString str $ _ext_create nullPtr nullPtr (fromIntegral nid)
-- |@'makeX509FromReq' req cert@ creates an empty X.509 certificate
-- and copies as much data from the request as possible. The resulting
-- certificate doesn't have the following data and it isn't signed so
-- you must fill them and sign it yourself.
--
-- * Serial number
--
-- * Validity (Not Before and Not After)
--
-- Example:
--
-- > import Data.Time.Clock
-- >
-- > genCert :: X509 -> EvpPKey -> Integer -> Int -> X509Req -> IO X509
-- > genCert caCert caKey serial days req
-- > = do cert <- makeX509FromReq req caCert
-- > now <- getCurrentTime
-- > setSerialNumber cert serial
-- > setNotBefore cert $ addUTCTime (-1) now
-- > setNotAfter cert $ addUTCTime (days * 24 * 60 * 60) now
-- > signX509 cert caKey Nothing
-- > return cert
--
makeX509FromReq :: X509Req
-> X509
-> IO X509
makeX509FromReq req caCert
= do reqPubKey <- getPublicKey req
verified <- verifyX509Req req reqPubKey
when (verified == VerifyFailure)
$ fail "makeX509FromReq: the request isn't properly signed by its own key."
cert <- Cert.newX509
Cert.setVersion cert 2 -- Version 2 means X509 v3. It's confusing.
Cert.setIssuerName cert =<< Cert.getSubjectName caCert False
Cert.setSubjectName cert =<< getSubjectName req False
Cert.setPublicKey cert =<< getPublicKey req
return cert
-- | Add Extensions to a certificate (when the Server accepting certs requires it)
-- E.g.:
--
-- > addExtensionToX509 cert1 87 "CA:FALSE"
-- > addExtensionToX509 cert1 85 "critical,serverAuth, clientAuth" -- when this extension field is critical
--
addExtensionToX509 :: X509 -> Int -> String -> IO Bool
addExtensionToX509 (Cert.X509 certFPtr) nid value = do
-- Context and config pointers are set to nullPtr for simplicity.
-- Depending on your use case, you might need to provide actual values.
result <- withForeignPtr certFPtr $ \certPtr ->
withCString value $ \cValue -> do
extPtr <- _ext_create nullPtr nullPtr (fromIntegral nid) cValue
if extPtr /= nullPtr
then do
res <- _X509_add_ext certPtr extPtr (-1) -- Add to the end
return (res == 0)
else return False
return result
|