packages feed

certificate 0.6.0 → 0.7.0

raw patch · 5 files changed

+230/−64 lines, 5 filesdep +cryptocipherdep +cryptohashdep ~asn1-dataPVP ok

version bump matches the API change (PVP)

Dependencies added: cryptocipher, cryptohash

Dependency ranges changed: asn1-data

API changes (from Hackage documentation)

- Data.Certificate.X509: PubKey :: PubKeyALG -> PubKeyDesc -> PubKey
- Data.Certificate.X509: data PubKeyDesc
- Data.Certificate.X509Cert: PubKey :: PubKeyALG -> PubKeyDesc -> PubKey
- Data.Certificate.X509Cert: data PubKeyDesc
- Data.Certificate.X509Cert: instance Eq PubKeyDesc
- Data.Certificate.X509Cert: instance Show PubKeyDesc
+ Data.Certificate.X509: PubKeyALG_DH :: PubKeyALG
+ Data.Certificate.X509: PubKeyDH :: (Integer, Integer, Integer, Maybe Integer, ([Word8], Integer)) -> PubKey
+ Data.Certificate.X509: T61 :: ASN1StringType
+ Data.Certificate.X509: getSigningData :: X509 -> ByteString
+ Data.Certificate.X509Cert: PubKeyALG_DH :: PubKeyALG
+ Data.Certificate.X509Cert: PubKeyDH :: (Integer, Integer, Integer, Maybe Integer, ([Word8], Integer)) -> PubKey
+ Data.Certificate.X509Cert: T61 :: ASN1StringType
+ System.Certificate.X509: findCertificate :: (X509 -> Bool) -> IO (Maybe X509)
+ System.Certificate.X509: getSystemPath :: IO FilePath
+ System.Certificate.X509: instance Eq ReadErr
+ System.Certificate.X509: instance Show ReadErr
+ System.Certificate.X509: readAll :: IO [Either ReadErr X509]
- Data.Certificate.X509: PubKeyDSA :: (Integer, Integer, Integer, Integer) -> PubKeyDesc
+ Data.Certificate.X509: PubKeyDSA :: (Integer, Integer, Integer, Integer) -> PubKey
- Data.Certificate.X509: PubKeyECDSA :: [ASN1] -> PubKeyDesc
+ Data.Certificate.X509: PubKeyECDSA :: [ASN1] -> PubKey
- Data.Certificate.X509: PubKeyRSA :: (Int, Integer, Integer) -> PubKeyDesc
+ Data.Certificate.X509: PubKeyRSA :: (Int, Integer, Integer) -> PubKey
- Data.Certificate.X509: PubKeyUnknown :: [Word8] -> PubKeyDesc
+ Data.Certificate.X509: PubKeyUnknown :: OID -> [Word8] -> PubKey
- Data.Certificate.X509Cert: PubKeyDSA :: (Integer, Integer, Integer, Integer) -> PubKeyDesc
+ Data.Certificate.X509Cert: PubKeyDSA :: (Integer, Integer, Integer, Integer) -> PubKey
- Data.Certificate.X509Cert: PubKeyECDSA :: [ASN1] -> PubKeyDesc
+ Data.Certificate.X509Cert: PubKeyECDSA :: [ASN1] -> PubKey
- Data.Certificate.X509Cert: PubKeyRSA :: (Int, Integer, Integer) -> PubKeyDesc
+ Data.Certificate.X509Cert: PubKeyRSA :: (Int, Integer, Integer) -> PubKey
- Data.Certificate.X509Cert: PubKeyUnknown :: [Word8] -> PubKeyDesc
+ Data.Certificate.X509Cert: PubKeyUnknown :: OID -> [Word8] -> PubKey

Files

