前言
博客使用 HTTPS 已经有一段时间了,最近把当时的配置过程梳理了一遍,希望能对有需要的同学有所帮助。
概念介绍
最近 HTTPS 是越来越流行了,前一阵子的百度,最近的淘宝、知乎,都已经开启了全站 HTTPS 的时代。HTTPS 即 Hypertext Transfer Protocol Secure,简单理解就是在 HTTP 协议上加了一层加密。具体的细节以后弄透彻了再写写。
无利不起早,那么 HTTPS 的好处是什么呢?我个人觉得有以下几点:
- 数据传输加密,防止信息被窃取。使用 HTTP 协议时,用户的密码、银行账户、隐私信息等都是在网络上明文传输,很容易被中间人截取。
- 防欺诈。当你使用 EV 级别证书(https://www.wosign.com/EVSSL/index.htm)时,浏览器网址显示和一般 SSL 证书不一样。可以对比下:https://www.paypal.com/signin/ 和 https://www.baidu.com/。使用此类证书的网址,主流的浏览器都会在地址栏显示企业的名称(支持中文),增加了网站信任度,进一步防止用户被钓鱼网站欺骗,当然证书的价格也更贵一些。
- 防止劫持。这点可以说是国内各大公司下决心支持 HTTPS 的主要原因了。一旦被劫持,用户访问速度会变慢,看到的内容会被篡改,比如商家在百度投放的广告被替换成竞品的广告,淘宝的商品被带上小尾巴(返利链接),甚至直接跳转到x东。这些不光是损害用户的利益,更是直接影响公司收入。
- 使用新的技术。比如 SPDY/HTTP2(头部压缩、连接复用、Server Push等) 的基础都是 HTTPS,新协议对移动端 APP 性能提升帮助很大:《双11手淘前端技术: H5性能最佳实践》
当然 HTTPS 不是银弹,不是用了就保险了,安全是一个整体,这是典型的水桶场景,往往一个系统被攻破是在其薄弱的环节。
证书申请
证书申请的大概流程是,首先在自己的服务器使用 openssl 命令(尽量使用高版本的 OpenSSL,除了能规避漏洞,还能使用更高级的密钥加密/交换算法,提升安全性和性能)生成 csr 和 key 文件,命令如下:
|
openssl req -new -newkey rsa:2048 -sha256 -nodes -out example.com.csr -keyout example.com.key.pem -subj "/C=CN/ST=Beijing/L=Beijing/O=Example Inc./OU=Web Security/CN=example.com" |
如命令参数所示,私钥 key RSA 的加密强度是 2048 位(rsa:2048)。CSR 文件使用了更安全的 sha256(SHA-2)摘要算法(SHA-1算法在2016年将不被证书厂商和现代浏览器支持)。
-subj 参数指定证书申请者的信息,如果不直接使用此参数,也会有交互式的命令提示填写这些参数。此部分具体参数释义:
Country Name (2 letter code): 输入国家地区代码,例如中国的 CN
State or Province Name (full name): 地区省份
Locality Name (eg, city): 城市名称
Organization Name (eg, company): 公司名称
Organizational Unit Name (eg, section): 部门名称
Common Name (e.g. server FQDN or YOUR name): 申请证书域名,如果是泛域名证书,则应该填写 *.example.com
Email Address: 电子邮箱
命令还会提示输入challenge password
,这个不是加密证书,而是相当于一个二次认证密码,如果厂商证书支持challenge password
,而且你也设置了的话,后续证书操作需要验证这个密码,提高安全性。
生成的 CSR(Certificate Signing Request,即证书签名请求,不是证书)文件包含了证书的主要信息和公钥,同时生成了私钥 key 文件(注意保存)。然后把 CSR 文件提交给 CA 厂商(不用提交 key 文件),一般提交的时候会让你选择服务器类型,本文指选的 Nginx。中间还会有一个域名所有者验证过程,一般几分钟后(根据证书类型不同,时间不一样)就会发一份邮件给你。
邮件附件一般会带有厂商用 CA 私钥签好的证书文件 example.com.cert.pem(包含 域名、CA 信息,key 对应的公钥,CA 的签名)。同时会给你 CA 的证书链 example.com.cert.ca.pem,如果 CA 证书是多个文件(通常包括 1-2 个中间证书和 1 个根证书),那么需要按照证书信任链由下游到上游,把文件合并成 example.com.cert.ca.pem(cat COMODOECCDomainValidationSecureServerCA.crt COMODOECCAddTrustCA.crt AddTrustExternalCARoot.crt > example.com.cert.ca.pem)。不用把根证书放进 CA 证书链文件里面,因为浏览器和操作系统都内置了根证书,也只认自己内置的根证书。
不过现在更流行的是使用 acme.sh 自动申请和续签 Let’s Encrypt 证书,即免费又好用。acme.sh 提供的方式有很多种,大家按需配置即可,我是 DNSPod + Nginx,仅供参考:
|
# 申请证书 acme.sh --issue -d example.com -d www.example.com -d api.example.com --dns dns_dp --keylength ec-256 # 安装证书位置 acme.sh --install-cert --ecc -d example.com \ --key-file /path/ssl/example.com.key.pem \ --ca-file /path/ssl/example.com.cert.ca.pem \ --fullchain-file /path/ssl/example.com.cert.full.pem \ --reloadcmd "sudo systemctl force-reload nginx" |
证书处理
后面的步骤各个 HTTP Server 就有点不一样了:
|
# Nginx,自己的证书和CA证书组成下游到根的完整证书链,key单独配置 cat example.com.cert.pem example.com.cert.ca.pem > example.com.cert.full.pem # Lighttpd,key和自己证书合并为一个新的证书,ca证书单独配置 cat example.com.key.pem example.com.cert.pem > example.com.key.cert.pem # 附 Lighttpd 的配置: $SERVER["socket"] == "xxx.example.com:443" { ssl.engine = "enable" ssl.pemfile = "/path/ssl/example.com.key.cert.pem" ssl.ca-file = "/path/ssl/example.com.cert.ca.pem" server.name = "xxx.example.com" …… } |
tomcat 则比较特别,证书编码格式需要的不是 pem 而是 JKS,得转换一下。
|
# 先转换为pfx格式 openssl pkcs12 –export –out example.com.cert.pfx –in example.com.cert.full.pem –inkey example.com.key.pem # 回车后输入pfx证书的密码两次 # 使用java jdk将pfx格式证书转换为jks格式证书 keytool -importkeystore -srckeystore example.com.cert.pfx –destkeystore example.com.cert.jks -srcstoretype PKCS12 -deststoretype JKS # 回车后输入一次pfx证书密码,然后输入两次要设置的jks证书密码 |
到这里为止出现了好几个文件,可能有点迷糊,这里总结一下
文件名
|
释义
|
example.com.csr
|
Certificate Signing Request,证书签名请求,不是证书
|
example.com.key.pem
|
证书私钥(key),不能泄露(即使是对 CA 厂商),和CSR文件成对存在
|
example.com.cert.pem
|
自己的域名证书(pem格式),包含 key 对应的公钥
|
example.com.cert.ca.pem
|
CA厂商证书链(pem格式)
|
example.com.cert.full.pem
|
合并了自己证书和CA证书的完整证书链(pem格式)
|
example.com.key.cert.pem
|
合并了key和域名证书的文件(pem格式)
|
example.com.cert.pfx
|
pfx/p12格式的完整证书链(包含了key)
|
example.com.cert.jks
|
jks格式(JAVA)的完整证书链(包含了key)
|
对于这几种文件的区别,可以参考:https://www.cnblogs.com/yjmyzz/p/openssl-tutorial.html、https://blog.freessl.cn/ssl-cert-format-introduce/。实际不用那么麻烦,一般证书厂商会提供不同 webserver 的配置文件,如果没有则需要动手一下,或者使用证书厂商提供的转换工具。
TLS握手简述
借由这几个证书文件,简单介绍一下 TLS 的握手过程(RSA 密钥交换方式):
客户端
|
服务端
|
ClientHello
明文发送握手请求(上报支持的能力)
生成一个随机数r1
|
|
|
ServerHello
明文返回域名证书(example.com.cert.full.pem)
还会返回一个随机数r2
这里服务端也可以要求客户端提供证书进行校验(流程会有所不同,省略)
|
对证书进行验证,前面有介绍,证书中包含 CA 信息和签名信息,逐级向上验证即可
ClientKeyExchange
验证通过后从域名证书中拿出公钥,然后使用公钥加密PreMasterSecret(随机生成),发送给服务端(密文)
|
|
|
服务端使用私钥(example.com.key.pem)解密得出PreMasterSecret
|
客户端使用r1、r2、PreMasterSecret生成session key(会话密钥)
|
服务端也用同样的方式生成session key,生成算法相同( PRF)
|
使用session key进行对称加密
|
|
可以看出核心就是 PreMasterSecret 是密文,其它都是明文。如果中间人想要截取就只能替换证书中的公钥(用自己私钥解密),因为证书本身也有完整性签名,就只能替换整个证书,但证书又是一级级到 Root CA 校验,所以 TLS 握手的结果是安全的。
证书逐级验证的过程(来源:https://security.stackexchange.com/questions/56389/ssl-certificate-framework-101-how-does-the-browser-actually-verify-the-validity)
才疏学浅,介绍的非常简陋,建议阅读 https://halfrost.com/https_tls1-2_handshake/ 及系列文章
配置详解
下面正式开始 Nginx 的配置详解。 Continue Reading...