packages feed

certificate 0.3.2 → 0.4.0

raw patch · 4 files changed

+254/−144 lines, 4 filesdep +HUnitdep +QuickCheckdep +cmdargsdep −haskell98dep ~asn1-datanew-component:exe:TestsPVP ok

version bump matches the API change (PVP)

Dependencies added: HUnit, QuickCheck, cmdargs, directory, text

Dependencies removed: haskell98

Dependency ranges changed: asn1-data

API changes (from Hackage documentation)

- Data.Certificate.X509: CertificateDN :: Maybe String -> Maybe String -> Maybe String -> Maybe String -> [(OID, String)] -> CertificateDN
- Data.Certificate.X509: cdnCommonName :: CertificateDN -> Maybe String
- Data.Certificate.X509: cdnCountry :: CertificateDN -> Maybe String
- Data.Certificate.X509: cdnOrganization :: CertificateDN -> Maybe String
- Data.Certificate.X509: cdnOrganizationUnit :: CertificateDN -> Maybe String
- Data.Certificate.X509: cdnOthers :: CertificateDN -> [(OID, String)]
- Data.Certificate.X509: data CertificateDN
- Data.Certificate.X509: instance Eq CertificateDN
- Data.Certificate.X509: instance Show CertificateDN
+ Data.Certificate.X509: BMP :: ASN1StringType
+ Data.Certificate.X509: IA5 :: ASN1StringType
+ Data.Certificate.X509: Printable :: ASN1StringType
+ Data.Certificate.X509: PubKeyALG_DSA :: PubKeyALG
+ Data.Certificate.X509: PubKeyALG_ECDSA :: PubKeyALG
+ Data.Certificate.X509: PubKeyALG_RSA :: PubKeyALG
+ Data.Certificate.X509: PubKeyALG_Unknown :: OID -> PubKeyALG
+ Data.Certificate.X509: PubKeyECDSA :: ASN1 -> PubKeyDesc
+ Data.Certificate.X509: SignatureALG_Unknown :: OID -> SignatureALG
+ Data.Certificate.X509: SignatureALG_dsaWithSHA1 :: SignatureALG
+ Data.Certificate.X509: SignatureALG_ecdsaWithSHA384 :: SignatureALG
+ Data.Certificate.X509: SignatureALG_md2WithRSAEncryption :: SignatureALG
+ Data.Certificate.X509: SignatureALG_md5WithRSAEncryption :: SignatureALG
+ Data.Certificate.X509: SignatureALG_sha1WithRSAEncryption :: SignatureALG
+ Data.Certificate.X509: UTF8 :: ASN1StringType
+ Data.Certificate.X509: Univ :: ASN1StringType
+ Data.Certificate.X509: data ASN1StringType
+ Data.Certificate.X509: data PubKeyALG
+ Data.Certificate.X509: data SignatureALG
+ Data.Certificate.X509: instance Eq ASN1StringType
+ Data.Certificate.X509: instance Eq PubKeyALG
+ Data.Certificate.X509: instance Show ASN1StringType
+ Data.Certificate.X509: instance Show PubKeyALG
+ Data.Certificate.X509: oidCommonName :: OID
+ Data.Certificate.X509: oidCountry :: OID
+ Data.Certificate.X509: oidOrganization :: OID
+ Data.Certificate.X509: oidOrganizationUnit :: OID
+ Data.Certificate.X509: type ASN1String = (ASN1StringType, Text)
- Data.Certificate.X509: Certificate :: Int -> Integer -> SignatureALG -> CertificateDN -> CertificateDN -> (Time, Time) -> PubKey -> Maybe CertificateExts -> Maybe (SignatureALG, [Word8]) -> [ASN1] -> Certificate
+ Data.Certificate.X509: Certificate :: Int -> Integer -> SignatureALG -> [(OID, ASN1String)] -> [(OID, ASN1String)] -> (Time, Time) -> PubKey -> Maybe CertificateExts -> Maybe (SignatureALG, [Word8]) -> [ASN1] -> Certificate
- Data.Certificate.X509: PubKey :: SignatureALG -> PubKeyDesc -> PubKey
+ Data.Certificate.X509: PubKey :: PubKeyALG -> PubKeyDesc -> PubKey
- Data.Certificate.X509: certIssuerDN :: Certificate -> CertificateDN
+ Data.Certificate.X509: certIssuerDN :: Certificate -> [(OID, ASN1String)]
- Data.Certificate.X509: certSubjectDN :: Certificate -> CertificateDN
+ Data.Certificate.X509: certSubjectDN :: Certificate -> [(OID, ASN1String)]

