3

I've created a self-signed certificate for foo.localhost using a Let's Encrypt recommendation using this Makefile:

include ../.env

configuration = csr.cnf
certificate = self-signed.crt
key = self-signed.key

.PHONY: all
all: $(certificate)

$(certificate): $(configuration)
    openssl req -x509 -out $@ -keyout $(key) -newkey rsa:2048 -nodes -sha256 -subj '/CN=$(HOSTNAME)' -extensions EXT -config $(configuration)

$(configuration):
    printf "[dn]\nCN=$(HOSTNAME)\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:$(HOSTNAME)\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth" > $@

.PHONY: clean
clean:
    $(RM) $(configuration)

I've then assigned that to a web server. I've verified that the server returns the relevant certificate:

$ openssl s_client -showcerts -connect foo.localhost:8443 < /dev/null
CONNECTED(00000003)
depth=0 CN = foo.localhost
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = foo.localhost
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=foo.localhost
   i:/CN=foo.localhost
-----BEGIN CERTIFICATE-----
[…]
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=foo.localhost
issuer=/CN=foo.localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1330 bytes and written 269 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: […]
    Session-ID-ctx: 
    Master-Key: […]
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket:
    […]

    Start Time: 1529622990
    Timeout   : 7200 (sec)
    Verify return code: 21 (unable to verify the first certificate)
    Extended master secret: no
---
DONE

How do I make cURL trust it without modifying anything in /etc? --cacert does not work, presumably because there is no CA:

$ curl --cacert tls/foo.localhost.crt 'https://foo.localhost:8443/'
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

The goal is to enable HTTPS during development:

  • I can't have a completely production-like certificate without a lot of work to enable DNS verification in all development environments. Therefore I have to use a self-signed certificate.
  • I still obviously want to make my development environment as similar as possible to production, so I can't simply ignore any and all certificate issues. curl -k is like catch (Exception e) {} in this case - nothing at all like a browser talking to a web server.

In other words, when running curl [something] https://project.local/api/foo I want to be confident that

  1. if TLS is configured properly except for having a self-signed certificate the command will succeed and
  2. if I have any issues with my TLS configuration except for having a self-signed certificate the command will fail.

Using HTTP or --insecure fails the second criterion.

  • There seems to be a solution there: stackoverflow.com/a/21262787/6368697 – Patrick Mevzek Jun 22 '18 at 2:20
  • @PatrickMevzek No, "without modifying anything in /etc" is not satisfied by that solution. – l0b0 Jun 22 '18 at 2:58
  • Although there's no real CA, a selfsigned cert is effectively treated as its own CA for validation purposes. Try openssl x509 <file to make sure it's in the right format and openssl s_client ... -CAfile file to see if that validates. (BTW -showcerts only applies to chain certs from the server and is meaningless when there are no chain certs.) Also, curl doesn't always use OpenSSL and if not it doesn't always accept exactly the same formats; check curl -V (uppercase V). – dave_thompson_085 Jun 22 '18 at 8:44
  • What do you mean by "make cURL trust it"? In general there is no notion of "trust" for self-signed certificates since anyone can make them. What is that you want? Only to accept that one certificate's fingerprint? Only a certain certificate including the extensions? Something else? – V13 Oct 17 '18 at 22:49
  • I'm having a similar issue. I get the certificate chain of a self-signed CA of our corporate proxy using the openssl s_client -showcerts answer, but curl -v --cacert cacert.pem URL won't add the self-signed CA as an explicit whitelisting of trust with CERT_TRUST_REVOCATION_STATUS_UNKNOWN. – Josh Peak Oct 24 '18 at 23:13
0

Following these steps should solve your issue:

  1. Download and save the self-signed certificate: echo quit | openssl s_client -showcerts -servername "${API_HOST}" -connect "${API_HOST}":443 > cacert.pem
  2. Tell the curl client about it: curl --cacert cacert.pem --location --silent https://${API_HOST}

