Server Name Indication,简称为 SNI,是 TLS 的一个扩展,为解决这个问题应运而生。有了 SNI,服务端可以通过 Client Hello 中的 SNI 扩展拿到用户要访问网站的 Server Name,进而发送与之匹配的证书,顺利完成 SSL 握手。
Nginx 在很早之前就支持了 SNI,可以通过 nginx -V 来验证。以下是我的验证结果:
./nginx -V
nginx version: nginx/1.9.9
built by gcc4.8.4(Ubuntu4.8.4-2ubuntu1~14.04)
built withOpenSSL1.0.2e-dev xx XXX xxxx
TLS SNI support enabled
configure arguments:--with-openssl=../openssl --with-http_ssl_module --with-http_v2_module
然而,并不是所有浏览器都支持 SNI,以下是常见浏览器支持 SNI 的最低版本:
浏览器最低版本Chrome Vista+ 全支持;XP 需要 Chrome 6+;OSX 10.5.7+ 且 Chrome 5+
Firefox 2.0+
Internet Explorer 7+ (需要 Vista+)
Safari 3+ (需要 OS X 10.5.6+)
Mobile Safari iOS 4.0+
Android Webview 3.0+
可以看到,现在还有一定用户量的 Windows XP IE6~8、Android 2.x Webview 都不支持 SNI。如果要避免在这些浏览器中出现证书错误,只能将使用不同证书的 HTTPS 站点部署在不同 IP 上,最简单的做法是分开部署到不同机器上。
证书选择HTTPS 网站需要通过 CA 取得合法证书,证书通过数字签名技术确保第三方无法伪造。证书的简单原理如下:
根据版本号、序列号、签名算法标识、发行者名称、有效期、证书主体名、证书主体公钥信息、发行商唯一标识、主体唯一标识、扩展生成 TBSCertificate( 待签名证书(To Be Signed Certificate))信息;
签发数字签名:使用 HASH 函数对 TBSCertificate 计算得到消息摘要,用 CA 的私钥对消息摘要进行加密,得到签名;
校验数字签名:使用相同的 HASH 函数对 TBSCertificate 计算得到消息摘要,与使用 CA 公钥解密签名得到内容相比较;
使用 SHA-1 做为 HASH 函数的证书被称之为 SHA-1 证书,由于目前已经找到 SHA-1 的碰撞条件,将证书换成使用更安全的 SHA-2 做为 HASH 函数的 SHA-2 证书被提上日程。
实际上,微软已经宣称自 2017 年 1 月 1 日起,将全面停止对 SHA-1 证书的支持。届时在最新版本的 Windows 系统中,SHA-1 证书将不被信任。
而根据 Chrome 官方博客的文章,使用 SHA-1 证书且证书有效期在 2016 年 1 月 1 号至 2016 年 12 月 31 号之间的站点会被给予「安全的,但存在漏洞」的提示,也就是地址栏的小锁不再是绿色的,并且会有一个黄色小三角。而使用 SHA-1 证书且证书有效期超过 2017 年 1 月 1 号的站点会被给予「不安全」的红色警告,小锁上直接显示一个红色的叉。
然而,并不是所有的终端都支持 SHA-2 证书,服务端不支持还好办,浏览器只能依赖于用户升级了。下面是常见浏览器支持 SHA-2 证书的最低版本:
浏览器支持 SHA-2 证书的最低版本Chrome 26+
Firefox 1.5+
Internet Explorer 6+ (需要 XP SP3+)
Safari 3+ (需要 OS X 10.5+)
Android Webview 2.3+
可以看到,如果要照顾没有打 XP SP3 补丁的 IE6 用户,只能继续使用 SHA-1 证书。
在我之前的文章中,还提到过 ECC 证书,这种新型的证书支持度更差,这里略过不提,有兴趣的同学可以查看。