Files

Certificate.hs view
@@ -1,70 +1,145 @@+{-# LANGUAGE DeriveDataTypeable #-}+ import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as LC import qualified Data.ByteString as B+import qualified Data.Text.Lazy as T+import Data.Text.Lazy.Encoding (decodeUtf8) import Data.Certificate.X509 import Data.Certificate.Key import Data.Certificate.PEM-import System+import System.Console.CmdArgs import Control.Monad+import Control.Applicative ((<$>)) import Data.Maybe+import System.Exit  import Data.ASN1.DER--readcert :: FilePath -> IO (Either String Certificate)-readcert file = B.readFile file >>= return . maybe (Left "no valid certificate found") (decodeCertificate . L.fromChunks . (:[])) . parsePEMCert+import Numeric  readprivate :: FilePath -> IO (Either String PrivateKey) readprivate file = B.readFile file >>= return . maybe (Left "no valid private RSA key found") (decodePrivateKey . L.fromChunks . (:[])) . parsePEMKeyRSA -showCert :: Certificate -> String-showCert cert =-	let ver = certVersion cert in-	let ser = certSerial cert in-	let sigalg = certSignatureAlg cert in-	let idn = certIssuerDN cert in-	let sdn = certSubjectDN cert in-	let valid = certValidity cert in-	let pk = certPubKey cert in-	let exts = certExtensions cert in-	let sig = certSignature cert in-	let other = certOthers cert in+hexdump :: L.ByteString -> String+hexdump bs = concatMap hex $ L.unpack bs+	where hex n+		| n > 0xa   = showHex n ""+		| otherwise = "0" ++ showHex n "" -	unlines [-		"version: " ++ show ver,-		"serial:  " ++ show ser,-		"sigalg:  " ++ show sigalg,-		"issuer:  " ++ show idn,-		"subject: " ++ show sdn,-		"valid:   " ++ show valid,-		"pk:      " ++ show pk,-		"exts:    " ++ show exts,-		"sig:     " ++ show sig,-		"other:   " ++ show other ]+showDN dn = mapM_ (\(oid, (_,t)) -> putStrLn ("  " ++ show oid ++ ": " ++ T.unpack t)) dn +showExts e = putStrLn $ show e++showCert :: Certificate -> IO ()+showCert cert = do+	putStrLn ("version: " ++ show (certVersion cert))+	putStrLn ("serial:  " ++ show (certSerial cert))+	putStrLn ("sigalg:  " ++ show (certSignatureAlg cert))+	putStrLn "issuer:"+	showDN $ certIssuerDN cert+	putStrLn "subject:"+	showDN $ certSubjectDN cert+	putStrLn ("valid:  " ++ show (certValidity cert))+	putStrLn ("pk:     " ++ show (certPubKey cert))+	putStrLn "exts:"+	showExts $ certExtensions cert+	putStrLn ("sig:    " ++ show (certSignature cert))+	putStrLn ("other:  " ++ show (certOthers cert))++ showKey :: PrivateKey -> String-showKey key =-	unlines [-		"version:          " ++ (show $ privKey_version key),-		"len-modulus:      " ++ (show $ privKey_lenmodulus key),-		"modulus:          " ++ (show $ privKey_modulus key),-		"public exponant:  " ++ (show $ privKey_public_exponant key),-		"private exponant: " ++ (show $ privKey_private_exponant key),-		"p1:               " ++ (show $ privKey_p1 key),-		"p2:               " ++ (show $ privKey_p2 key),-		"exp1:             " ++ (show $ privKey_exp1 key),-		"exp2:             " ++ (show $ privKey_exp2 key),-		"coefficient:      " ++ (show $ privKey_coef key)-		]+showKey key = unlines+	[ "version:          " ++ (show $ privKey_version key)+	, "len-modulus:      " ++ (show $ privKey_lenmodulus key)+	, "modulus:          " ++ (show $ privKey_modulus key)+	, "public exponant:  " ++ (show $ privKey_public_exponant key)+	, "private exponant: " ++ (show $ privKey_private_exponant key)+	, "p1:               " ++ (show $ privKey_p1 key)+	, "p2:               " ++ (show $ privKey_p2 key)+	, "exp1:             " ++ (show $ privKey_exp1 key)+	, "exp2:             " ++ (show $ privKey_exp2 key)+	, "coefficient:      " ++ (show $ privKey_coef key)+	] +showASN1 :: ASN1 -> IO ()+showASN1 = prettyPrint 0 where+	prettyPrint l a = indent l >> p l a >> putStrLn ""+	indent l        = putStr (replicate l ' ')+	p _ (EOC)                  = putStr ""+	p _ (Boolean b)            = putStr ("bool: " ++ show b)+	p _ (IntVal i)             = putStr ("int: " ++ showHex i "")+	p _ (BitString i bs)       = putStr ("bitstring: " ++ hexdump bs)+	p _ (OctetString bs)       = putStr ("octetstring: " ++ hexdump bs)+	p _ (Null)                 = putStr "null"+	p _ (OID is)               = putStr ("OID: " ++ show is) +	p _ (Real d)               = putStr "real"+	p _ (Enumerated)           = putStr "enum"+	p _ (UTF8String t)         = putStr ("utf8string:" ++ T.unpack t)+	p l (Sequence o)           = putStrLn "sequence" >> mapM_ (prettyPrint (l+1)) o >> indent l >> putStr "end-sequence"+	p l (Set o)                = putStrLn "set" >> mapM_ (prettyPrint (l+1)) o >> indent l >> putStr "end-set"+	p _ (NumericString bs)     = putStr "numericstring:"+	p _ (PrintableString t)    = putStr ("printablestring: " ++ T.unpack t)+	p _ (T61String bs)         = putStr "t61string:"+	p _ (VideoTexString bs)    = putStr "videotexstring:"+	p _ (IA5String bs)         = putStr "ia5string:"+	p _ (UTCTime time)         = putStr ("utctime: " ++ show time)+	p _ (GeneralizedTime time) = putStr ("generalizedtime: " ++ show time)+	p _ (GraphicString bs)     = putStr "graphicstring:"+	p _ (VisibleString bs)     = putStr "visiblestring:"+	p _ (GeneralString bs)     = putStr "generalstring:"+	p _ (UniversalString t)    = putStr ("universalstring:" ++ T.unpack t)+	p _ (CharacterString bs)   = putStr "characterstring:"+	p _ (BMPString t)          = putStr ("bmpstring: " ++ T.unpack t)+	p l (Other tc tn x)        = putStr "other:"++mainX509 :: CertMainOpts -> IO ()+mainX509 opts = 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 decodeASN1 $ 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+		Left err   -> error ("decoding certificate failed: " ++ show err)+		Right c    -> showCert c+	exitSuccess+	+mainKey :: CertMainOpts -> IO ()+mainKey opts = do+	c <- readprivate $ head $ files opts+	case c of+		Left err   -> error err+		Right cert -> putStrLn $ showKey cert++data CertMainOpts =+	  X509+		{ files :: [FilePath]+		, asn1 :: Bool+		, text :: Bool+		, raw :: Bool+		}+	| Key+		{ files :: [FilePath]+		}+	deriving (Show,Data,Typeable)++x509Opts = X509+	{ files = def &= args &= typFile+	, asn1 = def+	, text = def+	, raw = def+	} &= help "x509 certificate related commands"++keyOpts = Key+	{ files = def &= args &= typFile+	} &= help "keys related commands"++mode = cmdArgsMode $ modes [x509Opts,keyOpts]+	&= help "create, manipulate certificate (x509,etc) and keys" &= program "certificate" &= summary "certificate v0.1"+ main = do-	args <- getArgs-	case args !! 0 of-		"private" -> do-			c <- readprivate (args !! 1)-			case c of-				Left err   -> error err-				Right cert -> putStrLn $ showKey cert-		"cert" -> do-			c <- readcert (args !! 1)-			case c of-				Left err   -> error err-				Right cert -> putStrLn $ showCert cert+	x <- cmdArgsRun mode+	case x of+		X509 _ _ _ _ -> mainX509 x+		Key  _ -> mainKey x
Data/Certificate/X509.hs view
@@ -10,22 +10,34 @@ -- Read/Write X509 certificate -- -module Data.Certificate.X509 (+module Data.Certificate.X509+	( 	-- * Data Structure-	PubKeyDesc(..),-	PubKey(..),-	CertificateDN(..),-	Certificate(..),+	  SignatureALG(..)+	, PubKeyALG(..)+	, PubKeyDesc(..)+	, PubKey(..)+	, Certificate(..)+	, ASN1StringType(..)+	, ASN1String +	-- * some common OIDs found in certificate Distinguish Names+	, oidCommonName+	, oidCountry+	, oidOrganization+	, oidOrganizationUnit+ 	-- * serialization from ASN1 bytestring-	decodeCertificate,-	encodeCertificate+	, decodeCertificate+	, encodeCertificate 	) where  import Data.Word import Data.List (find) import Data.ASN1.DER import Data.Maybe+import Data.ByteString.Lazy (ByteString)+import Data.Text.Lazy (Text) import qualified Data.ByteString.Lazy as L import Control.Monad.State import Control.Monad.Error@@ -66,29 +78,28 @@ 	  SignatureALG_md5WithRSAEncryption 	| SignatureALG_md2WithRSAEncryption 	| SignatureALG_sha1WithRSAEncryption-	| SignatureALG_rsa-	| SignatureALG_dsa 	| SignatureALG_dsaWithSHA1+	| SignatureALG_ecdsaWithSHA384 	| SignatureALG_Unknown OID 	deriving (Show, Eq) +data PubKeyALG =+	  PubKeyALG_RSA+	| PubKeyALG_DSA+	| PubKeyALG_ECDSA+	| 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)+	| PubKeyECDSA ASN1                               -- ^ ECDSA format not done yet FIXME 	| PubKeyUnknown [Word8]                          -- ^ unrecognized format 	deriving (Show,Eq) -data PubKey = PubKey SignatureALG PubKeyDesc -- OID RSA|DSA|rawdata+data PubKey = PubKey PubKeyALG PubKeyDesc -- OID RSA|DSA|rawdata 	deriving (Show,Eq) -data CertificateDN = CertificateDN-	{ cdnCommonName       :: Maybe String      -- ^ Certificate DN Common Name-	, cdnCountry          :: Maybe String      -- ^ Certificate DN Country of Issuance-	, cdnOrganization     :: Maybe String      -- ^ Certificate DN Organization-	, cdnOrganizationUnit :: Maybe String      -- ^ Certificate DN Organization Unit-	, cdnOthers           :: [ (OID, String) ] -- ^ Certificate DN Other Attributes-	} deriving (Show,Eq)- -- FIXME use a proper standard type for representing time. type Time = (Int, Int, Int, Int, Int, Int, Bool) @@ -112,12 +123,21 @@ 	, certExtOthers               :: [ (OID, Bool, ASN1) ] 	} deriving (Show,Eq) +data ASN1StringType = UTF8 | Printable | Univ | BMP | IA5 deriving (Show,Eq)+type ASN1String = (ASN1StringType, Text)++oidCommonName, oidCountry, oidOrganization, oidOrganizationUnit :: OID+oidCommonName       = [2,5,4,3]+oidCountry          = [2,5,4,6]+oidOrganization     = [2,5,4,10]+oidOrganizationUnit = [2,5,4,11]+ data Certificate = Certificate 	{ certVersion      :: Int                           -- ^ Certificate Version 	, certSerial       :: Integer                       -- ^ Certificate Serial number 	, certSignatureAlg :: SignatureALG                  -- ^ Certificate Signature algorithm-	, certIssuerDN     :: CertificateDN                 -- ^ Certificate Issuer DN-	, certSubjectDN    :: CertificateDN                 -- ^ Certificate Subject DN+	, certIssuerDN     :: [ (OID, ASN1String) ]         -- ^ Certificate Issuer DN+	, certSubjectDN    :: [ (OID, ASN1String) ]         -- ^ Certificate Subject DN 	, certValidity     :: (Time, Time)                  -- ^ Certificate Validity period 	, certPubKey       :: PubKey                        -- ^ Certificate Public key 	, certExtensions   :: Maybe CertificateExts         -- ^ Certificate Extensions@@ -127,7 +147,7 @@  {- | parse a RSA pubkeys from ASN1 encoded bits.  - return PubKeyRSA (len-modulus, modulus, e) if successful -}-parse_RSA :: L.ByteString -> PubKeyDesc+parse_RSA :: ByteString -> PubKeyDesc parse_RSA bits = 	case decodeASN1 $ bits of 		Right (Sequence [ IntVal modulus, IntVal pubexp ]) ->@@ -137,6 +157,12 @@ 	where 		calculate_modulus n i = if (2 ^ (i * 8)) > n then i else calculate_modulus n (i+1) +parse_ECDSA :: ByteString -> PubKeyDesc+parse_ECDSA bits =+	case decodeASN1 bits of+		Right l -> PubKeyECDSA l+		Left x  -> PubKeyUnknown $ map (fromIntegral . fromEnum) $ show x+ newtype ParseCert a = P { runP :: ErrorT String (State [ASN1]) a } 	deriving (Monad, MonadError String) @@ -190,19 +216,31 @@ 	[ ([1,2,840,113549,1,1,5], SignatureALG_sha1WithRSAEncryption) 	, ([1,2,840,113549,1,1,4], SignatureALG_md5WithRSAEncryption) 	, ([1,2,840,113549,1,1,2], SignatureALG_md2WithRSAEncryption)-	, ([1,2,840,113549,1,1,1], SignatureALG_rsa)-	, ([1,2,840,10040,4,1],    SignatureALG_dsa) 	, ([1,2,840,10040,4,3],    SignatureALG_dsaWithSHA1)+	, ([1,2,840,10045,4,3,3],  SignatureALG_ecdsaWithSHA384) 	] +pk_table :: [ (OID, PubKeyALG) ]+pk_table =+	[ ([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)+	]  oidSig :: OID -> SignatureALG oidSig oid = maybe (SignatureALG_Unknown oid) snd $ find ((==) oid . fst) sig_table +oidPubKey :: OID -> PubKeyALG+oidPubKey oid = maybe (PubKeyALG_Unknown oid) snd $ find ((==) oid . fst) pk_table+ sigOID :: SignatureALG -> OID sigOID (SignatureALG_Unknown oid) = oid sigOID sig = maybe [] fst $ find ((==) sig . snd) sig_table +pubkeyalgOID :: PubKeyALG -> OID+pubkeyalgOID (PubKeyALG_Unknown oid) = oid+pubkeyalgOID sig = maybe [] fst $ find ((==) sig . snd) pk_table+ parseCertHeaderAlgorithmID :: ParseCert SignatureALG parseCertHeaderAlgorithmID = do 	n <- getNext@@ -211,45 +249,30 @@ 		Sequence [ OID oid ]       -> return $ oidSig oid 		_                          -> throwError ("algorithm ID bad format " ++ show n) -stringOfASN1String :: ASN1 -> String-stringOfASN1String (PrintableString x) = map (toEnum.fromEnum) $ L.unpack x-stringOfASN1String (UTF8String x)      = map (toEnum.fromEnum) $ L.unpack x-stringOfASN1String (T61String x)       = map (toEnum.fromEnum) $ L.unpack x-stringOfASN1String (UniversalString x) = map (toEnum.fromEnum) $ L.unpack x-stringOfASN1String (BMPString x)       = map (toEnum.fromEnum) $ L.unpack x-stringOfASN1String x                   = error ("not a print string " ++ show x)+asn1String :: ASN1 -> ASN1String+asn1String (PrintableString x) = (Printable, x)+asn1String (UTF8String x)      = (UTF8, x)+asn1String (UniversalString x) = (Univ, x)+asn1String (BMPString x)       = (BMP, x)+asn1String (IA5String x)       = (IA5, x)+asn1String x                   = error ("not a print string " ++ show x) -parseCertHeaderDNHelper :: [ASN1] -> State CertificateDN ()-parseCertHeaderDNHelper l = do-	forM_ l $ (\e -> case e of-		Set [ Sequence [ OID [2,5,4,3], val ] ] ->-			modify (\s -> s { cdnCommonName = Just $ stringOfASN1String val })-		Set [ Sequence [ OID [2,5,4,6], val ] ] ->-			modify (\s -> s { cdnCountry = Just $ stringOfASN1String val })-		Set [ Sequence [ OID [2,5,4,10], val ] ] ->-			modify (\s -> s { cdnOrganization = Just $ stringOfASN1String val })-		Set [ Sequence [ OID [2,5,4,11], val ] ] ->-			modify (\s -> s { cdnOrganizationUnit = Just $ stringOfASN1String val })-		Set [ Sequence [ OID oid, val ] ] ->-			modify (\s -> s { cdnOthers = (oid, show val) : cdnOthers s })-		_      ->-			return ()-		)+encodeAsn1String :: ASN1String -> ASN1+encodeAsn1String (Printable, x) = PrintableString x+encodeAsn1String (UTF8, x)      = UTF8String x+encodeAsn1String (Univ, x)      = UniversalString x+encodeAsn1String (BMP, x)       = BMPString x+encodeAsn1String (IA5, x)       = IA5String x -parseCertHeaderDN :: ParseCert CertificateDN+parseCertHeaderDN :: ParseCert [ (OID, ASN1String) ] parseCertHeaderDN = do 	n <- getNext 	case n of-		Sequence l -> do-			let defdn = CertificateDN-				{ cdnCommonName       = Nothing-				, cdnCountry          = Nothing-				, cdnOrganization     = Nothing-				, cdnOrganizationUnit = Nothing-				, cdnOthers           = []-				}-			return $ execState (parseCertHeaderDNHelper l) defdn +		Sequence l -> mapM parseDNOne l 		_          -> throwError "Distinguished name bad format"+	where+		parseDNOne (Set [ Sequence [OID oid, val]]) = return (oid, asn1String val)+		parseDNOne _                                = throwError "field in dn bad format"  parseCertHeaderValidity :: ParseCert (Time, Time) parseCertHeaderValidity = do@@ -260,17 +283,22 @@  matchPubKey :: ASN1 -> ParseCert PubKey matchPubKey (Sequence[Sequence[OID pkalg,Null],BitString _ bits]) = do-	let sig = oidSig pkalg+	let sig = oidPubKey pkalg 	let desc = case sig of-		SignatureALG_sha1WithRSAEncryption -> parse_RSA bits-		SignatureALG_md5WithRSAEncryption  -> parse_RSA bits-		SignatureALG_md2WithRSAEncryption  -> parse_RSA bits-		SignatureALG_rsa                   -> parse_RSA bits+		PubKeyALG_RSA                      -> parse_RSA bits 		_                                  -> PubKeyUnknown $ L.unpack bits 	return $ PubKey sig desc +matchPubKey (Sequence[Sequence[OID pkalg,OID pkgalg2],BitString _ bits]) = do+	let sig = oidPubKey pkalg+	let desc = case sig of+		PubKeyALG_ECDSA  -> parse_ECDSA bits+		_                -> PubKeyUnknown $ L.unpack bits+	return $ PubKey sig desc++ matchPubKey (Sequence[Sequence[OID pkalg,Sequence[IntVal p,IntVal q,IntVal g]], BitString _ pubenc ]) = do-	let sig = oidSig pkalg+	let sig = oidPubKey pkalg 	case decodeASN1 pubenc of 		Right (IntVal dsapub) -> return $ PubKey sig (PubKeyDSA (dsapub, p, q, g)) 		_                     -> throwError "unrecognized DSA pub format"@@ -361,17 +389,17 @@ 	exts     <- parseCertExtensions 	l        <- getRemaining 	-	return $ Certificate {-		certVersion      = version,-		certSerial       = serial,-		certSignatureAlg = sigalg,-		certIssuerDN     = issuer,-		certSubjectDN    = subject,-		certValidity     = validity,-		certPubKey       = pk,-		certSignature    = Nothing,-		certExtensions   = exts,-		certOthers       = l+	return $ Certificate+		{ certVersion      = version+		, certSerial       = serial+		, certSignatureAlg = sigalg+		, certIssuerDN     = issuer+		, certSubjectDN    = subject+		, certValidity     = validity+		, certPubKey       = pk+		, certSignature    = Nothing+		, certExtensions   = exts+		, certOthers       = l 		}  {- | parse root structure of a x509 certificate. this has to be a sequence of 3 objects :@@ -400,31 +428,21 @@ decodeCertificate :: L.ByteString -> Either String Certificate decodeCertificate by = either (Left . show) processCertificate $ decodeASN1 by -encodeDN :: CertificateDN -> ASN1-encodeDN dn = Sequence sets+encodeDN :: [ (OID, ASN1String) ] -> ASN1+encodeDN dn = Sequence $ map dnSet dn 	where-		sets = catMaybes [-			maybe Nothing (mapSet [2,5,4,3]) $ cdnCommonName dn,-			maybe Nothing (mapSet [2,5,4,6]) $ cdnCountry dn,-			maybe Nothing (mapSet [2,5,4,10]) $ cdnOrganization dn,-			maybe Nothing (mapSet [2,5,4,11]) $ cdnOrganizationUnit dn-			] -		mapSet oid str = Just $ Set [-			Sequence [-				OID oid,-				PrintableString (L.pack $ map (toEnum . fromEnum) str) ]-			]+		dnSet (oid, stringy) = Set [ Sequence [ OID oid, encodeAsn1String stringy ]]  encodePK :: PubKey -> ASN1-encodePK (PubKey sig (PubKeyRSA (_, modulus, e))) = Sequence [ Sequence [ OID $ sigOID sig, Null ], BitString 0 bits ]+encodePK (PubKey sig (PubKeyRSA (_, modulus, e))) = Sequence [ Sequence [ OID $ pubkeyalgOID sig, Null ], BitString 0 bits ] 	where bits = encodeASN1 $ Sequence [ IntVal modulus, IntVal e ] -encodePK (PubKey sig (PubKeyDSA (pub, p, q, g)))  = Sequence [ Sequence [ OID $ sigOID sig, dsaseq ], BitString 0 bits ]+encodePK (PubKey sig (PubKeyDSA (pub, p, q, g)))  = Sequence [ Sequence [ OID $ pubkeyalgOID sig, dsaseq ], BitString 0 bits ] 	where 		dsaseq = Sequence [ IntVal p, IntVal q, IntVal g ] 		bits   = encodeASN1 $ IntVal pub -encodePK (PubKey sig (PubKeyUnknown l))           = Sequence [ Sequence [ OID $ sigOID sig, Null ], BitString 0 (L.pack l) ]+encodePK (PubKey sig (PubKeyUnknown l))           = Sequence [ Sequence [ OID $ pubkeyalgOID sig, Null ], BitString 0 (L.pack l) ]  encodeCertificateHeader :: Certificate -> [ASN1] encodeCertificateHeader cert =
+ Tests.hs view
@@ -0,0 +1,4 @@+import qualified Tests.Unit as Unit++main = do+	Unit.runTests
certificate.cabal view
@@ -1,5 +1,5 @@ Name:                certificate-Version:             0.3.2+Version:             0.4.0 Description:     Certificates and Key reader/writer     .@@ -17,6 +17,10 @@ Homepage:            http://github.com/vincenthz/hs-certificate Cabal-Version:       >=1.6 +Flag test+  Description:       Build unit test+  Default:           False+ Flag executable   Description:       Build the executable   Default:           False@@ -25,8 +29,9 @@   Build-Depends:     base >= 3 && < 7,                      binary >= 0.5,                      bytestring,+                     text >= 0.11,                      mtl,-                     asn1-data >= 0.2,+                     asn1-data >= 0.3 && < 0.4,                      base64-bytestring   Exposed-modules:   Data.Certificate.X509,                      Data.Certificate.PEM,@@ -37,7 +42,15 @@   Main-Is:           Certificate.hs   if flag(executable)     Buildable:       True-    Build-depends:   haskell98+    Build-depends:   cmdargs, text >= 0.11+  else+    Buildable:       False++executable           Tests+  Main-is:           Tests.hs+  if flag(test)+    Buildable:       True+    Build-Depends:   base >= 3 && < 7, directory, HUnit, QuickCheck >= 2, bytestring   else     Buildable:       False