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.
Exclamations:
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.
Other info:
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