Certificate.hs view
@@ -5,7 +5,7 @@ import qualified Data.ByteString as B import qualified Data.Text.Lazy as T import Data.Text.Lazy.Encoding (decodeUtf8)-import Data.Certificate.X509+import qualified Data.Certificate.X509 as X509 import Data.Certificate.KeyRSA as KeyRSA import Data.Certificate.KeyDSA as KeyDSA import Data.Certificate.PEM@@ -14,7 +14,15 @@ import Control.Applicative ((<$>)) import Data.Maybe import System.Exit+import System.Certificate.X509 as SysCert +-- for signing/verifying certificate+import qualified Crypto.Hash.SHA1 as SHA1+import qualified Crypto.Hash.MD2 as MD2+import qualified Crypto.Hash.MD5 as MD5+import qualified Crypto.Cipher.RSA as RSA+import qualified Crypto.Cipher.DSA as DSA+ import Data.ASN1.DER (decodeASN1Stream, ASN1(..), ASN1ConstructionType(..)) import Numeric @@ -28,19 +36,19 @@  showExts e = putStrLn $ show e -showCert :: X509 -> IO ()-showCert (X509 cert _ sigalg sigbits) = do-	putStrLn ("version: " ++ show (certVersion cert))-	putStrLn ("serial:  " ++ show (certSerial cert))-	putStrLn ("sigalg:  " ++ show (certSignatureAlg cert))+showCert :: X509.X509 -> IO ()+showCert (X509.X509 cert _ sigalg sigbits) = do+	putStrLn ("version: " ++ show (X509.certVersion cert))+	putStrLn ("serial:  " ++ show (X509.certSerial cert))+	putStrLn ("sigalg:  " ++ show (X509.certSignatureAlg cert)) 	putStrLn "issuer:"-	showDN $ certIssuerDN cert+	showDN $ X509.certIssuerDN cert 	putStrLn "subject:"-	showDN $ certSubjectDN cert-	putStrLn ("valid:  " ++ show (certValidity cert))-	putStrLn ("pk:     " ++ show (certPubKey cert))+	showDN $ X509.certSubjectDN cert+	putStrLn ("valid:  " ++ show (X509.certValidity cert))+	putStrLn ("pk:     " ++ show (X509.certPubKey cert)) 	putStrLn "exts:"-	showExts $ certExtensions cert+	showExts $ X509.certExtensions cert 	putStrLn ("sigAlg: " ++ show sigalg) 	putStrLn ("sig:    " ++ show sigbits) @@ -109,17 +117,63 @@ 	p (Other tc tn x)        = putStr "other"  doMain :: CertMainOpts -> IO ()-doMain opts@(X509Opt _ _ _ _) = do+doMain opts@(X509 _ _ _ _ _) = do 	cert <- maybe (error "cannot read PEM certificate") (id) . parsePEMCert <$> B.readFile (head $ files opts)  	when (raw opts) $ putStrLn $ hexdump $ L.fromChunks [cert] 	when (asn1 opts) $ case decodeASN1Stream $ L.fromChunks [cert] of 		Left err   -> error ("decoding ASN1 failed: " ++ show err) 		Right asn1 -> showASN1 asn1-	when (text opts || not (or [asn1 opts,raw opts])) $ case decodeCertificate $ L.fromChunks [cert] of++	let x509o = X509.decodeCertificate $ L.fromChunks [cert]+	let x509  = case x509o of 		Left err   -> error ("decoding certificate failed: " ++ show err)-		Right c    -> showCert c+		Right c    -> c+	when (text opts || not (or [asn1 opts,raw opts])) $ showCert x509+	when (verify opts) $ verifyCert x509 	exitSuccess+	where+		verifyCert x509@(X509.X509 cert _ sigalg sig) = do+			sysx509 <- SysCert.findCertificate (matchsysX509 cert)+			case sysx509 of+				Nothing                        -> putStrLn "couldn't find signing certificate"+				Just (X509.X509 syscert _ _ _) -> do+					verifyAlg (B.concat $ L.toChunks $ X509.getSigningData x509)+					          (B.pack sig)+					          sigalg+					          (X509.certPubKey syscert)++		rsaVerify h hdesc pk a b = either (Left . show) (Right) $ RSA.verify h hdesc pk a b++		verifyF X509.SignatureALG_md2WithRSAEncryption (X509.PubKeyRSA rsakey) =+			rsaVerify MD2.hash (B.pack [0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x04,0x10]) (mkRSA rsakey)++		verifyF X509.SignatureALG_md5WithRSAEncryption (X509.PubKeyRSA rsakey) =+			rsaVerify MD5.hash (B.pack [0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x04,0x10]) (mkRSA rsakey)++		verifyF X509.SignatureALG_sha1WithRSAEncryption (X509.PubKeyRSA rsakey) =+			rsaVerify SHA1.hash (B.pack [0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x04,0x10]) (mkRSA rsakey)++		verifyF X509.SignatureALG_dsaWithSHA1 (X509.PubKeyDSA (pub,p,q,g)) =+			(\_ _ -> Left "unimplemented DSA checking")++		verifyF _ _ =+			(\_ _ -> Left "unexpected/wrong signature")++		mkRSA (lenmodulus, modulus, e) =+			RSA.PublicKey { RSA.public_sz = lenmodulus, RSA.public_n = modulus, RSA.public_e = e }++		verifyAlg toSign expectedSig sigalg pk =+			let f = verifyF sigalg pk in+			case f toSign expectedSig of+				Left err    -> putStrLn ("certificate couldn't be verified: something happened: " ++ show err)+				Right True  -> putStrLn "certificate verified"+				Right False -> putStrLn "certificate not verified"++		matchsysX509 cert (X509.X509 syscert _ _ _) = do+			let x = X509.certSubjectDN syscert+			let y = X509.certIssuerDN cert+			x == y 	 doMain (Key files) = do 	content <- B.readFile $ head files@@ -141,22 +195,24 @@ 			putStrLn "no recognized private key found"  data CertMainOpts =-	  X509Opt-		{ files :: [FilePath]-		, asn1  :: Bool-		, text  :: Bool-		, raw   :: Bool+	  X509+		{ files  :: [FilePath]+		, asn1   :: Bool+		, text   :: Bool+		, raw    :: Bool+		, verify :: Bool 		} 	| Key 		{ files :: [FilePath] 		} 	deriving (Show,Data,Typeable) -x509Opts = X509Opt-	{ files = def &= args &= typFile-	, asn1  = def-	, text  = def-	, raw   = def+x509Opts = X509+	{ files  = def &= args &= typFile+	, asn1   = def+	, text   = def+	, raw    = def+	, verify = def 	} &= help "x509 certificate related commands"  keyOpts = Key
Data/Certificate/X509.hs view
@@ -15,13 +15,15 @@ 	-- * Data Structure (reexported from X509Cert) 	, SignatureALG(..) 	, PubKeyALG(..)-	, PubKeyDesc(..) 	, PubKey(..) 	, ASN1StringType(..) 	, ASN1String 	, Certificate(..) 	, CertificateExts(..) +	-- * helper for signing/veryfing certificate+	, getSigningData+ 	-- * serialization from ASN1 bytestring 	, decodeCertificate 	, encodeCertificate@@ -38,6 +40,15 @@  data X509 = X509 Certificate (Maybe L.ByteString) SignatureALG [Word8] 	deriving (Show,Eq)++{- | get signing data related to a X509 message,+ - which is either the cached data or the encoded certificate -}+getSigningData :: X509 -> L.ByteString+getSigningData (X509 _ (Just e) _ _)   = e+getSigningData (X509 cert Nothing _ _) = e+	where+		(Right e) = encodeASN1Stream header+		header    = asn1Container Sequence $ encodeCertificateHeader cert  {- | decode an X509 from a bytestring  - the structure is the following:
Data/Certificate/X509Cert.hs view
@@ -3,7 +3,6 @@ 	-- * Data Structure 	  SignatureALG(..) 	, PubKeyALG(..)-	, PubKeyDesc(..) 	, PubKey(..) 	, ASN1StringType(..) 	, ASN1String@@ -51,17 +50,17 @@ 	  PubKeyALG_RSA 	| PubKeyALG_DSA 	| PubKeyALG_ECDSA+	| PubKeyALG_DH 	| PubKeyALG_Unknown OID 	deriving (Show,Eq) -data PubKeyDesc =-	  PubKeyRSA (Int, Integer, Integer)              -- ^ RSA format with (len modulus, modulus, e)-	| PubKeyDSA (Integer, Integer, Integer, Integer) -- ^ DSA format with (pub, p, q, g)+data PubKey =+	  PubKeyRSA (Int,Integer,Integer)                -- ^ RSA format with (len modulus, modulus, e)+	| PubKeyDSA (Integer,Integer,Integer,Integer)    -- ^ DSA format with (pub, p, q, g)+	| PubKeyDH (Integer,Integer,Integer,Maybe Integer,([Word8], Integer))+	                                                 -- ^ DH format with (p,g,q,j,(seed,pgenCounter)) 	| PubKeyECDSA [ASN1]                             -- ^ ECDSA format not done yet FIXME-	| PubKeyUnknown [Word8]                          -- ^ unrecognized format-	deriving (Show,Eq)--data PubKey = PubKey PubKeyALG PubKeyDesc -- OID RSA|DSA|rawdata+	| PubKeyUnknown OID [Word8]                      -- ^ unrecognized format 	deriving (Show,Eq)  -- FIXME use a proper standard type for representing time.@@ -79,7 +78,7 @@ 	| CertKeyUsageDecipherOnly 	deriving (Show, Eq) -data ASN1StringType = UTF8 | Printable | Univ | BMP | IA5 deriving (Show,Eq)+data ASN1StringType = UTF8 | Printable | Univ | BMP | IA5 | T61 deriving (Show,Eq) type ASN1String = (ASN1StringType, Text)  data Certificate = Certificate@@ -109,21 +108,22 @@  {- | parse a RSA pubkeys from ASN1 encoded bits.  - return PubKeyRSA (len-modulus, modulus, e) if successful -}-parse_RSA :: ByteString -> PubKeyDesc+parse_RSA :: ByteString -> ParseASN1 PubKey parse_RSA bits = 	case decodeASN1Stream $ bits of 		Right [Start Sequence, IntVal modulus, IntVal pubexp, End Sequence] ->-			PubKeyRSA (calculate_modulus modulus 1, modulus, pubexp)+			return $ PubKeyRSA (calculate_modulus modulus 1, modulus, pubexp) 		_ ->-			PubKeyUnknown $ L.unpack bits+			throwError ("bad RSA format") 	where 		calculate_modulus n i = if (2 ^ (i * 8)) > n then i else calculate_modulus n (i+1) -parse_ECDSA :: ByteString -> PubKeyDesc+parse_ECDSA :: ByteString -> ParseASN1 PubKey parse_ECDSA bits = 	case decodeASN1Stream bits of-		Right l -> PubKeyECDSA l-		Left x  -> PubKeyUnknown $ map (fromIntegral . fromEnum) $ show x+		Right l -> return $ PubKeyECDSA l+		Left _  -> return $ PubKeyUnknown (pubkeyalgOID PubKeyALG_ECDSA) (L.unpack bits)+ parseCertHeaderVersion :: ParseASN1 Int parseCertHeaderVersion = do 	v <- onNextContainerMaybe (Container Context 0) $ do@@ -154,6 +154,7 @@ 	[ ([1,2,840,113549,1,1,1], PubKeyALG_RSA) 	, ([1,2,840,10040,4,1],    PubKeyALG_DSA) 	, ([1,2,840,10045,2,1],    PubKeyALG_ECDSA)+	, ([1,2,840,10046,2,1],    PubKeyALG_DH) 	]  oidSig :: OID -> SignatureALG@@ -170,6 +171,13 @@ pubkeyalgOID (PubKeyALG_Unknown oid) = oid pubkeyalgOID sig = maybe [] fst $ find ((==) sig . snd) pk_table +pubkeyToAlg :: PubKey -> PubKeyALG+pubkeyToAlg (PubKeyRSA _)         = PubKeyALG_RSA+pubkeyToAlg (PubKeyDSA _)         = PubKeyALG_DSA+pubkeyToAlg (PubKeyDH _)          = PubKeyALG_DH+pubkeyToAlg (PubKeyECDSA _)       = PubKeyALG_ECDSA+pubkeyToAlg (PubKeyUnknown oid _) = PubKeyALG_Unknown oid+ parseCertHeaderAlgorithmID :: ParseASN1 SignatureALG parseCertHeaderAlgorithmID = do 	n <- getNextContainer Sequence@@ -184,6 +192,7 @@ asn1String (UniversalString x) = (Univ, x) asn1String (BMPString x)       = (BMP, x) asn1String (IA5String x)       = (IA5, x)+asn1String (T61String x)       = (IA5, x) asn1String x                   = error ("not a print string " ++ show x)  encodeAsn1String :: ASN1String -> ASN1@@ -192,6 +201,7 @@ encodeAsn1String (Univ, x)      = UniversalString x encodeAsn1String (BMP, x)       = BMPString x encodeAsn1String (IA5, x)       = IA5String x+encodeAsn1String (T61, x)       = T61String x  parseCertHeaderDN :: ParseASN1 [ (OID, ASN1String) ] parseCertHeaderDN = do@@ -222,23 +232,21 @@ 	l <- getNextContainer Sequence 	bits <- getNextBitString 	case l of-		[OID pkalg,Null]                                                   -> do+		[OID pkalg,Null] -> do 			let sig = oidPubKey pkalg-			let desc = case sig of+			case sig of 				PubKeyALG_RSA -> parse_RSA bits-				_             -> PubKeyUnknown $ L.unpack bits-			return $ PubKey sig desc-		[OID pkalg,OID pkalg2]                                            -> do+				_             -> return $ PubKeyUnknown pkalg $ L.unpack bits+		[OID pkalg,OID _] -> do 			let sig = oidPubKey pkalg-			let desc = case sig of+			case sig of 				PubKeyALG_ECDSA  -> parse_ECDSA bits-				_                -> PubKeyUnknown $ L.unpack bits-			return $ PubKey sig desc+				_                -> return $ PubKeyUnknown pkalg $ L.unpack bits 		[OID pkalg,Start Sequence,IntVal p,IntVal q,IntVal g,End Sequence] -> do 			let sig = oidPubKey pkalg 			case decodeASN1Stream bits of-				Right [IntVal dsapub] -> return $ PubKey sig (PubKeyDSA (dsapub, p, q, g))-				_                     -> throwError "unrecognized DSA pub format"+				Right [IntVal dsapub] -> return $ PubKeyDSA (dsapub, p, q, g)+				_                     -> return $ PubKeyUnknown pkalg $ L.unpack bits 		n -> 			throwError ("subject public key bad format : " ++ show n) @@ -347,19 +355,23 @@ 		dnSet (oid, stringy) = asn1Container Set (asn1Container Sequence [OID oid, encodeAsn1String stringy])  encodePK :: PubKey -> [ASN1]-encodePK (PubKey sig (PubKeyRSA (_, modulus, e))) =-	asn1Container Sequence (asn1Container Sequence [ OID $ pubkeyalgOID sig, Null ] ++ [BitString 0 bits])+encodePK k@(PubKeyRSA (_, modulus, e)) =+	asn1Container Sequence (asn1Container Sequence [pkalg,Null] ++ [BitString 0 bits]) 	where+		pkalg        = OID $ pubkeyalgOID $ pubkeyToAlg k 		(Right bits) = encodeASN1Stream $ asn1Container Sequence [IntVal modulus, IntVal e] -encodePK (PubKey sig (PubKeyDSA (pub, p, q, g)))  =-	asn1Container Sequence (asn1Container Sequence ([OID $ pubkeyalgOID sig] ++ dsaseq) ++ [BitString 0 bits])+encodePK k@(PubKeyDSA (pub, p, q, g)) =+	asn1Container Sequence (asn1Container Sequence ([pkalg] ++ dsaseq) ++ [BitString 0 bits]) 	where+		pkalg        = OID $ pubkeyalgOID $ pubkeyToAlg k 		dsaseq       = asn1Container Sequence [IntVal p,IntVal q,IntVal g] 		(Right bits) = encodeASN1Stream [IntVal pub] -encodePK (PubKey sig (PubKeyUnknown l))           = -	asn1Container Sequence (asn1Container Sequence [OID $ pubkeyalgOID sig, Null] ++ [BitString 0 $ L.pack l])+encodePK k@(PubKeyUnknown _ l) =+	asn1Container Sequence (asn1Container Sequence [pkalg,Null] ++ [BitString 0 $ L.pack l])+	where+		pkalg = OID $ pubkeyalgOID $ pubkeyToAlg k  encodeCertificateHeader :: Certificate -> [ASN1] encodeCertificateHeader cert =
+ System/Certificate/X509.hs view
@@ -0,0 +1,81 @@+-- |+-- Module      : System.Certificate.X509+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : linux only+--+-- this module is portable to unix system where there is usually+-- a /etc/ssl/certs with system X509 certificates.+--+-- the path can be dynamically override using the environment variable+-- defined by envPathOverride in the module, which by+-- default is SYSTEM_CERTIFICATE_PATH+--+module System.Certificate.X509+	( getSystemPath+	, readAll+	, findCertificate+	) where++import System.Directory (getDirectoryContents)+import System.Environment (getEnv)+import Data.List (isPrefixOf)++import Data.Either+import Data.Certificate.X509+import Data.Certificate.PEM+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L++import Control.Applicative ((<$>))+import Control.Exception+import Control.Monad++import Prelude hiding (catch)++defaultSystemPath :: FilePath+defaultSystemPath = "/etc/ssl/certs/"++envPathOverride :: String+envPathOverride = "SYSTEM_CERTIFICATE_PATH"++getSystemPath :: IO FilePath+getSystemPath = catch (getEnv envPathOverride) inDefault+	where+		inDefault :: IOException -> IO FilePath+		inDefault _ = return defaultSystemPath++data ReadErr =+	  Exception IOException+	| CertError String+	deriving (Show,Eq)++readCertificate :: FilePath -> IO (Either ReadErr X509)+readCertificate file = do+	rawdata <- try $ B.readFile file :: IO (Either IOException B.ByteString)+	either (return . Left . Exception) parseCert $ rawdata+	where+		parseCert pemdata = case parsePEMCert pemdata of+			Nothing       -> return $ Left $ CertError "certificate not in PEM format"+			Just certdata -> do+				return $ either (Left . CertError) Right $ decodeCertificate $ L.fromChunks [certdata]++readAll :: IO [Either ReadErr X509]+readAll = do+	path      <- getSystemPath+	certfiles <- filter (not . isPrefixOf ".") <$> getDirectoryContents path+	forM certfiles $ \certfile -> readCertificate (path ++ certfile)++findCertificate :: (X509 -> Bool) -> IO (Maybe X509)+findCertificate f = do+	path      <- getSystemPath+	certfiles <- filter (not . isPrefixOf ".") <$> getDirectoryContents path+	loop $ map (path ++) certfiles+	where+		loop []     = return Nothing+		loop (x:xs) = do+			ox509 <- readCertificate x+			case ox509 of+				Left _     -> loop xs+				Right x509 -> if f x509 then return $ Just x509 else loop xs
certificate.cabal view
@@ -1,5 +1,5 @@ Name:                certificate-Version:             0.6.0+Version:             0.7.0 Description:     Certificates and Key reader/writer     .@@ -26,17 +26,19 @@   Default:           False  Library-  Build-Depends:     base >= 3 && < 7,-                     bytestring,-                     text >= 0.11,-                     mtl,-                     asn1-data >= 0.4.5 && < 0.5,-                     base64-bytestring+  Build-Depends:     base >= 3 && < 5+                   , bytestring+                   , text >= 0.11+                   , mtl+                   , asn1-data >= 0.4.6 && < 0.5+                   , base64-bytestring+                   , directory   Exposed-modules:   Data.Certificate.X509,                      Data.Certificate.X509Cert,                      Data.Certificate.PEM,                      Data.Certificate.KeyDSA                      Data.Certificate.KeyRSA+                     System.Certificate.X509   Other-modules:     Data.Certificate.X509Internal   ghc-options:       -Wall @@ -44,7 +46,11 @@   Main-Is:           Certificate.hs   if flag(executable)     Buildable:       True-    Build-depends:   cmdargs, text >= 0.11+    Build-depends:   cmdargs+                   , text >= 0.11+                   , cryptohash+                   , cryptocipher >= 0.2.4+                   , directory   else     Buildable:       False