https(SSL) 升级实践手札

题记

HTTPS 见是见得多了,只是见过猪跑,没吃过猪肉。

正好有一个客户有需求要搞,于是搞就搞,正好可以有人帮我付学费,折腾了一天,颇为顺利地搞完了。

而且很幸运地遭遇了各种应当遭遇到的细节,也让我关注到了很多本来可能没机会注意到的细节。

特此札记。


一、概述

先说一下总括,以普及一下普罗大众。

首先,搞 https 有什么用,最直观的结果有如下几个:

  1. 协议名称从 http 变为 https;
  2. 默认端口从 80 变为 443;
  3. 浏览器的地址栏开头会显示一个绿色的(搞好之后)的安全标记;

不直观的结果有如下几个:

  1. 网站安全性提高,从中间的传输环节可以防止窃听与篡改;
  2. 获得与一些特殊的接口对接的特权(如 PayPal);
  3. 逼格提升;

然后是升级 https 的先决条件:

  1. 你得有网站,有服务器,有域名;
  2. 得申请到一个 SSL 证书(只有申请回来的才可以变成绿色);

大概要点如此,下面开始逐段逐段讲。

二、证书的申请准备

证书的申请大概是这么一回事,我们先通过 openssl 工具创建一个私钥文件mydomain.key

sudo openssl genrsa -des3 -out mydomain.key 1024

注意:

  1. 不同的证书是可能有加密位数不同的,这里用的是 1024,但有可能是 2048;
  2. 这个 openssl 工具在 windows 和 linux 上面都可以用。

输出的私钥文件大概长这个样子:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,XXXXXXXXXXXX

XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxX==
-----END RSA PRIVATE KEY-----

好了,然后我们通过下面的命令生成申请文件 (mydomain.csr)。

sudo openssl req -new -key mydomain.key -out mydomain.csr

这个 csr 文件在向证书颁发机构申请证书的时候需要提交,需要根据上一步的私钥才能产生,然后会需要输入下面一系列的信息:

参数 说明
Country Name (2 letter code) 使用国际标准组织(ISO)国码格式,填写2个字母的国家代号。中国请填写 CN
State or Province Name (full name) 省份,比如填写Shanghai
Locality Name (eg, city) 城市,比如填写Shanghai
Organization Name (eg, company) 组织单位,比如填写公司名称的拼音
Organizational Unit Name (eg, section) 比如填写IT Dept
Common Name (eg, your websites domain name) 行使 SSL 加密的网站域名。注意通配符域名的情况请使用 * 通配子域名,例如 *.easecloud.cn。
Email Address 邮件地址,可以不填
A challenge password 可以不填
An optional company name 可以不填

生成的 csr 文件大概长这个样子:

-----BEGIN CERTIFICATE REQUEST-----
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxX
-----END CERTIFICATE REQUEST-----

ok,只要最终生成好 csr 申请文件,就可以开始申请证书了。

参考文章:

http://qingwang.blog.51cto.com/505009/160626

https://www.digitalocean.com/community/tutorials/how-to-set-up-multiple-ssl-certificates-on-one-ip-with-nginx-on-ubuntu-12-04

三、证书的申请购买

购买证书一般要钱,比较廉价的可以看这个:https://www.ssls.com

也有免费的 startssl,参考:http://ju.outofmemory.cn/entry/80373

于是怎么购买我们就简单略过,只是最终我们会得到一个 .crt 证书文件,大概形如:

-----BEGIN CERTIFICATE-----
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxXXxXxxxXXXxX
XxxxXXXxXXxXxxxX==
-----END CERTIFICATE-----

好,那么我们凭着手上已经注册好的 mydomain.crt 证书和 mydomain.key 私钥文件,就可以在我们的 http 服务上面配置 SSL 了。

四、Nginx 配置启用 SSL

实际的启用比想象中的简单,只需要大致这几步:

  1. 在原有的虚拟主机 server 段配置上修改端口为 443,然后加入如下三行 SSL 配置:
server {
    listen 443;
    ssl on;
    ssl_certificate /var/ssl/mydomain.crt;
    ssl_certificate_key /var/ssl/mydomain.key;
}
  1. 新增一个 server 段侦听同样域名上的 80 端口,直接 301 跳转到 https 协议上的地址:
server {
    listen 80;
    server_name www.mydomain.com mydomain.com;
    rewrite ^/(.*)$ https://$host/$1 permanent;
}
  1. 然后重启一下 Nginx,这时候会要求输入私钥的密码,日后每次重启 nginx 都需要这一步(解决方法是后话)。

然后再访问网站,如无意外就可以显示到 https 的地址栏了。

五、解决 Nginx 启动输入证书密码的问题

参考:http://wangye.org/blog/archives/243/

大致原理是,将原本带密码的 SSL 私钥导出成一个不带密码的私钥。

openssl rsa -in mydomain.key -out mydomain.key.unsecure

就这样,然后在 nginx 中使用这个 unsecure 的 key 即可。

六、清除站内的非 https 内容

事实上,即使 https 已经正确在你的网站上安装,但是在地址栏上的 https 那个小锁头有可能还不是绿色的,而是显示一个黄色的三角形以示警告。

很多情况下,这是因为你的页面里面包含自动加载的非 https 的内容。

