Reputation: 165
I generated a certificate in the directory of my Haskell app:
openssl genrsa -out key.pem 2048
openssl req -new -key key.pem -out certificate.csr
openssl x509 -req -in certificate.csr -signkey key.pem -out certificate.pem
and then I ran my app:
import Network.Wai
import Network.Wai.Handler.Warp
import Servant
import Network.Wai.Handler.WarpTLS
startApp :: IO ()
startApp = do
let port = 3345
let tls = tlsSettings "certificate.csr" "key.pem"
runTLS tls (setPort port defaultSettings) app
And then went to https://localhost:3345 and I got an error "empty certificate chain"
What's wrong with it? Maybe I've put my certificate somewhere such as "/opt/...."?
For now all 3 files are in the root directory of my application: key.pem, certificate.csr and certificate.pem.
UPDATE:
It's arch linux, whereas on hosting I have Ubuntu, therefore I'll need a solution for both.
This certificate is self-signed, whereas on hosting it's issued by let's encrypt.
I've changed a code a bit: "csr" to pem:
let tls = tlsSettings "certificate.pem" "key.pem"
runTLS tls (setPort port defaultSettings) app
Here's another error:
$ curl -v https://localhost:3345
* Rebuilt URL to: https://localhost:3345/
* Trying ::1...
* connect to ::1 port 3345 failed: Connection refused
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3345 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, Client hello (1):
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
Upvotes: 0
Views: 3190
Reputation: 105886
TL;DR: Handling certificates is hard. Use the self-signed .pem
file (and the key) and add security exceptions to your browser or your operating system. The .csr
file isn't something you can use. The server is fine (except for the .csr
file), but you will end up with security warnings till you get your key signed by a CA (which isn't possible for local domain names).
What's wrong with it?
The server is (almost) fine. However, you want to send the .pem
file. The .csr
is a request for signing your certificate by a certificate authority (CA). A CA needs to get trusted by the user or another CA (that's trusted by the user). Let's have a look at your original commands:
openssl genrsa -out key.pem 2048
This will generate the private key, which will get used for the TLS handshake and other operations.
openssl req -new -key key.pem -out certificate.csr
This will generate the mentioned certificate sigining request. You would send this request to a CA, which checks your identity. For example, they would check whether the FQDN actually is yours. Otherwise, you could ask for a certificate for stackoverflow.com and use a MITM attack. This also concludes that you cannot ask a (non-local) CA for a certificate on a local name, like localhost
or hostname.local.domain.name
.
openssl x509 -req -in certificate.csr -signkey key.pem -out certificate.pem
This will take the original certificate request, use your own key to sign it, and generate a certificate (certificate.pem
). Usually, this certificate contains a CA chain, e.g. who signed the CA's certificate, who signed the certificate of the one that signed the CA's certificate and so on, till we end up at a root certificate.
So, all you have to do is to use that certificate together with your private key. This will lead to security warnings though, since you cannot prove your own identity. That's why you need --insecure
in curl
and a security exception in most browsers. That aside, it will work. Note that the server doesn't know that its certificate is self-signed. It's just using two files to initiate the communication with the client.
Upvotes: 3