I've created a private root CA and issued some SSL certificate for some https websites,
(I am not sure whether this should be called as self-signed certificate, in my case, the root ca certificate is self signed, but the SSL certificate for websites issued from the root CA is not self-signed. This makes me confused about how to choose google search keyword).
These https website works well in Chrome, Firefox, but not Safari. (Of course, Chrome and Firefox will show security warning but give me opportunity to continue viewing, but Safari does not completely).
The point is: my website provided a certificate chain file which is
The website certificate... + The root CA certificate...
, Chrome and Firefox can smartly find root CA from the certificate chain file, but Safari does not.
Safari reports error:
Safari Can't Open the Page – Safari can't open the page because Safari can't establish a secure connection to the server 'servername'.
Of course, I can in advance import the Root CA certificate to Mac's keychain, then Safari works without even warnings. But this is not what I want, I can not demand users to do such manual things.
If I generate a real self signed SSL certificate (without upper level CA), Safari will give me an opportunity to view the website. But this is also not what I want, because I have some Java application which call multiple https websites and should not ignore certificate check, I want to register just one common root CA certificate to Java cacerts keystore so that all relative https websites will be trusted automatically.
Can anyone tell me how to solve this issue? I mean without manually import Root CA in advance, yet let user have the opportunity to view the website anyway. Note, Chrome and Firefox works well.
I am going to specify some certificate extension to tell the browser where to find the root CA certificate, not sure if there is any other better way.
I don't mean "just send a new CA certificate (in a chain or otherwise) and your browser "smartly" accept it", I don mean let browser "accept it", but just let it pass the chain validation and ask user if they accept the risk of unknown root CA.
About "Chain files are not CAs": I know it much, chain file does not even include the root CA certificate, browser have some means to find root CA certificate, such as from preinstalled root CAs, or probably by a "Authority Info Access" field in the certificate, there is a URL point to root CA. This is a not-well-known area, also depends on browser implementation: How does browser get root CA(I don't mean trust or accept) from the website's certificate itself.
I have followed Apple's guideline https://support.apple.com/en-us/HT210176
EDIT 2021/02/12: things get more and more interesting for Safari:
- https://ip_address works. (of course with security warning)
- https://dns_name does not works (not even a security warning)
- https://dns_name works in private browsing mode (of course with security warning).
Let me append some tech detail, here is the certificate chain returned by the website: (Note that I replaced some sensitive names with word such as my-host...)
$ echo Q | openssl s_client -showcerts -verify 5 -connect my-host.my-domain:443 verify depth is 5 CONNECTED(00000005) depth=1 CN = My Root CA Name verify error:num=19:self signed certificate in certificate chain verify return:1 depth=1 CN = My Root CA Name verify return:1 depth=0 CN = my-host.my-domain verify return:1 --- Certificate chain 0 s:CN = my-host.my-domain i:CN = My Root CA Name -----BEGIN CERTIFICATE----- MIIDqjCCApKgAwIBAgIJAIh7Dn2n363zMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNV BAMMD1RyaWRlbnQgUm9vdCBDQTAeFw0yMTAyMDkwNDU4MzZaFw0yMjAzMTMwNDU4 ...... +bnUvHgMmMukXgMLo3e6tnF/Za9z/BCv0KESoFEIg7uWo+IUZv8wXYI8YQEaeeGt s2et3Js1eBqN1zle8ejoFuInQNS5wkalx0D+zTcCcXVvnUJm2womcKBxFAHJeZDg vvbsua6FH0JTVjeprdmx1mKkj+MP4N664vP8kAj6 -----END CERTIFICATE----- 1 s:CN = My Root CA Name i:CN = My Root CA Name -----BEGIN CERTIFICATE----- MIIDFzCCAf+gAwIBAgIJAIafS0ZYzGA4MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNV BAMMD1RyaWRlbnQgUm9vdCBDQTAeFw0yMTAyMDkwNDU0MTdaFw0zMTAyMDcwNDU0 ...... +b4lJl9fUHd01xqGbBGmp/BL8EI+IveSRTKr/Boi+klqvlgOi5TgUF/0R9gmwPRO OXIL5hdA7CTsgaURWJ897p5JVYsPtofjnL10 -----END CERTIFICATE----- --- Server certificate subject=CN = my-host.my-domain issuer=CN = My Root CA Name --- No client certificate CA names sent Peer signing digest: SHA256 Peer signature type: RSA Server Temp Key: X25519, 253 bits --- SSL handshake has read 2449 bytes and written 432 bytes Verification error: self signed certificate in certificate chain --- New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 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-AES256-GCM-SHA384 Session-ID: F6FF34026A5B10EE149D442FA4035916330AE09AC2B771369EA0B94754501892 Session-ID-ctx: Master-Key: BF444224C15CEDC3309F01E4C5DE8F8331AF9B82F0613601FE5D1B677F6526DD3FF97BB00DB3DC5BB0E35EA489861FF6 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - d6 5f 48 e0 90 10 48 22-46 83 7f 82 d1 a4 17 da ._H...H"F....... 0010 - 17 10 e8 1e e1 dc 17 58-9c 36 e8 d1 36 f5 d7 f6 .......X.6..6... ...... 00d0 - 85 63 2a 3c 0e 6c 18 f3-27 fb 21 7d bd 3d 8b 33 .c*<.l..'.!}.=.3 00e0 - bf 3c 60 da 06 2e 23 3b-a0 f2 f6 88 5e 0c 2b f2 .<`...#;....^.+. Start Time: 1612937133 Timeout : 7200 (sec) Verify return code: 19 (self signed certificate in certificate chain) Extended master secret: yes --- DONE
The apache2(2.4.29 Ubuntu) config:
<IfModule mod_ssl.c> <VirtualHost *:443> SSLEngine On SSLCertificateFile /etc/apache2/certs/web_cert.pem SSLCertificateKeyFile /etc/apache2/certs/web_key.pem DocumentRoot /var/www LimitRequestFieldSize 32768 </VirtualHost> </IfModule>
Note that the /etc/apache2/certs/web_cert.pem is actually a concatenation of the website's certificate and the root CA certificate, i.e., a certificate chain file.
The root CA certificate:
# openssl x509 -text -noout < /etc/apache2/certs/root_ca_cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: 86:9f:4b:46:58:cc:60:38 Signature Algorithm: sha256WithRSAEncryption Issuer: CN = My Root CA Name Validity Not Before: Feb 9 04:54:17 2021 GMT Not After : Feb 7 04:54:17 2031 GMT Subject: CN = My Root CA Name Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: ...... Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: critical CA:TRUE X509v3 Subject Key Identifier: EE:BF:15:0D:35:BC:4C:92:88:4F:9B:21:FC:B6:C4:29:C4:35:9E:D3 X509v3 Authority Key Identifier: keyid:EE:BF:15:0D:35:BC:4C:92:88:4F:9B:21:FC:B6:C4:29:C4:35:9E:D3 X509v3 Key Usage: Certificate Sign, CRL Sign Signature Algorithm: sha256WithRSAEncryption ......
The root CA key
# openssl rsa -text -noout < /etc/apache2/certs/root_ca_key.pem Private-Key: (2048 bit) modulus: ...... publicExponent: 65537 (0x10001) privateExponent: ...... prime1: ...... prime2: ...... exponent1: ...... exponent2: ...... coefficient: ......
The website certificate:
# openssl x509 -text -noout < /etc/apache2/certs/web_cert.pem Certificate: Data: Version: 3 (0x2) Serial Number: 88:7b:0e:7d:a7:df:ad:f3 Signature Algorithm: sha256WithRSAEncryption Issuer: CN = My Root CA Name Validity Not Before: Feb 9 04:58:36 2021 GMT Not After : Mar 13 04:58:36 2022 GMT Subject: CN = my-host.my-domain Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: ...... Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: E2:83:31:54:D3:49:D1:5E:78:6C:29:84:15:C9:80:48:26:AF:7A:EF X509v3 Authority Key Identifier: keyid:EE:BF:15:0D:35:BC:4C:92:88:4F:9B:21:FC:B6:C4:29:C4:35:9E:D3 DirName:/CN=My Root CA Name serial:86:9F:4B:46:58:CC:60:38 X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: critical TLS Web Server Authentication X509v3 Subject Alternative Name: DNS:my-host.my-domain, IP Address:100.100.100.100 Signature Algorithm: sha256WithRSAEncryption ......
The website's certificate key:
# openssl rsa -text -noout < /etc/apache2/certs/web_key.pem Private-Key: (2048 bit) modulus: ...... publicExponent: 65537 (0x10001) privateExponent: ...... prime1: ...... prime2: ...... exponent1: ...... exponent2: ...... coefficient: ......
Keychain verification result:
s# openssl verify -CAfile /etc/apache2/certs/root_ca_cert.pem < /etc/apache2/certs/web_cert.pem stdin: OK