昨天有朋友说支付宝官网 https://www.alipay.com/ 访问时浏览器报证书错误了。我试了一下,访问正常啊,不过 Certificate Patrol 告诉我支付宝更换 HTTPS 证书了,因为旧的证书要过期了。同时,证书的颁发者也换了,新证书是賽门铁克颁发的。后来我又使用 wget 访问了一下,竟然真的报错了!随即我更换了 Google Chrome 和另一个火狐配置,前者没有报错,后者却也报错了,报错信息是「该证书因为未提供证书颁发链而不被信任」(代码:sec_error_unknown_issuer):
后来又使用 Android 上的 Opera Mobile 和火狐访问,也是报错。
很奇怪,大体上是经常使用的浏览器不会报错,而那些很少使用的浏览器、不记录访问数据的工具都报错了。
昨晚一直没想明白,今天早上却突然想到了:是不是支付宝配置证书时没加入中间证书、或者加错了啊?(疲劳工作什么的果然会严重效率啊喵)
支付宝现在已经能够正常访问了,而我昨天也没有进行抓包,所以没有办法证明(或者证伪)这个猜测是否正确了。不过我却可以实验一下这样的配置会导致客户端如何反应。
准备工作
域名一枚。SSL 私钥一枚、证书一枚、中间证书一枚,如果你的 SSL 证书直接由浏览器信任的根证书机构颁发的就没办法实验这个了哦。浏览器若干。哦当然还要 nginx 一枚,至少要支持 SSL。
为了便于各位重现,这里使用了两个子域名,使用不同的配置。如果你访问的时候浏览器说找不到服务器的话,那是 DNS 那边还没更新啦,今天晚些时候再试试看 :-)
第一幕:不提供中间证书
用户使用firefox -no-remote -P
命令创建了一个全新的火狐配置实例。
用户:浏览器,我要访问 https://brokenchain.lilydjwg.me/。
浏览器:Hi,服务器,我要以 SSL 协议访问 brokenchain.lilydjwg.me。
服务器:浏览器你好,这是我的 SSL 证书。
浏览器:看看。由 AlphaSSL 颁发的证书?AlphaSSL 是谁呀??
浏览器:(对用户)啊咧,服务器给出了一个咱不认识的 SSL 证书,咱不能确认其真实性。有可能是服务器配置有问题,也有可能是你被中间人攻击啦,要小心了哦!(与昨支付宝的证书错误如出一辙。)
第二幕:使用正确配置的证书
运维人员把相应的 AlphaSSL 证书附加到了网站证书文件的后面,并且重新加载了配置。
用户:浏览器,我要访问 https://goodchain.lilydjwg.me/。
浏览器:Hi,服务器,我要以 SSL 协议访问 goodchain.lilydjwg.me。
服务器:浏览器你好,这是我的 SSL 证书。
浏览器:看看。是由 AlphaSSL 颁发的证书,不知道它是谁。不过对方给出了 AlphaSSL 的证书,它又是由 GlobalSign 颁发的。这货我认识,是我信任的机构。开始检查签名……签名无误。GlobalSign 确实信任 AlphaSSL,而我信任 GlobalSign。所以我信任这个证书。
浏览器:(对用户)一切正常!以安全的方式收到了你要访问的内容了喵~
第三幕:不提供中间证书,但浏览器拥有中间证书
用户再次访问配置有问题的服务器。
用户:浏览器,我要访问 https://brokenchain.lilydjwg.me/。
浏览器:Hi,服务器,我要以 SSL 协议访问 brokenchain.lilydjwg.me。
服务器:浏览器你好,这是我的 SSL 证书。
浏览器:看看。由 AlphaSSL 颁发的证书?AlphaSSL 我以前遇到过,找找看……找到了!AlphaSSL 的证书,它是由 GlobalSign 颁发的。这货我认识,是我信任的机构。开始检查签名……签名无误。GlobalSign 信任 AlphaSSL,而我又信任 AlphaSSL。所以我信任这个证书。
浏览器:(对用户)一切正常!以安全的方式收到了你要访问的内容了喵~
附录
用于实验的 nginx 配置:https://goodchain.lilydjwg.me/nginx.conf。
结束语
我这里,新建的火狐实例如预期的在没有获取过中间证书时报错,在获取过之后就不再报错了。而 Google Chrome 一开始就不报错,可能是它内置了该证书,也可能是它从网上的其它某个地方取得了这个证书。wget、curl 等命令行工具总是会报错,因为它们并不存储访问过的证书。
另外注意一下,使用 cat 命令连接网站证书和中间证书时,先确保证书文件最后有换行符,不然会出错的。UNIX 传统,文本的每一行以换行符结束。而微软的做法是,换行符仅仅作为两个行中间的分隔符,最后一行并不以换行符结束,所以在连接多个文件时会因为缺少换行符而出错。