Also one could use wget and ignore certificates with: wget --no-check-certificate https://${API_HOST}

  • 2
    -1 I already tried --cacert. And I'm definitely not interested in ignoring the certificate. This is terrible advice. – l0b0 Sep 11 '18 at 19:35
  • I understand your frustration. Did you try the openssl command and save the cert from the server you are connecting to? The ignore is just another option for someone else to try if they don't really need to test with HTTPS. – Robert Brisita Sep 11 '18 at 23:55
  • I already have the certificate. Please read my question. – l0b0 Sep 12 '18 at 1:39
  • That is where the problem lies. I believe you are using the certificate from the --out argument rather than following the steps in my answer. – Robert Brisita Sep 12 '18 at 16:55
  • I created the certificate, so I don't need to retrieve it. That is not what the question is about, and rather than assuming that I don't have the certificate I would suggest using comments to ask such follow-up questions. – l0b0 Sep 12 '18 at 19:27
0

A general guide without most of the little details.

  • The openssl s_client command is showing two errors:

    verify error:num=20:unable to get local issuer certificate
    verify error:num=21:unable to verify the first certificate
    

    That means that the default cert store in your machine is missing the needed certs.

Steps:

  1. You could build a new directory (anywhere), process it with the c_rehash script and tell openssl to use it to verify the certs with the option -CApath Directory. Make changes until you get rid of both errors while using the -CApath option.

  2. Then, tell curl about the certificate directory with:

    curl --capath <dir>
    

    and all the other options needed.

If you get blocked somewhere, comment.

  • I fail to understand. I which way do you need help? | | | | It should be easy to make a directory with the root certificate and tell curl to trust what is inside that directory. (Cont....) @l0b0 – Isaac Oct 17 '18 at 16:13
  • (Cont...) But that is not the whole solution. You need to make very sure that if a server provides a chain that have that root certificate as the last trust link (or an intermediate certificate) the chain will be accepted and trusted by curl (or any application using it). The only reliable way to do this test is to test the certificate (and the whole chain) in a new web server (a VM should do the trick). If you (1) get the root certificate to validate (without any verify error:num=) with the VM web server and you (2) copy the root server to a new directory and (Cont...) @l0b0 – Isaac Oct 17 '18 at 16:14
  • (3) give the trust chain to the old server, you should get it working. Sorry I can not find a simpler way to describe it. @l0b0 – Isaac Oct 17 '18 at 16:15
  • When creating self-signed certificates there is no separate root certificate. I've added the certificate creation Makefile to my question to clarify this. – l0b0 Oct 17 '18 at 23:35
  • If I need to create a self-signed CA certificate to do this, that would be fine. I just thought (naively, it seems) that there would be a simple way to do this. – l0b0 Oct 18 '18 at 0:06
-3

Try -k:

curl -k https://yourhost/

It should "accept" self-signed certificates

  • 2
    The question is how to trust self-signed certificates, not how to bypass certificate validation. – l0b0 Oct 17 '18 at 0:08
  • @l0b0: To make curl trust self-signed certificates. And it also says: "The goal is to enable HTTPS during development". curl -k achieves both. There is no validation in self-signed certificates, unless you are implying that you want to accept only a certain self-signed certificate, but this is not what the question says. Can you explain what is your objection? – V13 Oct 17 '18 at 0:14
  • I wrote "How do I make cURL trust it". If I asked you how to open SSH to a specific IP, would you tell me to open it to every IP? – l0b0 Oct 17 '18 at 23:37
  • What does "trust it" means in your mind? Is that a CA certificate (i.e. has the CA extensions) that you want to trust as CA? or is it a plain certificate and you want to make sure that this is the one you're receiving? Do you just want to look at the fingerprint of the key or the whole set of extensions? Do you maybe care just about the CN? – V13 Oct 17 '18 at 23:48
  • You can see from the certificate that it does not have CA extensions. It's a self-signed certificate. I want cURL to do the same validations it does for any certificate. As man curl puts it "The server connection is verified by making sure the server's certificate contains the right name and verifies successfully using the cert store." Maybe that means I need a way to create a cert store for a self-signed certificate. – l0b0 Oct 18 '18 at 0:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.