tls-debug-0.1.0: src/RetrieveCertificate.hs
{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, ViewPatterns #-}
import Network.TLS
import Network.TLS.Extra
import Data.Char
import Data.IORef
import Data.Time.Clock
import Data.Certificate.X509
import System.IO
import Control.Monad
import Prelude hiding (catch)
import qualified Crypto.Random.AESCtr as RNG
import Text.Printf
import System.Console.CmdArgs
openConnection s p = do
ref <- newIORef Nothing
rng <- RNG.makeSystem
let params = defaultParams
{ pCiphers = ciphersuite_all
, onCertificatesRecv = \l -> do
modifyIORef ref (const $ Just l)
return CertificateUsageAccept
}
ctx <- connectionClient s p params rng
_ <- handshake ctx
bye ctx
r <- readIORef ref
case r of
Nothing -> error "cannot retrieve any certificate"
Just certs -> return certs
data PArgs = PArgs
{ destination :: String
, port :: String
, chain :: Bool
, output :: String
, verify :: Bool
, verifyFQDN :: String
} deriving (Show, Data, Typeable)
progArgs = PArgs
{ destination = "localhost" &= help "destination address to connect to" &= typ "address"
, port = "443" &= help "destination port to connect to" &= typ "port"
, chain = False &= help "also output the chain of certificate used"
, output = "simple" &= help "define the format of output (full, pem, default: simple)" &= typ "format"
, verify = False &= help "verify the chain received with the trusted system certificates"
, verifyFQDN = "" &= help "verify the chain against a specific fully qualified domain name (e.g. web.example.com)" &= explicit &= name "verify-domain-name"
} &= summary "RetrieveCertificate remotely for SSL/TLS protocol"
&= details
[ "Retrieve the remote certificate and optionally its chain from a remote destination"
]
showCert "full" cert = putStrLn $ show cert
showCert _ (x509Cert -> cert) = do
putStrLn ("serial: " ++ (show $ certSerial cert))
putStrLn ("issuer: " ++ (show $ certIssuerDN cert))
putStrLn ("subject: " ++ (show $ certSubjectDN cert))
putStrLn ("validity: " ++ (show $ fst $ certValidity cert) ++ " to " ++ (show $ snd $ certValidity cert))
main = do
a <- cmdArgs progArgs
_ <- printf "connecting to %s on port %s ...\n" (destination a) (port a)
certs <- openConnection (destination a) (port a)
case (chain a) of
True ->
forM_ (zip [0..] certs) $ \(n, cert) -> do
putStrLn ("###### Certificate " ++ show (n + 1 :: Int) ++ " ######")
showCert (output a) cert
False ->
showCert (output a) $ head certs
when (verify a) $ do
putStrLn "### certificate chain trust"
ctime <- utctDay `fmap` getCurrentTime
certificateVerifyChain certs >>= showUsage "chain validity"
showUsage "time validity" (certificateVerifyValidity ctime certs)
when (verifyFQDN a /= "") $
showUsage "fqdn match" (certificateVerifyDomain (verifyFQDN a) certs)
where
showUsage :: String -> TLSCertificateUsage -> IO ()
showUsage s CertificateUsageAccept = printf "%s : accepted\n" s
showUsage s (CertificateUsageReject r) = printf "%s : rejected: %s\n" s (show r)