FiloSottile / mkcert

A simple zero-config tool to make locally trusted development certificates with any names you'd like.
https://mkcert.dev
BSD 3-Clause "New" or "Revised" License
49.31k stars 2.55k forks source link

macOS: unable to get local issuer certificate #431

Open Lilja opened 2 years ago

Lilja commented 2 years ago
$ openssl s_server -accept 8443 -key certs/proxy.customs-key.pem -cert certs/proxy.customs.pem -www
Using auto DH parameters
Using default temp ECDH parameters
ACCEPT
4569913004:error:14037418:SSL routines:ACCEPT_SR_KEY_EXCH:tlsv1 alert unknown ca:/System/Volumes/Data/SWE/macOS/BuildRoots/533514bb11/Library/Caches/com.apple.xbs/Sources/libressl/libressl-75.60.3/libressl-2.8/ssl/ssl_pkt.c:1200:SSL alert number 48
4569913004:error:140370E5:SSL routines:ACCEPT_SR_KEY_EXCH:ssl handshake failure:/System/Volumes/Data/SWE/macOS/BuildRoots/533514bb11/Library/Caches/com.apple.xbs/Sources/libressl/libressl-75.60.3/libressl-2.8/ssl/ssl_pkt.c:951:
ACCEPT
$ curl "https://proxy.customs:8443"
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.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.
$ mkcert -install
The local CA is already installed in the system trust store! 👍
Warning: "certutil" is not available, so the CA can't be automatically installed in Firefox! ⚠️
Install "certutil" with "brew install nss" and re-run "mkcert -install" 👈

Mac keychain:

image

macOS: 12.2.1 OpenSSL: LibreSSL 2.8.3 curl: curl 7.79.1 mkcert: v1.4.3

jerrybendy commented 2 years ago

Same issue for me. Using export CURL_CA_BUNDLE="/etc/ssl/cert.pem" can solve this. Is it any methods easier?

FX-HAO commented 1 year ago

I have no issue with curl. but I got the issue with ruby, python. did u fix it?

env:

> curl --version
curl 7.79.1 (x86_64-apple-darwin21.0) libcurl/7.79.1 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.11 nghttp2/1.45.1
Release-Date: 2021-09-22
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS GSS-API HSTS HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz MultiSSL NTLM NTLM_WB SPNEGO SSL UnixSockets

curl

