SSL 证书入门:概念、签发与本地 HTTPS
SSL(现代行业标准通常称为 TLS)是一套安全协议,SSL 证书就是基于该协议的数字证书,核心作用:加密互联网传输流量、校验服务器真实身份。
SSL 全称为安全套接字层,能够在 Web 浏览器与 Web 服务器之间建立加密通信链路。部署 SSL 证书之后网站就可以使用 HTTPS 协议。SSL 证书是存放在网站源服务器上的数据文件。
什么是 HTTPS?
超文本传输协议安全(HTTPS)是 HTTP 的安全版本,HTTP 是浏览器与网站之间传输数据的基础协议,HTTPS 在其基础上增加加密能力保障传输安全。
在传输登录账号、密码、个人隐私、医疗保险等敏感业务数据场景下,HTTPS 的加密能力尤为重要。 现代 Web 最佳实践建议所有网站都启用 HTTPS,需要登录鉴权的网站更是必须使用。
现代浏览器会对 HTTP、HTTPS 做差异化标记:连接安全时,地址栏会给出安全连接提示(旧版常见为挂锁图标)。Chrome 等主流浏览器会将未启用 HTTPS 的网站标记为不安全。
SSL 证书是如何工作的?
一份 SSL 证书数据文件内包含以下信息:
- 针对其颁发证书的域名
- 证书颁发给哪一个人、组织或设备
- 证书由哪一证书颁发机构颁发
- 证书颁发机构的数字签名
- 关联的子域
- 证书的颁发日期
- 证书的到期日期
- 公钥(私钥为保密状态)
密码学说明:SSL 使用非对称加密机制,使用公钥加密的数据只能使用配套私钥解密。公钥放置在证书内对外公开,私钥必须严格保密,不能对外泄露。
证书部署在网站源服务器,浏览器访问网站的时候服务器会将证书下发给访问设备。
大部分浏览器支持查看证书详情:Chrome 浏览器点击地址栏左侧挂锁图标,即可查看完整证书信息。
如何获取 SSL 证书?
想要证书被浏览器、操作系统信任,域名证书必须由 CA(证书颁发机构)签发。 CA 是受广泛信任的第三方机构,负责生成、颁发 SSL 证书;CA 使用自身私钥对证书进行数字签名,客户端设备通过校验签名确认证书合法性。部分 CA 机构证书服务收费,也存在免费 CA 机构(例如 Let’s Encrypt)。
证书签发完成后,需要上传并且在源站 Web 服务器完成安装激活。很多云托管平台可以代为完成证书部署。服务器证书激活成功后,网站就可以通过 HTTPS 访问,网站往返流量全部被加密保护。
什么是自签名 SSL 证书?
任何人都可以生成公私钥对,自行组装全部证书信息,制作证书,该类证书称为自签名证书。
自签名证书使用网站自身私钥完成签名,没有外部 CA 权威机构校验服务器身份。
重要特性:浏览器默认不信任自签名证书;即使地址栏协议是 https,浏览器依旧标记站点不安全,极端场景浏览器直接断开网络连接,网站无法加载。
备注:为了解决浏览器信任问题,可将根证书添加到系统或浏览器的受信任根证书颁发机构。
使用范围限制:仅用于内网、本地开发测试,禁止公网生产环境直接使用自签名证书。
不同类型证书的区别
区分证书不是依靠文件后缀,依靠文件实际内容与编码格式;同一份证书可以导出为多种后缀格式。
- .pem、.key:
.pem证书文件一般是文本格式,可以存放证书或私钥,或者两者都包含。.pem文件如果只包含私钥,一般用.key文件代替。 - .csr: 数字证书签名请求文件(Certificate Signing Request),将其提交给证书颁发机构(CA)对证书签名。
- .der、.cer: 证书的公钥格式(证书颁发机构签名后的证书或者自签名证书,包含证书持有人的信息、持有人的公钥以及签署者的签名等信息)。文件是二进制格式,只含有证书信息,不包含私钥。
- .crt: 证书的公钥格式。证书文件是二进制格式或文本格式,一般为文本格式,功能与
.der及.cer证书文件相同(cer 是 crt 证书的微软形式)。 - .jks: keytool 证书工具支持的证书私钥格式。
- .pfx、.p12:微软支持的私钥格式(
.p12是.pfx的新格式)。证书文件是二进制格式,同时包含证书和私钥,且一般有密码保护; - .keystore、.truststore: 两者本质都是 keystore,都是储存密钥的容器。keystore 用来存储自己的公钥和私钥,而 truststore 用来存储自己信任对象的公钥。
.truststore是必须的,如果没有显式的指定,那么 Java 会默认指定为$JAVA_HOME/lib/security/cacerts文件。
不同语言需要的证书格式并不一致,比如:Java 采用 jks、.NET 采用 pfx 和 cer、PHP 则采用 pem 和 cer。
OpenSSL
openssl 是一个功能丰富且自包含的开源安全工具箱。
它提供的主要功能有:SSL、TLS 协议实现(早期版本还包括已废弃的 SSLv2、SSLv3)、大量软算法(对称、非对称、摘要)、大数运算、非对称算法密钥生成、ASN.1 编解码库、证书请求(PKCS10)编解码、数字证书编解码、CRL 编解码、OCSP 协议、数字证书验证、PKCS7 标准实现和 PKCS12 个人数字证书格式实现等功能。
openssl 采用 C 语言作为开发语言,这使得它具有优秀的跨平台性能。openssl 支持 Linux、UNIX、Windows、macOS 等平台。
版本提示:OpenSSL 3.x 仍可生成 PKCS#12(.p12)。若旧系统(部分 Java、Windows)无法导入,生成时需加 -legacy,以兼容较旧的加密算法。
生成根证书
根证书(root.key)
openssl genrsa -out root.key 2048
- genrsa:产生 rsa 密钥命令。
- -out: 输出路径。
- 2048:密钥的长度位数(本命令已显式指定;旧版
genrsa未指定时默认曾为 512 位)。
可查看详细解析,包含两个大素数和两个指数和一个系数:
openssl rsa -in root.key -text
Private-Key: (2048 bit, 2 primes)
modulus:
publicExponent: 65537 (0x10001)
privateExponent:
prime1:
prime2:
exponent1:
exponent2:
coefficient:
writing RSA key
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
也可通过命令提取公钥:
openssl rsa -pubout -in root.key
writing RSA key
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
根证书请求文件(root.csr)
openssl req -new -out root.csr -key root.key -keyform PEM
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:zj
Locality Name (eg, city) []:hz
Organization Name (eg, company) [Internet Widgits Pty Ltd]:lizhao
Organizational Unit Name (eg, section) []:lizhao
Common Name (e.g. server FQDN or YOUR name) []:app.xxx.com
Email Address []:1927344728@qq.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123456
An optional company name []:lizhao
- CSR:Certificate Signing Request 英文缩写,即证书请求文件。
- req:产生证书签发申请命令。
- -new:新请求。
- -out:输出路径。
- -key:密钥是 root.key 文件。
- -keyform PEM:证书有 pem 和 der 格式之分。pem 为文本编码,常见于 Nginx、Apache、Node;der 为二进制编码。
签发根证书(root.cer)
openssl x509 -req -in root.csr -out root.cer -signkey root.key -CAcreateserial -days 3650
Warning: ignoring -CAcreateserial option since -CA option is not given
Certificate request self-signature ok
subject=C=cn, ST=zj, L=hz, O=lizhao, OU=lizhao, CN=lizhao, emailAddress=1927344728@qq.com
- x509:签发 X.509 格式证书命令
- -req:表示证书输入
- -in:表示输入文件。
- -out:表示输出文件。
- -signkey:表示自签名。
- -CAcreateserial:创建证书序列号,使用此选项,当 CA 序列号文件不存在时将被创建。它将包含序号“02”,正在签名的证书将具有 1 作为其序列号。通常如果指定了
-CA选项并且序列号文件不存在,则会出现错误。 - -days:表示有效时间。部分校验场景对过长有效期更严格,自签根证书用 3650 天一般仍可用。
生成 p12 格式证书
openssl pkcs12 -export -out root.p12 -inkey root.key -in root.cer
生成服务端证书
# 服务器私钥(server.key)
openssl genrsa -out server.key 2048
# 服务端请求文件(server.csr)
openssl req -new -out server.csr -key server.key -keyform PEM
# 注意:Windows 下,'/' 会被解析为路径
openssl req -new -out server.csr -key server.key -keyform PEM -subj /C=cn/ST=zj/L=hz/O=lizhao/OU=lizhao/CN=lizhao/emailAddress=1927344728@qq.com
# 使用根证书签发服务器证书(server.cer)
openssl x509 -req -in server.csr -out server.cer -CA root.cer -CAkey root.key -CAcreateserial -days 365
生成客户端证书
# 客户端私钥(client.key)
openssl genrsa -out client.key 2048
# 客户端请求文件(client.csr)
openssl req -new -out client.csr -key client.key -keyform PEM
# 使用根证书签发客户端证书(client.cer)
openssl x509 -req -in client.csr -out client.cer -CA root.cer -CAkey root.key -CAcreateserial -days 365
# 生成客户端 p12 格式证书
openssl pkcs12 -export -clcerts -in client.cer -inkey client.key -out client.p12
证书格式转换
# 转 P12(下文 `.crt` 与上文 `.cer` 为同类证书公钥文件,按实际文件名替换即可)
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "client"
# crt 转 pem
openssl x509 -in client.crt -out client.pem -outform PEM
# crt 转 cer
openssl x509 -in server.crt -out server.cer -outform der
openssl x509 -in client.crt -out client.cer -outform der
# 转 der
openssl x509 -in client.crt -outform der -out client.der
# 测试证书命令
curl --cacert root.crt --cert client.crt --key client.key --tlsv1.2 --insecure https://aaa.xxx.cn/api/bbb
keytool
Keytool 是一个 Java 数据证书的管理工具,Keytool 将密钥(key)和证书(certificates)存在一个称为 keystore 的文件中。
在 JDK 1.4 以后的版本自带 keytool,位于:%JAVA_HOME%\bin\keytool.exe。
# 生成签名证书
keytool -genkey -alias client -keyalg RSA -keysize 2048 -validity 36500 -keystore client.keystore
# 查看证书
keytool -list -v -keystore client.keystore
# .keystore 转 .p12
keytool -importkeystore -srckeystore client.keystore -srcstoretype JKS -deststoretype PKCS12 -destkeystore client.p12
# .p12 转 .jks
keytool -v -importkeystore -srckeystore client.p12 -srcstoretype PKCS12 -destkeystore client.jks -deststoretype JKS
# .jks 转 .pem
keytool -export -rfc -keystore client.jks -alias client -file client.pem
keytool -genkey -alias server -keyalg RSA -keysize 2048 -validity 36500 -keystore server.keystore
keytool -importkeystore -srckeystore server.keystore -srcstoretype JKS -deststoretype PKCS12 -destkeystore server.p12
keytool -v -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore server.jks -deststoretype JKS
keytool -export -rfc -keystore server.jks -alias server -file server.pem
mkcert
mkcert 是一个使用 Go 语言编写的生成本地自签证书的小程序,具有跨平台、使用简单、支持多域名、自动信任 CA 等一系列方便的特性,仅供本地开发环境快速搭建 HTTPS,禁止公网生产环境使用。
安全警告:mkcert 生成的 rootCA-key.pem 拥有劫持本机全部 HTTPS 请求的能力,切勿分享、外泄该私钥文件。
安装
不同操作系统安装方式,参考官方仓库,下面为常用方式。
macOS
brew install mkcert
brew install nss # 适配 Firefox 浏览器
Linux(Debian、Ubuntu)
sudo apt install libnss3-tools
# 二进制包安装
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
Windows(Chocolatey)
choco install mkcert
使用命令
mkcert 规避了繁杂的 openssl 命令,几个简单的参数就可以生成一个本地可信的 HTTPS 证书了。
# 将 mkcert 使用的根证书加入了本地可信 CA
mkcert -install
# 生成自签证书
mkcert localhost 127.0.0.1 ::1
Node 不会读取系统根信任库,使用 mkcert 证书的 Node 项目,需要设置环境变量指定 CA 文件:
export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"移动端设备使用:需要拿到
mkcert -CAROOT输出路径下的rootCA.pem,在手机手动安装根证书,移动端访问本地 HTTPS 服务才会信任证书。
高级参数示例:
# 指定输出证书、私钥文件名
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1
# 使用 ecdsa 椭圆曲线密钥生成证书
mkcert -ecdsa localhost
# 输出 pkcs12 格式
mkcert -pkcs12 localhost
# 根据已有 csr 请求文件生成证书
mkcert -csr xxx.csr
查看 CA 根证书存放路径
mkcert -CAROOT
环境变量
CAROOT可以自定义 mkcert 存放 CA 证书目录;TRUST_STORES环境变量可以限定需要写入的信任存储。
如何使用证书?
Node.js 中使用
const fs = require("fs")
const express = require("express")
const https = require("https")
const app = express()
const server = https.createServer({
key: fs.readFileSync('./clientKey.pem'),
cert: fs.readFileSync('./clientCert.pem')
}, app).listen(9601, () => {
console.log('本地安全链接:https://localhost:9601')
})
webpack 中使用
通常启动本地服务是通过 http://localhost 来访问的,这在目前普遍使用 HTTPS 协议的情况下,可能会遇到一些问题。比如:无法访问 HTTPS 保护服务器,或者可访问,但限制了 Cookie 发送(iOS)。
module.exports = {
devServer: {
https: true,
key: fs.readFileSync('./clientKey.pem'),
cert: fs.readFileSync('./clientCert.pem')
}
}
实测: 可以用 https://localhost:9601 访问,但无法用 https://0.0.0.0:9601 或者本地 IP 访问,Chrome 中报错:net::ERR_CERT_INVALID。
备注:webpack-dev-server 4+ 将 https: true 调整为 server: { type: 'https', options: { key, cert } },配置时按所用版本选择。
uni-app 中使用
uni.configMTLS({
certificates: [{
'host': 'www.test.com',
'client': '/static/client.p12',
'clientPassword': '123456789',
'server': ['/static/server.cer'],
}],
success ({code}) {
console.log(code)
}
});
相关问题
如何让 Mac 信任证书?
如果要生成让 Mac 信任自签名的证书,需要先让 Mac 信任根证书,再为本地开发环境颁发证书。
让 Mac 信任根证书,以便它所发布的所有单个证书也是可信的:启动台 -> 搜索输入“钥匙串” -> 单击,在钥匙串访问窗口,文件 -> 导入项目 导入生成的 .pem 文件。双击导入的证书,并在 『信任』部分中将 『使用此证书时:』下拉列表更改为 『始终信任』 。
只需要信任根 CA 证书,由该根证书签发的业务服务证书,系统会自动信任。