5

我正在尝试使用happstack-server-tls创建一个 Web 服务器,该服务器将使用由私有 CA 签名的证书。不幸的是,TLS 握手似乎只有在我给服务器一个自签名证书时才能成功。Wireshark 显示,当我的服务器使用由我的私有 CA 签名的证书时,它不会向客户端发送 Server Hello 消息,而是发送 Fatal (2) Alert 消息报告握手失败 (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 握手失败。

但是,Web 浏览器在用作使用相同证书的服务器时可以建立 TLS 连接:openssl

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
4

0 回答 0