我创建了一个自签名的根 CA 证书,然后创建了一个使用根 CA 签名的 IA 证书,最后创建了一个使用 IA 证书签名的服务器证书。
这些是我使用的命令:
@ECHO OFF
ECHO Create Root CA Private key...
openssl ecparam -name secp384r1 -genkey -out "SSL-Bundle/Root-CA.key"
ECHO Create Root CA Private key successfully!
ECHO Create Root CA CSR...
openssl req -new -sha256 -key "SSL-Bundle/Root-CA.key" -config "SSL-Bundle/config_root_ca.cnf" -out "SSL-Bundle/Root-CA.csr"
ECHO Create Root CA CSR successfully!
ECHO Create Root CA...
REM Install Root CA into Trusted Root Certification Authorities store and Third-Party Root Certification Authorities store
certutil -f -addstore AuthRoot "SSL-Bundle/Root-CA.crt"
ECHO Create Root CA successfully!
ECHO Create Intermediate CA Private key...
openssl ecparam -name secp384r1 -genkey -out "SSL-Bundle/Intermediate-CA.key"
ECHO Create Intermediate CA Private key successfully!
ECHO Create Intermediate CA CSR...
openssl req -new -sha256 -key "SSL-Bundle/Intermediate-CA.key" -config "SSL-Bundle/config_intermediate_ca.cnf" -out "SSL-Bundle/Intermediate-CA.csr"
ECHO Create Intermediate CA CSR successfully!
ECHO Sign Intermediate CA CSR with Root CA...
openssl ca -config "SSL-Bundle/config_intermediate_ca.cnf" -in "SSL-Bundle/Intermediate-CA.csr" -out "SSL-Bundle/Intermediate-CA.crt"
REM Install Intermediate CA into Intermediate Certification Authorities store
certutil -f -addstore CA "SSL-Bundle/Intermediate-CA.crt"
ECHO Sign Intermediate CA CSR with Root CA successfully!
ECHO Create Server Private key...
openssl ecparam -name prime256v1 -genkey -out "SSL-Bundle/Certificates/server.key"
ECHO Create Server Private key successfully!
ECHO Create Server CSR...
openssl req -new -sha256 -key "SSL-Bundle/Certificates/server.key" -config "SSL-Bundle/config_server_ssl.cnf" -out "SSL-Bundle/Certificates/server.csr"
ECHO Create Server CSR successfully!
ECHO Sign Server CSR with Intermediate CA...
openssl ca -config "SSL-Bundle/config_server_ssl.cnf" -in "SSL-Bundle/Certificates/server.csr" -out "SSL-Bundle/Certificates/server.crt"
ECHO Sign Server CSR with Intermediate CA successfully!
这些是我使用的配置文件:
config_root_ca.cnf:
[ req ]
prompt=no
string_mask=default
distinguished_name=req_distinguished_name
default_bits=2048
[ req_distinguished_name ]
countryName=VN
# stateOrProvinceName=
# localityName=
organizationName=Localhost Inc.
organizationalUnitName=Localhost Co., Ltd.
commonName=Root CA ECC
# emailAddress=
# we use 'ca' as the default section because we're usign the ca command
[ ca ]
default_ca=root_ca
[ root_ca ]
# a text file containing the next serial number to use in hex. Mandatory.
# This file must be present and contain a valid serial number.
serial=SSL-Bundle/Root-CA.srl
# the text database file to use. Mandatory. This file must be present though
# initially it will be empty.
database=SSL-Bundle/Root-CA.txt
# specifies the directory where new certificates will be placed. Mandatory.
new_certs_dir=SSL-Bundle/Certificates
# the file containing the CA certificate. Mandatory
certificate=SSL-Bundle/Root-CA.crt
# the file contaning the CA private key. Mandatory
private_key=SSL-Bundle/Root-CA.key
# the message digest algorithm. Remember to not use MD5
default_md=sha384
# for how many days will the signed certificate be valid
default_days=397
# the start date to certify a certificate for
default_startdate=19700101000000Z
# either this option or default_days
default_enddate=21060207062815Z
# a section with a set of variables corresponding to DN fields
policy=root_ca_policy
# Extensions added while singing with the `openssl ca` command
x509_extensions=x509_ext
# MOST IMPORTANT PART OF THIS CONFIG
copy_extensions=copy
[ root_ca_policy ]
# if the value is "match" then the field value must match the same field in the
# CA certificate. If the value is "supplied" then it must be present.
# Optional means it may be present. Any fields not mentioned are silently
# deleted.
countryName=match
stateOrProvinceName=optional
localityName=optional
organizationName=supplied
organizationalUnitName=supplied
commonName=supplied
emailAddress=optional
[ x509_ext ]
basicConstraints=critical,CA:TRUE
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
keyUsage=critical,digitalSignature,cRLSign,keyCertSign
config_intermediate_ca.cnf:
[ req ]
prompt=no
string_mask=default
distinguished_name=req_distinguished_name
default_bits=2048
[ req_distinguished_name ]
countryName=VN
# stateOrProvinceName=
# localityName=
organizationName=Localhost Inc.
organizationalUnitName=Localhost Pte. Ltd.
commonName=Intermediate CA ECC
# emailAddress=
# we use 'ca' as the default section because we're usign the ca command
[ ca ]
default_ca=intermediate_ca
[ intermediate_ca ]
# a text file containing the next serial number to use in hex. Mandatory.
# This file must be present and contain a valid serial number.
serial=SSL-Bundle/Root-CA.srl
# the text database file to use. Mandatory. This file must be present though
# initially it will be empty.
database=SSL-Bundle/Root-CA.txt
# specifies the directory where new certificates will be placed. Mandatory.
new_certs_dir=SSL-Bundle/Certificates
# the file containing the CA certificate. Mandatory
certificate=SSL-Bundle/Root-CA.crt
# the file contaning the CA private key. Mandatory
private_key=SSL-Bundle/Root-CA.key
# the message digest algorithm. Remember to not use MD5
default_md=sha384
# for how many days will the signed certificate be valid
default_days=397
# the start date to certify a certificate for
default_startdate=19700101000000Z
# either this option or default_days
default_enddate=20791231235959Z
# a section with a set of variables corresponding to DN fields
policy=intermediate_ca_policy
# Extensions added while singing with the `openssl ca` command
x509_extensions=x509_ext
# MOST IMPORTANT PART OF THIS CONFIG
copy_extensions=copy
[ intermediate_ca_policy ]
# if the value is "match" then the field value must match the same field in the
# CA certificate. If the value is "supplied" then it must be present.
# Optional means it may be present. Any fields not mentioned are silently
# deleted.
countryName=match
stateOrProvinceName=optional
localityName=optional
organizationName=supplied
organizationalUnitName=supplied
commonName=supplied
emailAddress=optional
[ x509_ext ]
basicConstraints=critical,CA:TRUE,pathlen:0
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
keyUsage=critical,digitalSignature,keyCertSign,cRLSign
extendedKeyUsage=serverAuth,clientAuth
config_server_ssl.cnf:
[ req ]
prompt=no
string_mask=default
distinguished_name=req_distinguished_name
default_bits=2048
[ req_distinguished_name ]
countryName=VN
# stateOrProvinceName=
# localityName=
organizationName=Localhost Inc.
organizationalUnitName=Localhost LLC
commonName=Localhost ECC
# emailAddress=
# we use 'ca' as the default section because we're usign the ca command
[ ca ]
default_ca=server_ca
[ server_ca ]
# a text file containing the next serial number to use in hex. Mandatory.
# This file must be present and contain a valid serial number.
serial=SSL-Bundle/Intermediate-CA.srl
# the text database file to use. Mandatory. This file must be present though
# initially it will be empty.
database=SSL-Bundle/Intermediate-CA.txt
# specifies the directory where new certificates will be placed. Mandatory.
new_certs_dir=SSL-Bundle/Certificates
# the file containing the CA certificate. Mandatory
certificate=SSL-Bundle/Intermediate-CA.crt
# the file contaning the CA private key. Mandatory
private_key=SSL-Bundle/Intermediate-CA.key
# the message digest algorithm. Remember to not use MD5
default_md=sha256
# for how many days will the signed certificate be valid
default_days=397
# the start date to certify a certificate for
default_startdate=19700101000000Z
# either this option or default_days
default_enddate=20380319031407Z
# a section with a set of variables corresponding to DN fields
policy=ia_ca_policy
# Extensions added while singing with the `openssl ca` command
x509_extensions=x509_ext
# MOST IMPORTANT PART OF THIS CONFIG
copy_extensions=copy
[ ia_ca_policy ]
# if the value is "match" then the field value must match the same field in the
# CA certificate. If the value is "supplied" then it must be present.
# Optional means it may be present. Any fields not mentioned are silently
# deleted.
countryName=match
stateOrProvinceName=optional
localityName=optional
organizationName=supplied
organizationalUnitName=supplied
commonName=supplied
emailAddress=optional
[ x509_ext ]
basicConstraints=critical,CA:false
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
keyUsage=critical,digitalSignature
extendedKeyUsage=serverAuth
subjectAltName=@alternate_names
[ alternate_names ]
DNS.1=localhost
DNS.2=*.localhost
IP.1=127.0.0.1
根 CA 证书已安装到受信任的根证书颁发机构存储和第三方根证书颁发机构存储中。IA 证书已安装到中级证书颁发机构存储中。我检查了一下certmgr.msc
,一切都安装正确,当我打开这些证书时,根CA、IA和服务器都返回了正确的证书路径,并且证书状态为This certificate is OK
。
当我打开:Chrome 设置 > 隐私和安全 > 安全 > 管理证书时。在“中级证书颁发机构”选项卡上有一个自签名 IA 证书,在“受信任的根证书颁发机构”选项卡上还有一个自签名根 CA 证书。
然后我配置了 Apache:
Listen 443
<VirtualHost _default_:80>
DocumentRoot "/WWW"
<Directory "/WWW">
Options -Indexes +FollowSymLinks +ExecCGI
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:443>
DocumentRoot "/WWW"
ServerName localhost
ServerAlias www.localhost
<Directory "/WWW">
Options FollowSymLinks ExecCGI
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /Apache/conf/ssl/server.crt
SSLCertificateKeyFile /Apache/conf/ssl/server.key
SSLCertificateChainFile /Apache/conf/ssl/Root-CA.crt
</VirtualHost>
但是当我尝试在 Google Chrome 上访问 https://localhost 时,我仍然收到错误NET::ERR_CERT_AUTHORITY_INVALID
,并且在地址栏上出现红色警告Not Secure。
我想知道我错过了什么?如何让Google Chrome也信任我的自签名证书(即不再有警告并且地址栏也将变为绿色安全)?
openssl x509 -noout -text -in <cert.crt>
更新:以下是@garethTheRed 请求的输出。
openssl x509 -noout -text -in Root-CA.crt
:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: ecdsa-with-SHA384
Issuer: C = VN, O = Localhost Inc., OU = "Localhost Co., Ltd.", CN = Localhost ECC P384 Root CA
Validity
Not Before: Jan 1 00:00:00 1970 GMT
Not After : Feb 7 06:28:15 2106 GMT
Subject: C = VN, O = Localhost Inc., OU = "Localhost Co., Ltd.", CN = Localhost ECC P384 Root CA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (384 bit)
pub:
04:9f:a9:ed:b2:46:dd:2d:78:42:5f:cc:41:8b:6a:
26:82:f9:8d:5f:52:a5:ea:7c:1f:e7:73:a2:43:0c:
d2:69:a9:ba:b1:06:53:f8:2f:6f:94:95:59:09:4b:
6e:fd:51:72:54:7e:11:7a:88:cf:4b:49:99:d9:f5:
92:c1:19:b0:10:6f:c7:f5:e0:36:c9:5d:32:01:18:
d1:52:26:fb:57:d8:3c:4e:15:ba:b0:7c:80:00:bb:
0e:d9:bc:26:1b:1b:d5
ASN1 OID: secp384r1
NIST CURVE: P-384
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Key Identifier:
74:49:7D:71:7E:A0:31:28:6E:EC:46:BA:D4:99:72:41:78:B5:11:6A
X509v3 Authority Key Identifier:
keyid:74:49:7D:71:7E:A0:31:28:6E:EC:46:BA:D4:99:72:41:78:B5:11:6A
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
Signature Algorithm: ecdsa-with-SHA384
30:66:02:31:00:fa:2d:4a:35:4b:21:83:14:dc:13:8b:5a:e5:
a8:da:d2:1a:86:fc:59:47:07:da:c4:2a:fd:ff:07:cc:96:4e:
f2:d2:0d:da:80:d8:e7:e7:c6:cb:0c:b6:87:c6:f5:20:95:02:
31:00:e2:32:d8:09:af:ef:a8:1e:03:4c:d2:e9:6f:92:bf:82:
c7:52:8e:43:33:da:38:c9:a4:42:37:b9:cc:db:48:d9:ae:3e:
55:43:4d:df:d5:35:f3:b0:0e:42:87:9f:b0:1a
openssl x509 -noout -text -in Intermediate-CA.crt
:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: ecdsa-with-SHA384
Issuer: C = VN, O = Localhost Inc., OU = "Localhost Co., Ltd.", CN = Localhost ECC P384 Root CA
Validity
Not Before: Jan 1 00:00:00 1970 GMT
Not After : Dec 31 23:59:59 2079 GMT
Subject: C = VN, O = Localhost Inc., OU = Localhost Pte. Ltd., CN = Localhost ECC P384 Intermediate CA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (384 bit)
pub:
04:07:c4:b3:2c:2f:33:cb:76:d2:19:f1:21:04:02:
5d:92:14:06:dc:87:c6:3d:af:c8:27:18:8d:9e:10:
e5:11:43:47:28:24:e5:be:b6:cf:12:8e:80:59:c8:
d7:82:c9:88:9b:bb:06:fa:0b:46:44:a2:39:6c:23:
5c:b0:a8:80:91:d9:6d:9a:4c:ae:e8:9d:69:73:b6:
60:81:e0:e9:69:e2:8a:44:41:dc:a3:f3:e5:ae:7e:
02:00:f4:41:74:22:7c
ASN1 OID: secp384r1
NIST CURVE: P-384
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Subject Key Identifier:
F3:38:E4:E6:B9:1E:EC:12:E5:80:96:00:94:A9:76:06:D1:C9:B4:89
X509v3 Authority Key Identifier:
keyid:74:49:7D:71:7E:A0:31:28:6E:EC:46:BA:D4:99:72:41:78:B5:11:6A
DirName:/C=VN/O=Localhost Inc./OU=Localhost Co., Ltd./CN=Localhost ECC P384 Root CA
serial:00
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
Signature Algorithm: ecdsa-with-SHA384
30:65:02:30:68:3f:e8:2f:45:3f:fb:f9:65:62:fc:f5:3a:d0:
b3:3e:21:9a:76:c3:b3:cc:8c:2c:77:69:48:ed:2e:85:0d:5f:
35:8d:9b:ca:de:bb:32:d5:8b:c3:39:53:2a:c8:61:82:02:31:
00:f2:cb:ed:ba:ba:68:19:73:f0:53:7c:10:63:3f:b8:20:4f:
f2:19:b9:86:7b:bd:c9:b3:7d:2e:c5:34:62:e2:1f:4f:b0:24:
9c:b3:ae:fd:f5:54:3a:08:e3:dd:f9:50:fe
openssl x509 -noout -text -in Server.crt
:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = VN, O = Localhost Inc., OU = Localhost Pte. Ltd., CN = Localhost ECC P384 Intermediate CA
Validity
Not Before: Jan 1 00:00:00 1970 GMT
Not After : Mar 19 03:14:07 2038 GMT
Subject: C = VN, O = Localhost Inc., OU = Localhost LLC, CN = Localhost ECC P256
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:b6:8f:91:37:73:9d:a7:31:ee:6e:d6:74:e2:15:
d4:af:78:cc:d5:21:1f:0a:52:be:66:af:36:01:c4:
d3:b1:7e:c7:99:e9:cd:0d:41:9b:e6:28:cf:26:8c:
f9:44:2f:c9:ee:6d:e5:f5:d3:00:82:7b:19:95:3e:
89:5b:ef:a2:ac
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
32:19:D3:18:FA:46:0C:A6:2B:1F:CF:A1:04:8D:99:6F:C5:03:54:AD
X509v3 Authority Key Identifier:
keyid:F3:38:E4:E6:B9:1E:EC:12:E5:80:96:00:94:A9:76:06:D1:C9:B4:89
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:localhost, DNS:*.localhost, IP Address:127.0.0.1
Signature Algorithm: ecdsa-with-SHA256
30:64:02:30:4d:43:cc:3d:4e:7a:8d:78:ec:bb:01:64:60:93:
26:96:7c:07:55:9c:66:ae:4f:13:c9:1f:d3:01:ff:ae:10:da:
a1:ca:5b:d7:9d:f5:8f:81:89:0d:56:32:7b:53:58:ad:02:30:
78:7e:06:56:f6:fc:93:67:ba:53:f4:cb:22:1d:15:86:62:63:
9a:2e:fa:14:bd:8b:2c:01:a3:6b:8c:20:94:61:a9:e0:cc:a4:
8f:c9:a8:a5:88:59:72:b1:35:5d:e2:e6
Okay, I've identified the problem and found a solution. The command line and configuration file I used generated the certificates in question without any problem. The error was determined to be due to a feature called Chrome Root Store and Certificate Verifier. This feature was added since Chrome 105:
Although also according to this article, a certificate added to the Trusted Root Certification Authorities store will also be trusted by Chrome Certificate Verifier, but since it is not in the Chrome Root Store it will give the same warning as the certificate not trusted.
I tried on older versions (Chrome 49 - Chrome Root Store and Certificate Verifier feature is not available, uses platform root store and verifier) these certificates work fine:
And according to the article Testing the Chrome Root Store and Certificate Verifier, the only workaround is to disable Chrome Root Store and Certificate Verifier. Here is how to do it (on Chrome >= 105):
Disable the Chrome Root Store & Certificate Verifier by starting Chrome with the following flag:
--disable-features=ChromeRootStoreUsed
Disable the Chrome Root Store and Certificate Verifier by going to chrome://flags, searching for the flag
Chrome Root Store
(chrome://flags/#chrome-root-store-enabled
), and set it toDisabled
. This will then require Chrome to restart.And here is the result (I tested on Chrome 109):
Note: The Root Store and Certificate Verifier feature is available on all browsers using Chromium, including Microsoft Edge. This feature is called
Microsoft Root Store
on Microsoft Edge.