> curl -X POST https://api.localhost/v1/url
*   Trying 127.0.0.1:443...
* Connected to api.localhost (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: O=
*  start date: Jan 19 07:00:18 2023 GMT
*  expire date: Apr 19 07:00:18 2025 GMT
*  subjectAltName: host "api.localhost" matched cert's "api.localhost"
*  issuer: O=
*  SSL certificate verify ok.
> POST /v1/url HTTP/1.1
> Host: api.localhost
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< connection: close
< content-type: application/json; charset=utf-8
< content-length: 40
< Date: Thu, 19 Jan 2023 14:00:54 GMT

ruby

> RestClient.post("https://api.localhost/v1/url", nil, nil)

Traceback (most recent call last):
/Users/fuxin.haocrypto.com/.rbenv/versions/2.7.6/lib/ruby/2.7.0/net/protocol.rb:44:in `connect_nonblock': SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate) (OpenSSL::SSL::SSLError)
/Users/fuxin.haocrypto.com/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/rest-client-2.0.2/lib/restclient/request.rb:758:in `rescue in transmit': SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate) (RestClient::SSLCertificateNotVerified)

python

> requests.post("https://api.localhost:443/v1/url")

File /usr/local/lib/python3.9/site-packages/requests/api.py:119, in post(url, data, json, **kwargs)
    107 def post(url, data=None, json=None, **kwargs):
    108     r"""Sends a POST request.
    109
    110     :param url: URL for the new :class:`Request` object.
   (...)
    116     :rtype: requests.Response
    117     """
--> 119     return request('post', url, data=data, json=json, **kwargs)

File /usr/local/lib/python3.9/site-packages/requests/api.py:61, in request(method, url, **kwargs)
     57 # By using the 'with' statement we are sure the session is closed, thus we
     58 # avoid leaving sockets open which can trigger a ResourceWarning in some
     59 # cases, and look like a memory leak in others.
     60 with sessions.Session() as session:
---> 61     return session.request(method=method, url=url, **kwargs)

File /usr/local/lib/python3.9/site-packages/requests/sessions.py:542, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    537 send_kwargs = {
    538     'timeout': timeout,
    539     'allow_redirects': allow_redirects,
    540 }
    541 send_kwargs.update(settings)
--> 542 resp = self.send(prep, **send_kwargs)
    544 return resp

File /usr/local/lib/python3.9/site-packages/requests/sessions.py:655, in Session.send(self, request, **kwargs)
    652 start = preferred_clock()
    654 # Send the request
--> 655 r = adapter.send(request, **kwargs)
    657 # Total elapsed time of the request (approximately)
    658 elapsed = preferred_clock() - start

File /usr/local/lib/python3.9/site-packages/requests/adapters.py:514, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    510         raise ProxyError(e, request=request)
    512     if isinstance(e.reason, _SSLError):
    513         # This branch is for urllib3 v1.22 and later.
--> 514         raise SSLError(e, request=request)
    516     raise ConnectionError(e, request=request)
    518 except ClosedPoolError as e:

SSLError: HTTPSConnectionPool(host='api.localhost', port=443): Max retries exceeded with url: /v1/url (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)')))
FX-HAO commented 1 year ago

I fixed my problems. for Python, The reason is that Python Requests uses certificates from the python-certifi package., not those of the underlying operating system. so the fix is

In [19]:  import certifi
    ...:  print(certifi.where())
/usr/local/lib/python3.9/site-packages/certifi/cacert.pem

cat "$(mkcert -CAROOT)/rootCA.pem" >> /usr/local/lib/python3.9/site-packages/certifi/cacert.pem

For ruby, thanks to emboss's answer it's more complicated because Ruby does use any system trust certificates by default. My workaround:

module SetDefaultOpenSSLTrustStore
  def initialize(*args, **kwargs)
    super

    cert_store = OpenSSL::X509::Store.new
    cert_store.set_default_paths
    @cert_store = cert_store
  end
end

Net::HTTP.prepend SetDefaultOpenSSLTrustStore
shnikola commented 1 year ago

Ruby can also be solved by added the root certificate to the open_ssl certs folder. First, get OPENSSL_CERT_PATH that ruby by running irb:

require "openssl"
puts OpenSSL::X509::DEFAULT_CERT_DIR

Depending how you installed Ruby, you might get something like ~/.rbenv/versions/3.1.4/openssl/ssl/certs

In your shell, copy the mkcert's RootCA to this directory:

cp "$(mkcert -CAROOT)/rootCA.pem" <OPENSSL_CERT_PATH>

Then reload the certs for this openssl with

<OPENSSL_CERT_PATH>/../../../bin/c_rehash

(e.g. ~/.rbenv/versions/3.1.4/openssl/bin/c_rehash)

re1ad commented 1 year ago

for python u can run command: /Applications/Python/Install Certificates.command

toufali commented 8 months ago

Ruby can also be solved by added the root certificate to the open_ssl certs folder. First, get OPENSSL_CERT_PATH that ruby by running irb:

require "openssl"
puts OpenSSL::X509::DEFAULT_CERT_DIR

Depending how you installed Ruby, you might get something like ~/.rbenv/versions/3.1.4/openssl/ssl/certs

In your shell, copy the mkcert's RootCA to this directory:

cp "$(mkcert -CAROOT)/rootCA.pem" <OPENSSL_CERT_PATH>

Then reload the certs for this openssl with

<OPENSSL_CERT_PATH>/../../bin/c_rehash

(e.g. ~/.rbenv/versions/3.1.4/openssl/bin/c_rehash)

Thanks @shnikola your steps worked for me except the last one – the file was not at the correct location. Instead I did:

brew info openssl

which revealed the following caveat:

To add additional certificates, place .pem files in /opt/homebrew/etc/openssl@3/certs and run /opt/homebrew/opt/openssl@3/bin/c_rehash

following the caveat did the trick:

/opt/homebrew/opt/openssl@3/bin/c_rehash