所谓的自动加载,可能是 img/js/css/iframe 等等资源,请注意只要网站里面加载了一些非 http 的这类资源(注意不管是本域还是外域都有这个限制,因为只要是 http 内容,就存在被篡改的风险,那么很可能一个 js 的请求被篡改了就会影响到整站的安全性),就会触发警告,在调试器的 console 里面也是可以看到的。

于是,在 https 上线之后,很有必要把全站的非 https 资源清查一遍,以确保全栈的 https 统一性,点亮绿色的锁头图标。

七、那些意外与折腾

事实上,由于初次实施这个 SSL 升级策略的是客户的网站,这个网站的服务器是用的 CentOS 5.11,结果在我将证书安装到 nginx 之后,显示的锁头就是黄色的了。

然后点开之后,发现里面提示与网站的连接采用旧的加密技术(TLS1.0),于是显示了这个警告。

苦苦找寻,在互联网上面几乎找不到这个问题的解决方案,下面这些有所涉及,但都解决不了问题:

http://security.stackexchange.com/questions/83831/google-chrome-your-connection-to-website-is-encrypted-with-obsolete-cryptograph

https://tecwhisperer.com/site-encrypted-obsolete-cryptography/

然后最终脑洞频开,想到了为什么好好滴有 TLS1.2 不用走的是 TLS1.0 的问题,终于将我引到了正轨上面:

原因是这样的,我在系统里面查看了一下安装的 nginx 版本:

nginx -V

这样可以显示出所有的 nginx 编译参数,以及开启的模块等等。

我当时在这个 CentOS 5.11 上面安装的是最新版的 nginx1.8.0 的 rpm,以为没事,不料果真有问题。

因为 nginx 里面的加密方法是依赖于 openssl 这个库的,而那台机上面的 openssl 恰好是 0.9.8 版本,不支持 TLS1.2,只支持到了 TLS1.0,所以首先得弄个新版的 openssl 库。

最新的版本是 1.0.2 我整了个 1.0.1 源码安装,并将 yum install 的执行文件替换掉。

升级到 OpenSSL 1.0.1:

http://unix.stackexchange.com/questions/84283/how-can-i-get-tlsv1-2-support-in-apache-on-rhel6-centos-sl6/86326

注意,完成了上述步骤,我的文件夹里面就具备了一套 openssl 的源码,而且路径里面的 openssl 已经是 1.0.1。

我直接敲 openssl version 显示的版本号是 1.0.1,我以为,这就搞定了。

可是我错了。

我重装了一次 nginx,发现问题依旧。

然后继续在网上查。

然后发现 nginx -V 之后,赫然显示 openssl 是不支持的!而且有这一段:

TLS SNI support disabled

说明 openssl 根本没有整上!

后参考这篇文章:<https://dearb.me/archive/2015-03-28/enable-tls-sni-to-support-multiple-certificates-on-the-same-ip />

终于发现问题所在!原来 nginx 对 openssl 的支持,是需要在源码安装的 configure 里面指定 Openssl 的路径,并且声明需要这个支持。

无奈之下,按照上面的这篇文章,重新编译安装了一次 nginx。

不料还有问题!然后脑洞再开,查看了一下 rpm 安装的 nginx 里面,nginx -V 显示的编译参数,然后与上面博文中的参数合并!

就是下面这坨!

nginx version: nginx/1.8.0
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-55)
built with OpenSSL 1.0.1h 5 Jun 2014
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-cc-opt='-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' --with-openssl=../openssl-1.0.1h/

七、Firefox 及手机浏览器无法识别证书的问题

后面发现,如果使用 Firefox 或者手机浏览器访问网站,是会提示网站不被信任的。

这样的话需要手动添加这个网站的例外,但这诚然不是解决了问题。

如果加了例外,可以参照这里将(Firefox)例外的规则删掉:http://forum.ubuntu.org.cn/viewtopic.php?t=377679

这样的情况下,需要做一些特殊的处理:

StartSSL(自己的免费证书): http://liulanmi.com/zt/5916.html

RapidSSL(客户的付费证书): https://knowledge.rapidssl.com/support/ssl-certificate-support/index?page=content&actp=CROSSLINK&id=AR1549

总体来说的话,方法就是将颁发机构的中间证书也加入到最终的证书文件 .crt 里面去,加完这些,就在各个浏览器中浏览也不会得到警告提示了。


终于大功告成!辛酸。

对于旧版的系统,会存在很多这样的问题,但是这一折腾也非常幸福,起码让我重新熟悉了 nginx 的编译安装以及其与 openssl,TLS 加密算法版本之间的爱恨情仇,下次就可以驾轻就熟了!

特此存证。


参考文章:

http://han.guokai.blog.163.com/blog/static/136718271201211631456811/


后记

最近偶然发现了另一个免费的证书源:

https://www.qianduan.net/qie-huan-dao-lets-encryptmian-fei-zheng-shu/?utm_source=tuicool&utm_medium=referral


【转载请附】愿以此功德,回向 >>

原文链接:https://www.huangwenchao.com.cn/2015/07/https-update-log.html【https(SSL) 升级实践手札】

发表评论

电子邮件地址不会被公开。 必填项已用*标注