I’ve finished my first quarter as a Software Engineer on the Local team, which means I now have a couple of sprints under my belt!
ERR_CERT_INVALID error in Chrome. The specific issue was first reported in the Local Community Forums: SSL Not working on Chrome with NET::ERR_CERT_INVALID
This sort of error was difficult to troubleshoot for a few reasons:
- Chrome doesn’t tell you what exactly is invalid about a certain certificate.
- Searching the internet for more info about this error led me to results like the answers in this StackOverflow question, which basically boil down to bypassing HTTPS:
Bypassing security isn’t a solution; it’s a temporary workaround and certainly isn’t an option when you’re the dev who’s making a tool that generates SSL certificates.
I learned a lot while working on this issue and thought it best to document some of the tools and resources I found while investigating this error!
Not all ERR_CERT_INVALID errors are the same.
Because there are many potential reasons for a certificate to be invalid, there isn’t an easy-to-find solution for every case. I couldn’t find a definitive list of what Chrome looks for, but the main reason seems to be incorrect dates.
Invalid Cert due to Invalid Dates
When a certificate is generated, it has a Valid Before and a Valid After date:
Certificate: Data: Version: 3 (0x2) Serial Number: 9b:6b:3d:a3:b9:a3:a4:b4 Signature Algorithm: sha256WithRSAEncryption Issuer: CN=local.mydomain.com Validity Not Before: Nov 19 13:27:30 2016 GMT Not After : Nov 19 13:27:30 2017 GMT Subject: CN=local.mydomain.com
The above cert will certainly fail on most systems today because most systems think they are ahead of 2017.
Note that I mentioned “most systems think” — remember that a computer needs to set its notion of time from someplace. If the dates for a certificate seem valid, one possible avenue to explore is if a computer’s Date/Time settings are correct. Pay particular attention to if a system is oddly connecting to the network (like a VPN or a “helpful-IT” work network.)
Something about the chain is invalid.
I know that heading isn’t helpful, but I encourage you to think creatively about what sort of things would make a browser angry. In my case, the issue was caused by a doubling up of the certificate. In particular, Chrome was reporting two of the same PEM blocks.
In my case, I zeroed in on why Chrome was complaining but now needed to determine how the sites came to be in this state.
Tools for Investigation
Investigating an SSL Certificate using Chrome
I was able to get a general idea of why Chrome didn’t like the site’s certificate by clicking the area to the left of the address bar. You’ll see a lock icon for sites where SSL is working correctly. Sites with miss-configured SSL certificates will be obvious — Chrome won’t let you do anything!
This screencast covers the basics of examining the certificate and drilling down to specific pieces of data. In particular, note that:
- You can click the icon next to the address bar for a window to drill down to specifics.
- For most SSL-related errors, an “Advanced” button will show additional details about what went wrong.
- The actual error (in this case
ERR_CERT_INVALID) is clickable and allows you to see the raw PEM-encoded certificate.
Investigating a certificate using OpenSSL
I like polished GUIs like anyone else, but Chrome’s certificate navigator wasn’t doing what I needed to do. At this point, I turned to OpenSSL to query and decode the certificate that the server was giving. Here’s a breakdown of the commands that helped me along the way.
Using OpenSSL as a TLS Client
openssl s_client -showcerts -connect example.local:443 -servername example.local
This command allows you to make and test a connection over HTTPS using OpenSSL. You can read the parts of the above command as:
openssl s_client— Use s_client, which is OpenSSL’s generic SSL/TLS client for connecting to hosts.
-showcerts— Include the server’s certificate in the output.
-connect example.local:443— Where the connection should be made. In this case, specifying the specific HTTPS port ensures that we are working with a secure connection.
-servername example.local— This indicates what domain you want the cert for. This is most useful when the server responds to more than one domain.
The above command should give an output similar to this:
depth=0 CN = invalid-cert-c1.local, C = XX, ST = XX, L = Fake Locality, O = "Super Fake Company, Fake.", OU = Fake Organizational Unit verify error:num=18:self signed certificate verify return:1 depth=0 CN = invalid-cert-c1.local, C = XX, ST = XX, L = Fake Locality, O = "Super Fake Company, Fake.", OU = Fake Organizational Unit verify return:1 CONNECTED(00000005) --- Certificate chain 0 s:/CN=invalid-cert-c1.local/C=XX/ST=XX/L=Fake Locality/O=Super Fake Company, Fake./OU=Fake Organizational Unit i:/CN=invalid-cert-c1.local/C=XX/ST=XX/L=Fake Locality/O=Super Fake Company, Fake./OU=Fake Organizational Unit -----BEGIN CERTIFICATE----- MIIEfTCCA2WgAwIBAgIJHCV7Ifs31UxeMA0GCSqGSIb3DQEBCwUAMIGZMR4wHAYD VQQDExVpbnZhbGlkLWNlcnQtYzEubG9jYWwxCzAJBgNVBAYTAlhYMQswCQYDVQQI EwJYWDEWMBQGA1UEBxMNRmFrZSBMb2NhbGl0eTEiMCAGA1UEChMZU3VwZXIgRmFr ZSBDb21wYW55LCBGYWtlLjEhMB8GA1UECxMYRmFrZSBPcmdhbml6YXRpb25hbCBV bml0MB4XDTIyMDIwNDE2MDUxMFoXDTMyMDIwNDE2MDUxMFowgZkxHjAcBgNVBAMT FWludmFsaWQtY2VydC1jMS5sb2NhbDELMAkGA1UEBhMCWFgxCzAJBgNVBAgTAlhY MRYwFAYDVQQHEw1GYWtlIExvY2FsaXR5MSIwIAYDVQQKExlTdXBlciBGYWtlIENv bXBhbnksIEZha2UuMSEwHwYDVQQLExhGYWtlIE9yZ2FuaXphdGlvbmFsIFVuaXQw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/BGKuaQTetBNytk54co3u E7/hbK5b2PnwpWBO7j37+qpqtiyh5iy5RMlXcjEpnTj5oeSGuZppEa0onDO04rFo Yz9PLawcGlH2NgZqNqAHczVEl5Q5wElqAQNCnXRA3bjvgWzV2VU5bTgVwIMF3ppw J6eVPfrxT7GBzVzjAXdOMlJVNLETq+yiniEkN1+nEmc2tIq3ypN4YQJlwNryQ6e6 2zEebgnaPDQaQyDkEeOpQM+EYqeqpDjRqR46rG3gGRALlJs1kKigNAI4ozk0eZQK /bTRNCtIOjcjcTu7wwQNRgqWoeFLR3+d9qA/Sdq0d2xC8tOC/7ua+u1pYYNMIJu1 AgMBAAGjgcUwgcIwCQYDVR0TBAIwADALBgNVHQ8EBAMCAvQwOwYDVR0lBDQwMgYI KwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUFBwMI MBEGCWCGSAGG+EIBAQQEAwIA9zAdBgNVHQ4EFgQUssqVADjlsz0/t+bpBK1j2+qy 2hwwOQYDVR0RBDIwMIIVaW52YWxpZC1jZXJ0LWMxLmxvY2FsghcqLmludmFsaWQt Y2VydC1jMS5sb2NhbDANBgkqhkiG9w0BAQsFAAOCAQEAsjSC8Vlndbz58eZqd/7/ f7j86m3NjZxVzzOKMXWSAnoY+3Uy1z0DT+u4uxDMuY6qx+OskmsUW4nVlidicpye T2EZUQuykCTJh/aqP6wdPoPuNJ0kYaG+cq/b+T1gDG0eVWT1jER3PmPl1hpOdWpd kzY1ErUQjN+hQXQ4a31rNUkS7hdMOsu1l+Xwz5n21SkAPc14rIoQfssny0SnCvCQ kV27Pm5OSAgybmVsD/2WnbIC5aEgcGX2rVgLk4kOB+25REXbwkm1KKggW+1Ae5qN ptEZ69kacQj2DZxcNyBZEBZbSu9EUFbvrtHkj+boQcZ7AKYdgWGyq8EkuI+okLAl CA== -----END CERTIFICATE----- --- Server certificate subject=/CN=invalid-cert-c1.local/C=XX/ST=XX/L=Fake Locality/O=Super Fake Company, Fake./OU=Fake Organizational Unit issuer=/CN=invalid-cert-c1.local/C=XX/ST=XX/L=Fake Locality/O=Super Fake Company, Fake./OU=Fake Organizational Unit --- No client certificate CA names sent Server Temp Key: ECDH, P-256, 256 bits --- SSL handshake has read 1859 bytes and written 352 bytes --- New, TLSv1/SSLv3, 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: BA84087FF3DEA801A256A4F0B89D30DDA63098C995B3C1D8770215A2B1DC29D7 Session-ID-ctx: Master-Key: BF6930C37EABB95DFD70A50780956197743BC23BBE6BA3FCAACA8452C36236365734CE4C7244A6110763675CF954CF64 TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - c7 48 17 29 cf 87 1f 51-0c d2 f8 78 cc 64 6e 9e .H.)...Q...x.dn. 0010 - 7f b7 c6 52 cb 71 48 aa-5a a0 54 13 44 7c c2 ba ...R.qH.Z.T.D|.. 0020 - 00 8e 61 d0 d8 42 21 67-4b 3f 0c 87 64 c0 64 48 ..a..B!gK?..d.dH 0030 - 91 73 90 30 5f 25 4f 89-56 ea a6 55 85 4f c8 3d .s.0_%O.V..U.O.= 0040 - 89 18 b2 bd 81 c3 a9 62-12 7d 76 51 6b 5f 33 90 .......b.}vQk_3. 0050 - 2b 38 45 38 ce d2 af 91-cf 49 4f 99 0a d9 6c 4c +8E8.....IO...lL 0060 - 93 8a 79 fb be 77 03 d8-1a b4 08 bf 77 27 36 b5 ..y..w......w'6. 0070 - 2e 98 bd 2c 95 e9 ba 79-9f 05 95 0b e6 3c e2 43 ...,...y.....<.C 0080 - f1 64 b5 25 00 8c f8 b4-3d 01 3f 87 5f dd 97 b4 .d.%....=.?._... 0090 - f7 12 61 ff 61 c4 39 13-5c fc dc 1d fa 37 11 0f ..a.a.9.\....7.. 00a0 - c5 d4 10 df cf 9a 87 05-c3 eb 6e 19 97 f6 1f d1 ..........n..... 00b0 - b6 2b 0b 66 f0 a1 f9 fa-7a ce c1 3d 5a 77 80 48 .+.f....z..=Zw.H 00c0 - c4 f8 a8 5f 65 f2 7e c3-f9 a3 06 e8 e7 a8 87 be ..._e.~......... Start Time: 1644343457 Timeout : 7200 (sec) Verify return code: 0 (ok) --- DONE
Verifying a certificate with OpenSSL
openssl verify -purpose any example.local.pem
Making a secure connection using the
s_client command is useful, but what if you wanted to verify and work with the certificate from the above output? The above
openssl verify command examines a certificate and determines if it is valid.
As a concrete example, you can save the PEM-encoded certificate in the above output to a file and receive output that looks something like this:
★ openssl verify -purpose any daniela.pem daniela.pem: CN = danielaorg.local, C = XX, ST = XX, L = Fake Locality, O = "Super Fake Company, Fake.", OU = Fake Organizational Unit error 18 at 0 depth lookup:self signed certificate OK
For me, on my journey of understanding
ERR_CERT_INVALID, verifying the cert ended up not helping. It confirmed what I knew, which was that this certificate was self-signed. But my next question was: “What exactly is in that certificate?”
Viewing the certificate contents with OpenSSL
openssl x509 -text -in example.local.pem
The above command decrypts the PEM-formated cert to get something more human-readable. Calling out a few items specifically:
- Lines 8-10 — Date validity. Double-check that the certificate is for a date range that makes sense.
- Lines 7,11 — The cert’s Subject and Issuer fields are usually different “things”. In the output below, they are the same organization, which implies that this is a self-signed certificate.
- Lines 36-37 — CA:FALSE means that this certificate can’t be considered a Certificate Authority. This ended up being the source of my bug. The certificate wasn’t authorized to be a Certificate Authority, but it was a self-signed cert. Making it so these certificates could be considered CAs made Chrome stop complaining!
- Lines 39,41 — These lines detail how this certificate can be used. In the previous
openssl verifycommand, we used the
-purpose anyflag to ask if this certificate was verifiable for ANY use.
Certificate: Data: Version: 3 (0x2) Serial Number: 59:c4:b6:1f:0a:8c:81:f6:5b Signature Algorithm: sha256WithRSAEncryption Issuer: CN=brayanname.local, C=XX, ST=XX, L=Fake Locality, O=Super Fake Company, Fake., OU=Fake Organizational Unit Validity Not Before: Feb 15 22:41:37 2022 GMT Not After : Feb 15 22:41:37 2032 GMT Subject: CN=brayanname.local, C=XX, ST=XX, L=Fake Locality, O=Super Fake Company, Fake., OU=Fake Organizational Unit Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:e8:08:84:51:84:fb:f0:97:4a:c2:4c:d7:87:0e: d5:10:57:41:06:92:08:8a:cd:9e:8e:a1:59:ef:b6: d0:8b:22:a5:1b:ea:4d:77:7d:24:59:d2:1e:2a:54: 1d:9a:7f:d2:92:a6:0a:4e:33:63:69:fb:f5:1a:3a: 41:0e:11:34:7f:42:62:58:58:ee:a4:f9:88:a1:ed: bf:ba:9e:b3:b9:00:16:77:6c:92:5b:ed:bf:ed:79: 89:7b:cd:cc:26:b5:fb:ce:d2:4f:9a:1b:18:01:2b: 02:65:f7:f6:90:7e:22:19:ff:f7:d4:3c:8c:09:c8: 4f:d1:84:ac:a2:1c:ae:55:1a:87:73:79:63:c2:8d: 6b:57:fa:9f:cc:c1:e4:5a:3c:ef:38:17:76:3c:5f: c3:bc:b9:c5:0f:50:dd:4a:7e:bc:00:bc:ca:64:53: af:b1:8d:d2:ad:2e:b6:47:0a:27:a7:03:7b:bb:c0: f4:74:f9:a5:88:9f:ff:59:aa:e1:02:31:ef:db:04: dc:34:a2:49:d9:8f:c8:87:8c:6a:0a:2c:ee:8f:34: b3:7c:d2:98:b5:9e:26:38:8c:33:7c:ab:3b:1f:e7: 1d:55:0b:be:76:ac:00:9a:c5:01:f1:1b:47:67:90: d4:37:c2:64:24:38:4e:0c:94:14:ce:3d:a1:77:68: 51:d1 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Certificate Sign X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping Netscape Cert Type: SSL Client, SSL Server, S/MIME, Object Signing, SSL CA, S/MIME CA, Object Signing CA X509v3 Subject Key Identifier: 05:CC:36:C8:0C:42:06:58:5D:DF:DC:47:58:3D:B3:A7:C6:30:75:06 X509v3 Subject Alternative Name: DNS:brayanname.local, DNS:*.brayanname.local Signature Algorithm: sha256WithRSAEncryption 5b:d9:bf:b8:53:7d:2c:48:8c:f4:01:3b:6f:2a:e6:06:c0:07: 13:60:82:02:f7:f8:f0:88:4d:c4:20:ca:c3:77:e9:29:21:ea: 5b:87:1e:57:12:54:51:c5:ef:32:77:06:3b:78:f8:bd:d7:a9: b4:e2:5c:34:bd:b1:af:b3:ce:54:b3:c1:7a:7e:d9:b6:d4:0a: 45:ef:00:6b:78:30:ca:89:f8:f7:d5:91:5c:2d:c6:0f:ce:69: f4:3f:98:17:c2:43:8f:9a:5b:e3:dd:46:81:07:0b:5e:a5:40: fb:81:28:7b:90:20:3a:8d:37:01:d1:93:16:9d:fd:e8:e5:fd: 9d:73:b4:5b:73:f7:e6:89:50:ca:74:90:4d:c6:d0:1b:f5:c7: d6:65:12:2c:f2:63:cb:74:34:64:ad:1e:dc:49:9c:c8:74:56: f2:3b:92:df:2f:12:a8:38:5b:22:f0:96:ef:e4:8a:39:b3:64: ed:4f:67:c7:db:38:74:64:58:2c:cc:3b:f9:75:ee:66:81:7c: 44:88:07:a9:cf:4c:1c:03:9c:38:90:5e:31:97:ba:2f:5e:0e: 30:db:8c:16:5b:01:ec:c3:0d:7d:92:88:da:1a:97:fa:59:d9: b5:9e:4e:b7:5e:7c:20:5d:df:77:6c:8c:73:ff:f4:59:6a:e3: a8:d6:be:02 -----BEGIN CERTIFICATE----- MIIEaTCCA1GgAwIBAgIJWcS2HwqMgfZbMA0GCSqGSIb3DQEBCwUAMIGUMRkwFwYD VQQDExBicmF5YW5uYW1lLmxvY2FsMQswCQYDVQQGEwJYWDELMAkGA1UECBMCWFgx FjAUBgNVBAcTDUZha2UgTG9jYWxpdHkxIjAgBgNVBAoTGVN1cGVyIEZha2UgQ29t cGFueSwgRmFrZS4xITAfBgNVBAsTGEZha2UgT3JnYW5pemF0aW9uYWwgVW5pdDAe Fw0yMjAyMTUyMjQxMzdaFw0zMjAyMTUyMjQxMzdaMIGUMRkwFwYDVQQDExBicmF5 YW5uYW1lLmxvY2FsMQswCQYDVQQGEwJYWDELMAkGA1UECBMCWFgxFjAUBgNVBAcT DUZha2UgTG9jYWxpdHkxIjAgBgNVBAoTGVN1cGVyIEZha2UgQ29tcGFueSwgRmFr ZS4xITAfBgNVBAsTGEZha2UgT3JnYW5pemF0aW9uYWwgVW5pdDCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAOgIhFGE+/CXSsJM14cO1RBXQQaSCIrNno6h We+20IsipRvqTXd9JFnSHipUHZp/0pKmCk4zY2n79Ro6QQ4RNH9CYlhY7qT5iKHt v7qes7kAFndsklvtv+15iXvNzCa1+87ST5obGAErAmX39pB+Ihn/99Q8jAnIT9GE rKIcrlUah3N5Y8KNa1f6n8zB5Fo87zgXdjxfw7y5xQ9Q3Up+vAC8ymRTr7GN0q0u tkcKJ6cDe7vA9HT5pYif/1mq4QIx79sE3DSiSdmPyIeMagos7o80s3zSmLWeJjiM M3yrOx/nHVULvnasAJrFAfEbR2eQ1DfCZCQ4TgyUFM49oXdoUdECAwEAAaOBuzCB uDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIC9DA7BgNVHSUENDAyBggrBgEFBQcDAQYI KwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwEQYJYIZIAYb4 QgEBBAQDAgD3MB0GA1UdDgQWBBQFzDbIDEIGWF3f3EdYPbOnxjB1BjAvBgNVHREE KDAmghBicmF5YW5uYW1lLmxvY2FsghIqLmJyYXlhbm5hbWUubG9jYWwwDQYJKoZI hvcNAQELBQADggEBAFvZv7hTfSxIjPQBO28q5gbABxNgggL3+PCITcQgysN36Skh 6luHHlcSVFHF7zJ3Bjt4+L3XqbTiXDS9sa+zzlSzwXp+2bbUCkXvAGt4MMqJ+PfV kVwtxg/OafQ/mBfCQ4+aW+PdRoEHC16lQPuBKHuQIDqNNwHRkxad/ejl/Z1ztFtz 9+aJUMp0kE3G0Bv1x9ZlEizyY8t0NGStHtxJnMh0VvI7kt8vEqg4WyLwlu/kijmz ZO1PZ8fbOHRkWCzMO/l17maBfESIB6nPTBwDnDiQXjGXui9eDjDbjBZbAezDDX2S iNoal/pZ2bWeTrdefCBd33dsjHP/9Flq46jWvgI= -----END CERTIFICATE-----