packages feed

tcp-streams-openssl (empty) → 0.6.0.0

raw patch · 14 files changed

+623/−0 lines, 14 filesdep +HUnitdep +HsOpenSSLdep +HsOpenSSL-x509-systemsetup-changed

Dependencies added: HUnit, HsOpenSSL, HsOpenSSL-x509-system, QuickCheck, base, bytestring, directory, io-streams, network, tcp-streams, tcp-streams-openssl, test-framework, test-framework-hunit, test-framework-quickcheck2

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for tcp-streams-openssl++## 0.1.0.0  -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ Data/OpenSSLSetting.hs view
@@ -0,0 +1,88 @@+-- | Helpers for setting up a tls connection with @HsOpenSSL@ package,+-- for further customization, please refer to @HsOpenSSL@ package.+--+-- Note, functions in this module will throw error if can't load certificates or CA store.+--+module Data.OpenSSLSetting+    ( -- * choose a CAStore+      TrustedCAStore(..)+      -- * make TLS settings+    , makeClientSSLContext+    , makeClientSSLContext'+    , makeServerSSLContext+    , makeServerSSLContext'+    ) where++import qualified OpenSSL.X509.SystemStore as X509+import qualified OpenSSL.Session          as SSL+import           OpenSSL                    (withOpenSSL)+import           Data.TLSSetting            (TrustedCAStore(..), mozillaCAStorePath)+++makeCAStore :: TrustedCAStore -> SSL.SSLContext -> IO ()+makeCAStore SystemCAStore  ctx  = X509.contextLoadSystemCerts ctx+makeCAStore MozillaCAStore ctx  = SSL.contextSetCAFile ctx =<< mozillaCAStorePath+makeCAStore (CustomCAStore fp) ctx = SSL.contextSetCAFile ctx fp++-- | make a simple 'SSL.SSLContext' that will validate server and use tls connection+-- without providing client's own certificate. suitable for connecting server which don't+-- validate clients.+--+makeClientSSLContext :: TrustedCAStore          -- ^ trusted certificates.+                     -> IO SSL.SSLContext+makeClientSSLContext tca = withOpenSSL $ do+    let caStore = makeCAStore tca+    ctx <- SSL.context+    caStore ctx+    SSL.contextSetDefaultCiphers ctx+    SSL.contextSetVerificationMode ctx (SSL.VerifyPeer True True Nothing)+    return ctx++-- | make a simple 'SSL.SSLContext' that will validate server and use tls connection+-- while providing client's own certificate. suitable for connecting server which+-- validate clients.+--+-- The chain certificate must be in PEM format and must be sorted starting with the subject's certificate+-- (actual client or server certificate), followed by intermediate CA certificates if applicable,+-- and ending at the highest level (root) CA.+--+makeClientSSLContext' :: FilePath       -- ^ public certificate (X.509 format).+                      -> [FilePath]     -- ^ chain certificate (X.509 format).+                      -> FilePath       -- ^ private key associated.+                      -> TrustedCAStore -- ^ server will use these certificates to validate clients.+                      -> IO SSL.SSLContext+makeClientSSLContext' pub certs priv tca = withOpenSSL $ do+    let caStore = makeCAStore tca+    ctx <- SSL.context+    caStore ctx+    SSL.contextSetDefaultCiphers ctx+    SSL.contextSetCertificateFile ctx pub+    SSL.contextSetPrivateKeyFile ctx priv+    mapM_ (SSL.contextSetCertificateChainFile ctx) certs+    SSL.contextSetVerificationMode ctx (SSL.VerifyPeer True True Nothing)+    return ctx++-- | make a simple 'SSL.SSLContext' for server without validating client's certificate.+--+makeServerSSLContext :: FilePath       -- ^ public certificate (X.509 format).+                     -> [FilePath]     -- ^ chain certificate (X.509 format).+                     -> FilePath       -- ^ private key associated.+                     -> IO SSL.SSLContext+makeServerSSLContext pub certs priv = withOpenSSL $ do+    ctx <- SSL.context+    SSL.contextSetDefaultCiphers ctx+    SSL.contextSetCertificateFile ctx pub+    SSL.contextSetPrivateKeyFile ctx priv+    mapM_ (SSL.contextSetCertificateChainFile ctx) certs+    return ctx++-- | make a 'SSL.SSLConext' that also validating client's certificate.+--+-- This's an alias to 'makeClientSSLContext''.+--+makeServerSSLContext' :: FilePath       -- ^ public certificate (X.509 format).+                      -> [FilePath]     -- ^ chain certificates (X.509 format).+                      -> FilePath       -- ^ private key associated.+                      -> TrustedCAStore -- ^ server will use these certificates to validate clients.+                      -> IO SSL.SSLContext+makeServerSSLContext' = makeClientSSLContext'
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016, winterland1989++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of winterland1989 nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,47 @@+tcp-streams-openssl+===================++[![Hackage](https://img.shields.io/hackage/v/tcp-streams-openssl.svg?style=flat)](http://hackage.haskell.org/package/tcp-streams-openssl)+[![Build Status](https://travis-ci.org/winterland1989/tcp-streams.svg)](https://travis-ci.org/winterland1989/tcp-streams)++This package provides TLS support based on [tcp-streams](http://hackage.haskell.org/package/tcp-streams) using [HsOpenSSL](http://hackage.haskell.org/package/HsOpenSSL). By default `tcp-streams` use `tls` package which is much easier to install but slightly slower.++This package is split from `tcp-streams` due to the difficulties of setting up openssl on many platform. You may need manually passing openssl library path if linker can't find them:++```+cabal install --extra-include-dirs=/usr/local/opt/openssl/include --extra-lib-dirs=/usr/local/opt/openssl/lib tcp-streams+```++License+-------++Copyright (c) 2016, Winterland++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of winterland1989 nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ System/IO/Streams/OpenSSL.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides convenience functions for interfacing @io-streams@+-- with @HsOpenSSL@. @ssl/SSL@ here stand for @HsOpenSSL@ library, not the+-- deprecated SSL 2.0/3.0 protocol. the receive buffer size is 32752.+-- sending is unbuffered, anything write into 'OutputStream' will be immediately+-- send to underlying socket.+--+-- The same exceptions rule which applied to TCP apply here, with addtional+-- 'SSL.SomeSSLException` to be watched out.+--+-- This module is intended to be imported @qualified@, e.g.:+--+-- @+-- import qualified "System.IO.Streams.OpenSSL" as SSL+-- @+--+module System.IO.Streams.OpenSSL+  ( -- * client+    connect+  , connectWithVerifier+  , withConnection+    -- * server+  , accept+    -- * helpers+  , sslToStreams+  , close+    -- * re-export helpers+  , module Data.OpenSSLSetting+  ) where++import qualified Control.Exception     as E+import           Control.Monad         (unless, void)+import           Data.ByteString.Char8 (ByteString)+import qualified Data.ByteString.Char8 as S+import           Data.OpenSSLSetting+import           Network.Socket        (HostName, PortNumber, Socket)+import qualified Network.Socket        as N+import           OpenSSL               (withOpenSSL)+import           OpenSSL.Session       (SSL, SSLContext)+import qualified OpenSSL.Session       as SSL+import qualified OpenSSL.X509          as X509+import           System.IO.Streams     (InputStream, OutputStream)+import qualified System.IO.Streams     as Streams+import qualified System.IO.Streams.TCP as TCP++bUFSIZ :: Int+bUFSIZ = 32752++-- | Given an existing HsOpenSSL 'SSL' connection, produces an 'InputStream' \/+-- 'OutputStream' pair.+--+sslToStreams :: SSL             -- ^ SSL connection object+             -> IO (InputStream ByteString, OutputStream ByteString)+sslToStreams ssl = do+    is <- Streams.makeInputStream input+    os <- Streams.makeOutputStream output+    return (is, os)++  where+    input = ( do+        s <- SSL.read ssl bUFSIZ+        return $! if S.null s then Nothing else Just s+        ) `E.catch` (\(_::E.SomeException) -> return Nothing)++    output Nothing  = return ()+    output (Just s) = SSL.write ssl s+{-# INLINABLE sslToStreams #-}++close :: SSL.SSL -> IO ()+close ssl = withOpenSSL $ do+    SSL.shutdown ssl SSL.Unidirectional+    maybe (return ()) N.close (SSL.sslSocket ssl)++-- | Convenience function for initiating an SSL connection to the given+-- @('HostName', 'PortNumber')@ combination.+--+-- This function will try to verify server's identity using a very simple algorithm,+-- which may not suit your need:+--+-- @+--   matchDomain :: String -> String -> Bool+--   matchDomain n1 n2 =+--       let n1' = reverse (splitDot n1)+--           n2' = reverse (splitDot n2)+--           cmp src target = src == "*" || target == "*" || src == target+--       in and (zipWith cmp n1' n2')+-- @+--+-- If the certificate or hostname is not verified, a 'SSL.ProtocolError' will be thrown.+--+connect :: SSLContext           -- ^ SSL context. See the @HsOpenSSL@+                                -- documentation for information on creating+                                -- this.+        -> Maybe String         -- ^ Optional certificate subject name, if set to 'Nothing'+                                -- then we will try to verify 'HostName' as subject name.+        -> HostName             -- ^ hostname to connect to+        -> PortNumber           -- ^ port number to connect to+        -> IO (InputStream ByteString, OutputStream ByteString, SSL)+connect ctx vhost host port = withOpenSSL $ do+    connectWithVerifier ctx verify host port+  where+    verify trusted cnname = trusted+                          && maybe False (matchDomain verifyHost) cnname+    verifyHost = maybe host id vhost+    matchDomain :: String -> String -> Bool+    matchDomain n1 n2 =+        let n1' = reverse (splitDot n1)+            n2' = reverse (splitDot n2)+            cmp src target = src == "*" || target == "*" || src == target+        in and (zipWith cmp n1' n2')+    splitDot :: String -> [String]+    splitDot "" = [""]+    splitDot x  =+        let (y, z) = break (== '.') x in+        y : (if z == "" then [] else splitDot $ drop 1 z)++-- | Connecting with a custom verification callback.+--+-- @since 0.6.0.0@+--+connectWithVerifier :: SSLContext       -- ^ SSL context. See the @HsOpenSSL@+                                        -- documentation for information on creating+                                        -- this.+                    -> (Bool -> Maybe String -> Bool) -- ^ A verify callback, the first param is+                                                -- the result of certificate verification, the+                                                -- second param is the certificate's subject name.+                    -> HostName             -- ^ hostname to connect to+                    -> PortNumber           -- ^ port number to connect to+                    -> IO (InputStream ByteString, OutputStream ByteString, SSL)+connectWithVerifier ctx f host port = withOpenSSL $ do+    sock <- TCP.connectSocket host port+    E.bracketOnError (SSL.connection ctx sock) close $ \ ssl -> do+        SSL.connect ssl+        trusted <- SSL.getVerifyResult ssl+        cert <- SSL.getPeerCertificate ssl+        subnames <- maybe (return []) (`X509.getSubjectName` False) cert+        let cnname = lookup "CN" subnames+            verified = f trusted cnname+        unless verified (E.throwIO $ SSL.ProtocolError "fail to verify certificate")+        (is, os) <- sslToStreams ssl+        return (is, os, ssl)+++-- | Convenience function for initiating an SSL connection to the given+-- @('HostName', 'PortNumber')@ combination. The socket and SSL connection are+-- closed and deleted after the user handler runs.+--+withConnection :: SSLContext+++               -> Maybe String+               -> HostName+               -> PortNumber+               -> (InputStream ByteString -> OutputStream ByteString -> SSL -> IO a)+                       -- ^ Action to run with the new connection+               -> IO a+withConnection ctx subname host port action =+    E.bracket (connect ctx subname host port) cleanup go++  where+    go (is, os, ssl) = action is os ssl++    cleanup (_, os, ssl) = E.mask_ $+        eatException $! Streams.write Nothing os >> close ssl++    eatException m = void m `E.catch` (\(_::E.SomeException) -> return ())+++-- | Accept a new connection from remote client, return a 'InputStream' / 'OutputStream'+-- pair and remote 'N.SockAddr', you should call 'TCP.bindAndListen' first.+--+-- this operation will throw 'SSL.SomeSSLException' on failure.+--+accept :: SSL.SSLContext            -- ^ check "Data.OpenSSLSetting".+       -> Socket                    -- ^ the listening 'Socket'.+       -> IO (InputStream ByteString, OutputStream ByteString, SSL.SSL, N.SockAddr)+accept ctx sock = withOpenSSL $ do+    (sock', sockAddr) <- N.accept sock+    E.bracketOnError (SSL.connection ctx sock') close $ \ ssl -> do+        SSL.accept ssl+        trusted <- SSL.getVerifyResult ssl+        unless trusted (E.throwIO $ SSL.ProtocolError "fail to verify certificate")+        (is, os) <- sslToStreams ssl+        return (is, os, ssl, sockAddr)+
+ tcp-streams-openssl.cabal view
@@ -0,0 +1,58 @@+name:                tcp-streams-openssl+version:             0.6.0.0+synopsis:            Tcp streams using openssl for tls support.+description:         Tcp streams using openssl for tls support.+license:             BSD3+license-file:        LICENSE+author:              winterland1989+maintainer:          winterland1989@gmail.com+homepage:            https://github.com/winterland1989/tcp-streams+copyright:           (c) Winterland 2016 +category:            Network+build-type:          Simple+extra-source-files:     ChangeLog.md+                    ,   README.md+                    ,   test/cert/*.pem+                    ,   test/cert/*.crt+                    ,   test/cert/*.key+cabal-version:       >=1.10++source-repository head+  type:     git+  location: git://github.com/winterland1989/tcp-streams.git++library+  exposed-modules:    Data.OpenSSLSetting+                  ,   System.IO.Streams.OpenSSL++  -- other-extensions:    +  build-depends:        base           >=4.7  && < 5.0+                    ,   network        >=2.3  && < 3.0+                    ,   bytestring     >= 0.10.2.0+                    ,   io-streams     >= 1.2 && < 2.0+                    ,   tcp-streams    >= 0.6 && < 0.7+                    ,   HsOpenSSL      >=0.10.3 && <0.12+                    ,   HsOpenSSL-x509-system == 0.1.*++  ghc-options:    -Wall+  -- hs-source-dirs:      +  default-language:    Haskell2010++test-suite testsuite+  type:              exitcode-stdio-1.0+  Hs-source-dirs:    src test+  main-is:           Main.hs+  default-language:  Haskell2010+  ghc-options:    -Wall -threaded+  build-depends:        base+                    ,   io-streams+                    ,   tcp-streams+                    ,   tcp-streams-openssl+                    ,   network+                    ,   bytestring+                    ,   HUnit                      >= 1.2      && <2+                    ,   QuickCheck                 >= 2.3.0.2  && <3+                    ,   test-framework             >= 0.6      && <0.9+                    ,   test-framework-hunit       >= 0.2.7    && <0.4+                    ,   test-framework-quickcheck2 >= 0.2.12.1 && <0.4+                    ,   directory      == 1.*
+ test/Main.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main (main) where++------------------------------------------------------------------------------+import           Control.Concurrent             (forkIO, newEmptyMVar, putMVar,+                                                 takeMVar)+import qualified Control.Exception              as E+import qualified Network.Socket                 as N+import           System.Timeout                 (timeout)+import           Test.Framework+import           Test.Framework.Providers.HUnit+import           Test.HUnit                     hiding (Test)+import qualified Data.ByteString                as B+import           System.Directory               (removeFile)+------------------------------------------------------------------------------+import qualified Data.OpenSSLSetting            as SSL+import qualified System.IO.Streams              as Stream+import qualified System.IO.Streams.TCP          as Raw+import qualified System.IO.Streams.OpenSSL      as SSL+------------------------------------------------------------------------------++main :: IO ()+main = defaultMain tests+  where+    tests = [ testGroup "TCP" rawTests+            , testGroup "OpenSSL" sslTests+            ]++------------------------------------------------------------------------------++rawTests :: [Test]+rawTests = [ testRawSocket ]++testRawSocket :: Test+testRawSocket = testCase "network/socket" $+    N.withSocketsDo $ do+    x <- timeout (10 * 10^(6::Int)) go+    assertEqual "ok" (Just ()) x++  where+    go = do+        portMVar   <- newEmptyMVar+        resultMVar <- newEmptyMVar+        forkIO $ client portMVar resultMVar+        server portMVar+        l <- takeMVar resultMVar+        assertEqual "testSocket" l ["ok"]++    client mvar resultMVar = do+        _ <- takeMVar mvar+        (is, os, sock) <- Raw.connect "127.0.0.1" 8888+        Stream.fromList ["", "ok"] >>= Stream.connectTo os+        N.shutdown sock N.ShutdownSend+        Stream.toList is >>= putMVar resultMVar+        N.close sock++    server mvar = do+        sock <- Raw.bindAndListen 8888 1024+        putMVar mvar ()+        (is, os, csock, _) <- Raw.accept sock+        os' <- Stream.atEndOfOutput (N.close csock) os+        os' `Stream.connectTo` is++------------------------------------------------------------------------------++sslTests :: [Test]+sslTests = [ testSSLSocket, testHTTPS' ]++testSSLSocket :: Test+testSSLSocket = testCase "network/socket" $+    N.withSocketsDo $ do+    x <- timeout (10 * 10^(6::Int)) go+    assertEqual "ok" (Just ()) x++  where+    go = do+        portMVar   <- newEmptyMVar+        resultMVar <- newEmptyMVar+        forkIO $ client portMVar resultMVar+        server portMVar+        l <- takeMVar resultMVar+        assertEqual "testSocket" l (Just "ok")++    client mvar resultMVar = do+        _ <- takeMVar mvar+        cp <- SSL.makeClientSSLContext (SSL.CustomCAStore "./test/cert/ca.pem")+        (is, os, ctx) <- SSL.connect cp (Just "Winter") "127.0.0.1" 8890+        Stream.fromList ["", "ok"] >>= Stream.connectTo os+        Stream.read is >>= putMVar resultMVar  -- There's no shutdown in tls, so we won't get a 'Nothing'+        SSL.close ctx++    server mvar = do+        sp <- SSL.makeServerSSLContext "./test/cert/server.crt" [] "./test/cert/server.key"+        sock <- Raw.bindAndListen 8890 1024+        putMVar mvar ()+        (is, os, ssl, _) <- SSL.accept sp sock+        os' <- Stream.atEndOfOutput (SSL.close ssl) os+        os' `Stream.connectTo` is++testHTTPS' :: Test+testHTTPS' = testCase "network/https" $+    N.withSocketsDo $ do+    x <- timeout (10 * 10^(6::Int)) go+    assertEqual "ok" (Just 1024) x+  where+    go = do+        cp <- SSL.makeClientSSLContext SSL.SystemCAStore+        (is, os, ctx) <- SSL.connect cp (Just "*.google.com") "www.google.com" 443+        Stream.write (Just "GET / HTTP/1.1\r\n") os+        Stream.write (Just "Host: www.google.com\r\n") os+        Stream.write (Just "\r\n") os+        bs <- Stream.readExactly 1024 is+        SSL.close ctx+        return (B.length bs)
+ test/cert/ca.key view
@@ -0,0 +1,16 @@+-----BEGIN PRIVATE KEY-----+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMprzly4sBhDqgmd+zwuXs7rlPZvRiZ3ySYmgGpTb9QZIPPTVTQ69GUC0WmeLtW/zni0YPUgowDUTf16X+LDjC3wT0kUvk6ek/rQgs/AToJcWBfYxCSElPRSGb5eiXQX8N+HPNXdViJEUvhoDv+3LgltliFZiY3BxDJ3tkWJOL7lshtAgMBAAECgYBnM4GZyluVc2IM1xVJXsW2gsvf+VnxoN7AAZ81FmcMZudjCJsHLwuNOFuWEpzkQ488ARLxxvr0IEnG8wyw7oDbdQWGd+AKjdTZgdkfAR9MIQCE4L3C5ZFfw4Slq6JvmucIsw7ERiulShcWrVMvseVAEYMNTm+40OWTiWfA1qwc+ANrQJBAP4UpSgD3FY8rJSznUXJCXwougrWwZQYuaYx4MmR2QGV+GIq8HF6QVVr9XzFAD/WIjtVm7nM+3pIS1ST9HPnQOncCQQDL80JMeNdMrXNqJZsy+3oR0mAFXhXi2MObBnnOHce44QwNO6TqyqPhLx7M55MwNNyA9/YTi1MmhWkLWRdTt+dek7AkEA9JoDdUZiNFMtAer4mVo022aJ1C1zJpO3Bhw2f1b9Rty2R7lYxmDFC1eo+8MzvkDzq5N626BO6SX3/3CAgaQ7heQJAHHyz6/6NBBbOIityjB5sneSFe3YXMEuQ+T8cUF/0f6xfhJGqLWl0joWIZdKKypb3ncQEySIS0TSdQYqGKZkir9QJAbW1uPEy4+7g0yXwG3YtJNE2UD9JIb+qzejeAAKcdglqrEZpJNdGpjG6hlp3XWqM6YXexrEfTs+CCskhGwSQe/RdA==+-----END PRIVATE KEY-----
+ test/cert/ca.pem view
@@ -0,0 +1,15 @@+-----BEGIN TRUSTED CERTIFICATE-----+MIICTTCCAbYCCQCy3IPKsROAWjANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJD+TjETMBEGA1UECgwKV2ludGVybGFuZDENMAsGA1UECwwEVGVzdDEPMA0GA1UEAwwG+V2ludGVyMScwJQYJKoZIhvcNAQkBFhh3aW50ZXJsYW5kMTk4OUBnbWFpbC5jb20w+HhcNMTYwNzE5MDYzNjA5WhcNMTcwNzE5MDYzNjA5WjBrMQswCQYDVQQGEwJDTjET+MBEGA1UECgwKV2ludGVybGFuZDENMAsGA1UECwwEVGVzdDEPMA0GA1UEAwwGV2lu+dGVyMScwJQYJKoZIhvcNAQkBFhh3aW50ZXJsYW5kMTk4OUBnbWFpbC5jb20wgZ8w+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMprzly4sBhDqgmdzwuXs7rlPZvRiZ3y+SYmgGpTb9QZIPPTVTQ69GUC0WmeLtW/zni0YPUgowDUTf16XLDjC3wT0kUvk6ek/+rQgs/AToJcWBfYxCSElPRSGb5eiXQX8N+HPNXdViJEUvhoDv3LgltliFZiY3BxDJ+3tkWJOL7lshtAgMBAAEwDQYJKoZIhvcNAQELBQADgYEATPckjNo9SmFuSQWI74ec+xvghpo/wG/dR7JHBcPMCftRmK5eKPkqrrtHhQz5LA61jhW+uL0q2+W6/oE+GvZnv+QWaMtuN0YKdPM4yVQR51AMZwwQd7OXo8qgaCAoI7Be8JMzOlS3pERLlupFpn8D9e+Te0iF3xlZX1kDbWUnS++oGM=+-----END TRUSTED CERTIFICATE-----
+ test/cert/client.crt view
@@ -0,0 +1,14 @@+-----BEGIN CERTIFICATE-----+MIICJTCCAY4CAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCQ04xEzARBgNV+BAoMCldpbnRlcmxhbmQxDTALBgNVBAsMBFRlc3QxDzANBgNVBAMMBldpbnRlcjEn+MCUGCSqGSIb3DQEJARYYd2ludGVybGFuZDE5ODlAZ21haWwuY29tMB4XDTE2MDcx+ODA5NDYwNloXDTE3MDcxODA5NDYwNlowSzELMAkGA1UEBhMCQ04xEzARBgNVBAMM+CldpbnRlcmxhbmQxJzAlBgkqhkiG9w0BCQEWGHdpbnRlcmxhbmQxOTg5QGdtYWls+LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3fb1pYRCExJFpKc2ai2i+B3CRsosZS4aCWAghVovCdmq8GOPcfH+IMxhX0hdUluKWOltj4Kl4Chi7iiZUyGNq+kV8CkKJ62WTwqgXUqJ4ku1eHKYXb0WSBj17Y4vCtMMAF/mmB9E/cxY08KOH3/Smp+RqmwDNoPymxmU7VCir256N0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQDAQB1VMXZd+nK/4gW5/vnoq/UOb+kmvoUi9ZPmGSopW2BHop3YEL5IkPn2UaHZH+edU8dkj79LV+xpg0yI3CpTJE+purqNBk799mpLii2pkoDwbnIn1hf7XmPInBNJJvRjqTMlfpaWb4+fGI6U0A27XtWi+6zy5s94arB4uoCrdvUTA==+-----END CERTIFICATE-----
+ test/cert/client.key view
@@ -0,0 +1,15 @@+-----BEGIN RSA PRIVATE KEY-----+MIICXAIBAAKBgQDd9vWlhEITEkWkpzZqLaIHcJGyixlLhoJYCCFWi8J2arwY49x8+f4gzGFfSF1SW4pY6W2PgqXgKGLuKJlTIY2qRXwKQonrZZPCqBdSoniS7V4cphdvR+ZIGPXtji8K0wwAX+aYH0T9zFjTwo4ff9KalGqbAM2g/KbGZTtUKKvbno3QIDAQAB+AoGAevoWw2txiEt5Vm5mUZGS0fhmYLt8ekG9+bQXdHHefelI6allQX0PIu/7yCSw+8x/7B74WqiR6o21sinAOTS/3nCcRKoaass20VIOI0DJLwOXJNliuN9DEr02uoifs+hRUD7MTYAETCEQ58HFldgGxU98uEPcCi1HFEhrymK9EUjYECQQDyFLJ+qK+vu/m5+ydtE+NKu5n8MyFWuMgHeE3FEhGlbQvUOeMwj82XAQN7g8XXiSkpIrHevA7iOJ3jO+0NcXS+wxAkEA6ropuMmJk4oKDLEqTpxBbS2ILjmfVoGjZiKjKZEo9dZ/RnFz4zEB+8SC4h55hpRp0gBBOh/QFNHcPko1WBq3YbQJAPlNX1UZG3T7PP6cZvfs1+vO7GCZn+8M5NLsjgq5xPp8BoaU5ueH8M3l+VQmLIT/eCgo1szvFtTaQZ9V1NU2EnsQJAQJGt+YHOwMLHMSemCZdV9+faIe47GUhmfRT1J/Ok9h0LWCB61bk3Q5u/FUykyWySH36Kc+t7FcrS4DTqEqhPPVkQJBAPFKvcd9z+AuUlnB9/VQbaJGSJ3VDqInJkRu6GVXMvH/+6VP3gdjAf4Jdu2sdTwQv/rSuvZg/6ngWcj6CXfbb0kw=+-----END RSA PRIVATE KEY-----
+ test/cert/server.crt view
@@ -0,0 +1,16 @@+-----BEGIN CERTIFICATE-----+MIIChDCCAe2gAwIBAgIJAMddlD79DkOBMA0GCSqGSIb3DQEBCwUAMGsxCzAJBgNV+BAYTAkNOMRMwEQYDVQQKDApXaW50ZXJsYW5kMQ0wCwYDVQQLDARUZXN0MQ8wDQYD+VQQDDAZXaW50ZXIxJzAlBgkqhkiG9w0BCQEWGHdpbnRlcmxhbmQxOTg5QGdtYWls+LmNvbTAeFw0xNjA3MTkwNzQwMjJaFw0xNzA3MTkwNzQwMjJaMIGAMQswCQYDVQQG+EwJDTjETMBEGA1UECAwKU29tZS1TdGF0ZTETMBEGA1UECgwKV2ludGVybGFuZDEN+MAsGA1UECwwEVGVzdDEPMA0GA1UEAwwGV2ludGVyMScwJQYJKoZIhvcNAQkBFhh3+aW50ZXJsYW5kMTk4OUBnbWFpbC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ+AoGBALVw5Aspmpujd76UGQf6mjutf6DWlmnXCfhiLDOMj+QGonBvbxrbRpIfjpWa+PfGDS/9mwYgDNAaU7cfUqEziwSgjTSVPF5PR721fxYFmG/269qGIuJ6i5ds9t7nQ+JcaBZAxN22Kw3mydZAJlGdG75pO5tTQRQYsG73XcX0n1wfBXAgMBAAGjGjAYMAkG+A1UdEwQCMAAwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4GBAGAcC0nVZYb5+XnS6VXF/HO9mLGb8B67GHDROND0JdS80s2sS063P4Lfks1Sg8KddybYfX+NPGliR+bo9VWBKe8gpegyyCrEb9F60+/SqTAlg3tO4q0AMkjFiyeOalvPz4cp464eatJ9s2+vNCJSOqZ6nYIoDl0K9vmVckYaQxS+2p4+-----END CERTIFICATE-----
+ test/cert/server.key view
@@ -0,0 +1,15 @@+-----BEGIN RSA PRIVATE KEY-----+MIICXAIBAAKBgQC1cOQLKZqbo3e+lBkH+po7rX+g1pZp1wn4YiwzjI/kBqJwb28a+20aSH46Vmj3xg0v/ZsGIAzQGlO3H1KhM4sEoI00lTxeT0e9tX8WBZhv9uvahiLie+ouXbPbe50CXGgWQMTdtisN5snWQCZRnRu+aTubU0EUGLBu913F9J9cHwVwIDAQAB+AoGAOr7RZvfSch/sLk0/CYFPHJFhKE8yJhSUytHmY85X7ElloKfDvltOrxL+lFt7+QM309j1r3cDDwHnJvxCQaUxlozB0JMDqWRVtTx//0NXNW8495H8Rn1Xwwyd1LY+e+2+jGTSKNhvSDXHPHJS93FgbbYoCXvGkVGx2mpDNksDrWDpECQQDkuV7U4KqtapU9+73u2GKnIpJDx3Hjm8N7DDfaA5SS/3kfIABZBfWJMCIbsSRTSvN9s3XsVUEvXpjX1+4uhW4Ck/AkEAyxQIPbuuqv+6bN3qHMLxjVGiNc92anWCifsy+4THWpWV5Q0F4WX5+pIxL3pbuIqJMXQfTCLBAifR5GTT/0XQa6QJBAImWFLyblTASQEpsiA+HEIL4s5Q6+GqRZWrcc7B6nOI8OaEGgA8NLaFjyfC8g2xzVrtTu+j5c+fJ0MluCLl8sIsMCQAVk+RsO80+peV8jEK48P7fHelPvwwigZbpnTPYtH/zL8fbpTGjDd0D76KpmCUFhDDtv5+dTTp2QzQnNZ6fcBF4OECQCkeFGnhs/YLyev2WsFx0rI8AlrjCeGq+t2HSv+/vc8M+Zv4P6QLK46sX0wy7xkCdtSzhd6tD/HCl/KT0y45q+DY=+-----END RSA PRIVATE KEY-----