我正在尝试使用happstack-server-tls创建一个Web服务器,该服务器将使用由私有CA签名的证书。不幸的是,如果我给服务器一个自签名证书,那么TLS握手似乎只会成功。 Wireshark显示,当我的服务器使用由我的私有CA签名的证书时,它不会向客户端发送服务器Hello消息,而是发送致命(2)警报消息,报告握手失败(40)。
这不是Web浏览器拒绝服务器证书的情况; Wireshark显示TLS握手甚至从未达到服务器向浏览器提供证书的程度。也不是未能就加密算法达成一致的情况,因为服务器使用与我想要使用的算法相同的自签名证书运行时没有问题。证书本身似乎有效,因为它在命令行上使用openssl s_server
按预期工作,并且happstack-server-tls似乎使用OpenSSL进行TLS操作,所以我不认为这是证书如何是一个问题生成。
我需要做些什么才能让happstack-server-tls与非自签名证书一起使用?
以下是一个最小的Haskell程序,它使用happstack-server-tls在端口8443上运行Web服务器来演示该问题。它最多需要三个参数:包含服务器私钥的文件,包含服务器证书的文件,以及(可选)包含CA证书的文件。
module Main ( main ) where
import Happstack.Server.Response (ok)
import Happstack.Server.SimpleHTTPS (TLSConf (..), nullTLSConf, simpleHTTPS)
import System.Environment (getArgs)
main :: IO ()
main = do args <- getArgs
case args of
[keyFile, certFile] -> runServer keyFile certFile Nothing
[keyFile, certFile, caFile] -> runServer keyFile certFile (Just caFile)
runServer :: FilePath -> FilePath -> Maybe FilePath -> IO ()
runServer keyFile certFile caFile = simpleHTTPS conf $ ok ":-)"
where conf = nullTLSConf { tlsPort = 8443
, tlsCert = certFile
, tlsKey = keyFile
, tlsCA = caFile
}
上述程序使用通过以下脚本生成的自签名证书:
#! /bin/sh
openssl ecparam -name secp384r1 -genkey -out selfsigned.key
openssl req -new -x509 -days 365 -key selfsigned.key -out selfsigned.crt
运行以下命令:
runghc Main.hs selfsigned.key selfsigned.crt
它的工作原理是Web浏览器可以成功连接到服务器(尽管如预期的那样抱怨自签名证书)。
但是,使用由以下脚本生成的非自签名证书不起作用,该脚本首先创建新的专用CA,然后使用该CA为服务器生成证书:
#! /bin/sh
rm -rf ca
mkdir ca
mkdir ca/newcerts
mkdir ca/private
touch ca/index.txt
echo "100001" >ca/serial
openssl ecparam -name secp384r1 -genkey -out ca/private/cakey.pem
openssl req -new -x509 -days 365 -key ca/private/cakey.pem -out ca/cacert.pem
openssl ecparam -name secp384r1 -genkey -out onelevel-server.key
openssl req -new -days 365 -key onelevel-server.key -out onelevel-server.req
openssl ca -config onelevel.openssl.cnf -in onelevel-server.req -out onelevel-server.crt
当我运行以下命令时:
runghc Main.hs onelevel-server.key onelevel-server.crt ca/cacert.pem
Web浏览器无法连接,因为(如Wireshark所示)初始TLS握手失败。
但是,当使用openssl
作为使用相同证书的服务器时,网络浏览器可以建立TLS连接:
openssl s_server -accept 8443 -key onelevel-server.key -cert onelevel-server.crt -CAfile ca/cacert.pem
要完成此示例,以下是前面提到的配置文件onelevel.openssl.cnf
,它提供了私有CA的配置:
[ ca ]
default_ca = CA_onelevel
[ CA_onelevel ]
dir = ./ca
certs = $dir/certs
crl_dir = $dir/crl
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem
serial = $dir/serial
crlnumber = $dir/crlnumber
crl = $dir/crl.pem
private_key = $dir/private/cakey.pem
x509_extensions = onelevel_cert
name_opt = ca_default
cert_opt = ca_default
default_days = 365
default_crl_days = 30
default_md = default
preserve = no
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = match
commonName = supplied
emailAddress = optional
[ onelevel_cert ]
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer