:::danger
本文大部分内容参考自[1]。
:::
我们的目标是生成一个Root CA,两个Intermediate CA, 分别用于服务证书签发和开发证书签发。
一开始我的根证书是依照参考文献[2]生成的,但是参考文献[2]中生成的根证书是不允许签发intermediate ca的,因此我打算重新做一个根证书。
生成根证书
先创建一个工作目录
mkdir /root/ca
创建一个index.txt文件用来追踪已签名的证书
serial文件用来作为证书的序列号
创建证书后index.txt和serial都会更新
cd /root/ca
mkdir certs crl newcerts private
chmod 700 private
touch index.txt
echo 1000 > serial
准备配置文件
CA证书的创建流程
openssl 文件必须有 ca 块,default_ca 告诉 openssl 使用 CA_default 块中的配置
[ ca ]
# `man ca`
default_ca = CA_default
注意这里dir必须与你的工作目录一致
[ CA_default ]
# Directory and file locations.
dir = /root/ca
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The root key and root certificate.
private_key = $dir/private/ca.key.pem
certificate = $dir/certs/ca.cert.pem
# For certificate revocation lists.
crlnumber = $dir/crlnumber
crl = $dir/crl/ca.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_strict
由于根证书的唯一作用是创建中间ca,因此我们使用policy_strict
[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
由于中间ca需要签server证书或者user证书,而server, user可以来自不同实体,因此中间ca需要更宽松的策略
[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
创建csr(certificate signing request 证书签名请求)时,需要req块
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
req_distinguished_name 块用来存放证书而基本信息
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
# Optionally, specify some defaults.
countryName_default = GB
stateOrProvinceName_default = England
localityName_default =
0.organizationName_default = Alice Ltd
#organizationalUnitName_default =
#emailAddress_default =
v3_ca
块作为根证书配置,v3_intermediate_ca
块作为中间ca证书配置
[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
pathlen:0 的限制使得中间ca只能签终端证书,不能签ca证书。
[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
usr_cert 块作为用户证书配置
[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
server_cert 作为服务器证书配置
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
crl_ext 作为 CRL 证书撤销机制
[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always
ocsp 块作为 OCSP 证书撤销机制配置
[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
生成私钥
genrsa 可以生成私钥,aes256
会用一个密码加密证书,签名时必须要密码才能签名
openssl genrsa -aes256 -out private/ca.key.pem 4096
chmod 400 private/ca.key.pem
生成根证书
根据私钥生成公钥
cd /root/ca
openssl req -config openssl.cnf \
-key private/ca.key.pem \
-new -x509 -days 7300 -sha256 -extensions v3_ca \
-out certs/ca.cert.pem
chmod 444 certs/ca.cert.pem
验证证书
自签名证书的 Subject 和 Issuer 是相同的,因为根证书只能是自签名的。
$ openssl x509 -noout -text -in certs/ca.cert.pem
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=GB, ST=England,
O=Alice Ltd, OU=Alice Ltd Certificate Authority,
CN=Alice Ltd Root CA
Validity
Not Before: Apr 11 12:22:58 2015 GMT
Not After : Apr 6 12:22:58 2035 GMT
Subject: C=GB, ST=England,
O=Alice Ltd, OU=Alice Ltd Certificate Authority,
CN=Alice Ltd Root CA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
这部分可以反映 v3_ca 块配置
X509v3 extensions:
X509v3 Subject Key Identifier:
38:58:29:2F:6B:57:79:4F:39:FD:32:35:60:74:92:60:6E:E8:2A:31
X509v3 Authority Key Identifier:
keyid:38:58:29:2F:6B:57:79:4F:39:FD:32:35:60:74:92:60:6E:E8:2A:31
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
:::info
将根证书设置为系统信任证书时,注意这句话_/usr/local/share/ca-certificates_
Directory of local CA certificates (with .crt extension).
:::
生成中间 ca
先准备一个工作目录
mkdir /root/ca/intermediate
中间CA需要多一个文件夹CSR用来存放证书申请
cd /root/ca/intermediate
mkdir certs crl csr newcerts private
chmod 700 private
touch index.txt
echo 1000 > serial
中间CA的配置与根ca配置有所不同
[ CA_default ]
dir = /root/ca/intermediate
private_key = $dir/private/intermediate.key.pem
certificate = $dir/certs/intermediate.cert.pem
crl = $dir/crl/intermediate.crl.pem
policy = policy_loose
生成私钥
中间CA证书仍然用aes256保护
cd /root/ca
openssl genrsa -aes256 \
-out intermediate/private/intermediate.key.pem 4096
chmod 400 intermediate/private/intermediate.key.pem
生成 CSR 证书签名请求
使用intermediate工作目录中的openssl.conf配置生成 csr
请注意,CommonName必须有别于根ca.
cd /root/ca
openssl req -config intermediate/openssl.cnf -new -sha256 \
-key intermediate/private/intermediate.key.pem \
-out intermediate/csr/intermediate.csr.pem
使用根证书生成证书并签名, extension 选择 v3_intermediate_ca 块
cd /root/ca
openssl ca -config openssl.cnf -extensions v3_intermediate_ca \
-days 3650 -notext -md sha256 \
-in intermediate/csr/intermediate.csr.pem \
-out intermediate/certs/intermediate.cert.pem
chmod 444 intermediate/certs/intermediate.cert.pem
签名完成后,intermediate的index.txt就多了一条记录
检查证书
查看证书详情
openssl x509 -noout -text \
-in intermediate/certs/intermediate.cert.pem
用根证书验证中间ca
openssl verify -CAfile certs/ca.cert.pem \
intermediate/certs/intermediate.cert.pem
:::info 一般来说用户的浏览器只有根证书,因此服务器证书若是由中间证书签发,必须提供完整的证书链使得浏览器可以查找到证书的issuer。 :::
生成服务器证书
服务端证书的生成和签名和中间CA基本没有区别,但是注意,server证书需要设置 SAN(subjectAltName)。
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alternate_names
[ alternate_names ]
DNS.1 = clash.dev
DNS.2 = *.clash.dev
另一个问题是证书链,私钥不需要拼接,但是公钥(.cert.pem)需要拼接为一个文件。
cat server.cert.pem intermediate.cert.pem ca.cert.pem > chain.cert.pem
其中server证书必须位于证书文件开头,而不能是中间。 :::info 一般来说用户的浏览器只有根证书,因此服务器证书若是由中间证书签发,必须提供完整的证书链使得浏览器可以查找到证书的issuer。 ::: 完整过程为
# 生成不需要密码的私钥
cd /root/ca
openssl genrsa \
-out intermediate/private/www.example.com.key.pem 2048
chmod 400 intermediate/private/www.example.com.key.pem
# 生成 csr
cd /root/ca
openssl req -config intermediate/openssl.cnf \
-key intermediate/private/www.example.com.key.pem \
-new -sha256 -out intermediate/csr/www.example.com.csr.pem
# 生成证书并签名,extensions 选择 server_cert
cd /root/ca
openssl ca -config intermediate/openssl.cnf \
-extensions server_cert -days 375 -notext -md sha256 \
-in intermediate/csr/www.example.com.csr.pem \
-out intermediate/certs/www.example.com.cert.pem
chmod 444 intermediate/certs/www.example.com.cert.pem
# 查看证书详情
openssl x509 -noout -text \
-in intermediate/certs/www.example.com.cert.pem
# 对照 ca 验证证书
openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \
intermediate/certs/www.example.com.cert.pem
部署
使用 Caddy 就可以部署啦
:8081 {
file_server
tls chain.cert.pem server.key.pem
}
附录 A 配置文件示范
根证书配置
# OpenSSL root CA configuration file.
# Copy to `/root/ca/openssl.cnf`.
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = /root/ca
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The root key and root certificate.
private_key = $dir/private/ca.key.pem
certificate = $dir/certs/ca.cert.pem
# For certificate revocation lists.
crlnumber = $dir/crlnumber
crl = $dir/crl/ca.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_strict
[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
# Optionally, specify some defaults.
countryName_default = GB
stateOrProvinceName_default = England
localityName_default =
0.organizationName_default = Alice Ltd
organizationalUnitName_default =
emailAddress_default =
[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
中间CA配置
# OpenSSL intermediate CA configuration file.
# Copy to `/root/ca/intermediate/openssl.cnf`.
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = /root/ca/intermediate
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The root key and root certificate.
private_key = $dir/private/intermediate.key.pem
certificate = $dir/certs/intermediate.cert.pem
# For certificate revocation lists.
crlnumber = $dir/crlnumber
crl = $dir/crl/intermediate.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_loose
[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
# Optionally, specify some defaults.
countryName_default = GB
stateOrProvinceName_default = England
localityName_default =
0.organizationName_default = Alice Ltd
organizationalUnitName_default =
emailAddress_default =
[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alternate_names
[ alternate_names ]
DNS.1 = hello.world
DNS.2 = *.hello.world
[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
配置B 配置文件语法
openssl 允许你将 basicConstraints=critical,CA:true,pathlen:1
用如下方式重写
basicConstraints=critical,@bs_section
[bs_section]
CA=true
pathlen=1
openssl 允许变量语法,如
dir = /root/ca/intermediate
certs = $dir/certs
crl_dir = $dir/crl
参考文献
[1]: https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
[2]: https://github.com/laravel/homestead/blob/main/scripts/create-certificate.sh