5

I am trying to create a web server using happstack-server-tls that will use a certificate signed by a private CA. Unfortunately, the TLS handshake only seems to succeed if I give the server a self-signed certificate. Wireshark shows that when my server is using a certificate signed by my private CA, instead of sending the client a Server Hello message, it sends a Fatal (2) Alert message reporting Handshake Failure (40).

This is not a case of the web browser rejecting the server's certificate; Wireshark shows that the TLS handshake never even gets to the point where the server presents a certificate to the browser. Nor is it a case of failure to agree on cryptographic algorithms, since the server runs without problems using a self-signed certificate that uses the same algorithm as the one I want to use. The certificate itself seems valid, since it works as expected using openssl s_server on the command line, and happstack-server-tls appears to use OpenSSL for TLS operations, so I don't think it's a problem with how the certificate is generated.

What do I need to do to get happstack-server-tls to work with a non-self-signed certificate?

The following is a minimal Haskell program that uses happstack-server-tls to run a web server on port 8443 that demonstrates the problem. It takes up to three arguments: the file containing the server's private key, the file containing the server's certificate, and (optionally) the file containing the CA's certificate.

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
                             }

The above program works using a self-signed certificate generated via the following script:

#! /bin/sh

openssl ecparam -name secp384r1 -genkey -out selfsigned.key
openssl req -new -x509 -days 365 -key selfsigned.key -out selfsigned.crt

And running the following command:

runghc Main.hs selfsigned.key selfsigned.crt

It works in that a web browser can connect to the server successfully (albeit complaining about a self-signed certificate, as expected).

However, it doesn't work using a non-self-signed certificate generated by the following script, which first creates a new private CA and then uses that CA to generate the certificate for the server:

#! /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

When I run the following command:

runghc Main.hs onelevel-server.key onelevel-server.crt ca/cacert.pem

Web browsers can't connect because (as shown by Wireshark) the initial TLS handshake fails.

However, a web browser can establish a TLS connecting when using openssl to act as a server using the same certificate:

openssl s_server -accept 8443 -key onelevel-server.key -cert onelevel-server.crt -CAfile ca/cacert.pem

To complete the example, here is the configuration file onelevel.openssl.cnf referenced earlier that provides the configuration for the private 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
Paul Kuliniewicz
  • 2,711
  • 18
  • 24
  • 1
    I have successfully used happstack-server-tls with self-signed certificates and with the free certificates from startssl.com. If you try the URL in, http://www.sslshopper.com/ssl-checker.html, does it provide any useful data? – stepcut Jan 06 '14 at 22:09
  • It appears that the link you provided doesn't support ECDSA certificates -- it says "No SSL certificates were found", but if I use RSA certificates instead, it gives sensible results. But that gave me the idea to try using RSA certificates with happstack-server-tls instead of ECDSA certificates, and it worked fine. So, the problem appears to be with non-self-signed ECDSA certificates, but not with non-self-signed RSA certificates nor with self-signed ECDSA certificates. Any ideas why that might be? – Paul Kuliniewicz Jan 07 '14 at 03:42
  • I have no idea. I don't even know what ECDSA is. I am certainly open to trying to get this fixed though. – stepcut Jan 09 '14 at 02:43
  • Elliptic Curve Digital Signature Algorithm (https://en.wikipedia.org/wiki/ECDSA). OpenSSL supports it, so I'm not sure why it's not working in this particular case. I'm still open to the idea that the problem has something to do with the way I'm generating the certificates, but right now I have no idea why it's failing only in one specific scenario. – Paul Kuliniewicz Jan 11 '14 at 01:12
  • I have logged a bug, https://code.google.com/p/happstack/issues/detail?id=245 – stepcut Jan 14 '14 at 20:17

0 Answers0