HTTPS配置全记录(不定期更新版)
更新说明
2021-10-17: 合并旧文《Let’s HTTPS》中的部分并更新,增加自签名CA部分。 2021-11-16:增加查看证书。
缘起
前几天帮肾上TR@SOE(CSDN唯一一个用户名里有特殊字符的用户)部署一个HTTPS,虽然去年写过用Let’s Encrypt证书部署HTTPS的文章,但是这次略有不同,所以作个记录吧,顺便也把常见的做法都说一说。
环境
常见的部署环境和条件有以下几种情况组合:
- Apache+自签名证书
- Apache+免费或商业证书
- Nginx+自签名证书
- Nginx+免费或商业证书
免费证书和商业证书本质上是一样的,都是可以被系统承认的证书,只是申请方式不同而已。
证书
证书结构
配置一个HTTPS服务所需要的证书包括几个部分:
- Server Key(服务器私钥)
- CSR(Certificate Signing Request)
- CRT(X509 Certificate)
创建证书的基本流程是这样:
- 生成自己的服务端私钥
- 输入基本信息并用私钥签名生成CSR
- 提交CSR给证书机构CA(免费或商业证书)签名生成CRT,或自己做CA签名生成CRT(自签名证书)
其中前两个步骤都是一样的,在这里统一说明一下。
生成服务器私钥
openssl genrsa -out server.key 4096
输出的server.key文件就是服务器私钥,4096是密钥长度,要求不高的话用2048也可。
生成服务器CSR
openssl req -new -sha256 -key server.key -out server.csr
因为sha1已经不安全,所以这里用了sha256,可能太旧的客户端(比如win98?)会不支持。
server.csr就是生成的CSR,server建议用你的网站名标识会比较方便识别。
然后按提示输入:
- 国家
- 省
- 市
- 公司
- 部门
- 通用名(即网站域名,这个必须准确,有些商业证书支持在这里用带www的域名后签发出同时支持不带www的域名)
- 密码(可选,设置的话以后重启webserver都需要输入密码)
自签名证书做法
简单自签名证书
生成服务器证书
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
其中签名用的KEY就是自己的服务端私钥,所以这是一个自签名证书。
有效期为3650天(即十年)。
自签名CA
以下是例子,除了key用椭圆曲线算法没用RSA以外,和域名证书的生成方式是一样的。
生成CA私钥:
openssl ecparam -out ca.key -name prime256v1 -genkey
使用的曲线为prime256v1
,支持的曲线可以这样查看:
openssl ecparam -list_curves
生成CA的CSR(注意:CA必须使用单独的通用名称,不可以用此CA给此通用名称签发证书):
openssl req -new -sha256 -key ca.key -out ca.csr
生成CA的证书:
openssl x509 -req -sha256 -days 3650 -in ca.csr -signkey ca.key -out ca.crt
有了CA证书以后就可以来生成服务器证书了。
生成服务器私钥(这里也用椭圆曲线算法):
openssl ecparam -out server.key -name prime256v1 -genkey
生成服务器CSR:
openssl req -new -sha256 -key server.key -out server.csr
最关键的一步——用CA证书签发服务器证书:
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -sha256
客户端使用自签名CA
因为自签名CA不在系统自带的CA列表里,所以由自签名CA签发的服务器证书不被客户端承认,访问时会报证书错误,这时需要把自签名的证书导入到客户端系统里。
这里以Linux为例:
mkdir -p /usr/share/ca-certificates/mycerts
cp /path_to_ca/ca.crt /usr/share/ca-certificates/mycerts/
dpkg-reconfigure ca-certificates
# 选择yes即可导入
之后就可以接受所有用此CA签发的服务器证书了。
免费证书生成
旧文《Let’as HTTPS》中关于Let’s Encrypt的部分已过时,新的方法要简单很多:
首先需要安装Let’s Encrypt官方客户端,顺便再装上nginx插件(最好用python3-pip安装,毕竟python2明年就不再支持了):
python3-pip install certbot certbot-nginx
然后就可以创建证书了,不再需要手工创建KEY和CSR,一步直接生成——甚至那个HTTP的nginx配置也会直接创建:
certbot run --nginx --email [your_email] -w /[path_to]/challenge -d [your_domain] --preferred-challenges http --renew-by-default --agree-tos
执行完就可以自动创建证书并保存,同时还会修改相应的nginx配置文件,非常方便。
如果想多个域名用同一个证书的话,可以加多个 -d 参数。
新的官方客户端还支持泛域名证书,不过就需要手工创建了,方法见《Let’s Encrypt的泛域名证书》。
更新证书也很简单:
certbot renew
即可自动更新本机所有的证书(当然不包括需要手工创建的泛域名证书)。
用以下命令则可以查看当前的所有证书:
certbot certificates
不想再要的证书也可以删除:
certbot delete
然后根据提示选择要删除的证书即可。
商业证书生成
首先找一个商业证书机构(CA)或其代理商下一个证书订单。
其次是选择证书类型。
证书类型
商业证书有很多类型,也有很多的CA可以选择,不同的CA,不同的类型价格也不一样。
常见的CA有:
- VeriSign(Symantec)
- GeoTrust(RapidSSL)
- Comodo
- ……
以上以价格从高到低排序。除此之外当然还有很多,具体可以打开你的系统证书列表看看。
需要特别说一句的就是:臭名昭著的CNNIC和沃通(WoSign,包括免费的StartSSL),已经被证明不安全,建议将它们从系统中删除,并且不要去申请使用它们的证书。
常见的证书类型有三种:
- DV(域名验证)
- OV(组织验证)
- EV(扩展验证)
验证级别从低到高排序,价格也是如此。
DV证书只验证域名,在最终访问者那边查看证书时将不会包含CSR中的组织信息,只有域名信息,也就是说你就算是在CSR里输入你是GOOGLE,到了客户端那里也是看不到的。
OV证书除了验证域名还需要验证组织,即你需要提供证据证明你在CSR里输入的公司或组织的确是你, 这样才能在客户端证书里查看到你的组织信息。
EV证书就要求更高了,通常是金融机构之类的用。
除此之外,证书还有一种区别:
- 单域名证书
- 多域名证书
- 泛域名证书
一样是价格从高到低排序。
单域名证书就是只能用于一个域名的证书,某些商业证书可以提供两个域名:带WWW的和不带WWW的。
多域名证书就是一个证书可以用于多个域名,默认支持三个域名,当然增加域名需要加钱,但比单独买单域名证书要便宜。适用于一个公司有多个域名,而且多个域名部署在一个服务器上,使用一个证书会比较省事,也比较便宜。
泛域名证书就是一个证书可以用于一个域名下的任意多个子域名。
申请流程
一般申请流程(仅指DV类型)如下:
- 选择CA或代理商
- 选择证书类型
- 选择有效期(以年为单位,一般时间越长单价越便宜)
- 下单付款
- 成交后按服务商的邮件或文档开始验证域名(以下为一种流程,不同服务商可能不太一样)
- 上传CSR(或用服务商提供的工具生成,但不推荐)
- 选择验证方式(EMAIL,文件,DNS等)
- 按相应方式操作
- 等待验证
- 取得证书(邮件或从服务商处自行下载):
其中各验证方式大致如下:
- Email:证书机构向域名注册邮箱或域名的webmaster等邮箱(可选)发送验证邮件
- 文件:在域名的指定路径下放验证文件供证书机构访问
- DNS:创建指定的DNS TXT记录供证书机构验证
通过验证后即可获得证书。
证书内容一般包括:域名的CRT,证书机构的CRT链(可能有多个),根证书CRT(CA的证书),如果用服务商的工具生成CSR,还应该会有CSR和KEY。
查看,转换和测试
查看key,CSR和X509证书
# key
openssl rsa -noout -text -in server.key
# csr
openssl req -noout -text -in server.csr
# x509证书
openssl x509 -noout -text -in server.crt
证书格式转换
# PEM转换为DER
openssl x509 -outform der -in server.crt -out server.der
# DER转换为PEM
openssl x509 -inform der -in server.der -out server.pem
# PEM转换为PKCS
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt -certfile ca.crt
# PKCS转换为PEM
openssl pkcs12 -in server.pfx -out server.pem -nodes
证书校验
# 自签名证书
openssl verify server.crt
# 带CA校验
openssl verify -CAfile ca.crt server.crt
查看和测试服务器证书
# 查看服务端证书
openssl s_client -connect yourserver:443
# 保存服务端证书
openssl s_client -connect yourserver:443 server.crt
# 用证书启动一个测试服务器
openssl s_server -accept 443 -cert server.crt -key server.key -www
部署
分别以最常用的Apache和Nginx为例,其它WebServer请参考相关文档。
Apache
这里只介绍Apache2(以2.4为例)。
HTTPS的基本配置可以直接套用默认配置,只是要改一下证书文件的部分:
SSLEngine on
SSLCertificateFile /path_to_ssl/server.crt
SSLCertificateKeyFile /path_to_ssl/server.key
SSLCertificateChainFile /path_to_ssl/server-ca.crt
其中server.crt和server.key就是前面说过的私钥和域名证书。
重点说一下server-ca.crt,这个就是前面谈到商业证书时提到的中间证书链(包括CA根证书)。对于Let’s Encrypt这样的免费证书,这个就是Let’s Encrypt的证书(见旧文),对于自签名证书,这一项可以去掉。
中间证书链的生成方法如下:
cat provider.crt provider-parent.crt root.crt > server-ca.crt
很简单,就是把那一堆证书串起来生成一个证书文件即可,不过需要注意顺序,从最低级的证书(你的域名证书的上一级)开始到最高级的CA根证书。
Apache从2.2.12开始支持SNI,可以为多个域名的虚拟主机使用各自独立的证书,用法与HTTP虚拟主机类似,只需要在每个虚拟主机配置里加上相应的证书配置即可。
需要注意的是:WinXP,JAVA6,Android 2.3等不支持SNI。
最后,如我在去年的旧文里所说,SSLv2/v3之类的协议和某些SSL加密算法已经不再安全,所以在没有特定的兼容性需要的情况下,需要配置禁用这些不安全的选项。
在全局配置(注意,不是单个虚拟主机配置里)里加上:
SSLProtocol TLSv1 +TLSv1.1 +TLSv1.2
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:!DHE-RSA-AES128-GCM-SHA256:!DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES128-SHA:!DHE-DSS-AES128-SHA256:!DHE-RSA-AES256-SHA256:!DHE-DSS-AES256-SHA:!DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
以上协议和加密算法都是目前还安全的,并且提供了最大可能的兼容性。未来如果出现新的安全问题,请自行调整。
Nginx
基本上在旧文里都说过了,这里再总结一下。
相比Apache的配置,Nginx没有单独的中间证书链项目,所以是把中间证书链和域名证书串在一起作为完整的域名证书来用:
cat server.crt server-ca.crt > server.full.crt
配置HTTPS:
listen 443 ssl http2;
ssl_certificate "/path_to_ssl/server.full.crt";
ssl_certificate_key "/path_to_ssl/server.key";
注意:新版的Nginx不再支持ssl on
指令,而是直接在listen
指令后加上ssl,另外,更新的版本还可以支持http2,直接加上即可。
SNI使用和虚拟主机类似的方法配置。
全局安全配置:
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:!DHE-RSA-AES128-GCM-SHA256:!DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES128-SHA:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
配置完成后重启服务即可生效。
校验
不论是用Apache还是Nginx,都建议配置完HTTPS以后到SSL Labs验证一下安全性。
推送到[go4pro.org]