Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

开始使用 ECC 证书

$
0
0

我之前的文章多次提到 ECC 证书,但一直没有专门介绍 ECC 证书的文章,今天补上。本文包含三部分内容:1)简单介绍 ECC 证书是什么;2)介绍如何申请 ECC 证书;3)以 Nginx 为例介绍如何使用 ECC 证书。顺便说下,本站已经用上了 ECC 证书。本站主要用到的技术及主要支持的特性,请点击查看。

简单介绍

HTTPS 通过 TLS 层和证书机制提供了内容加密、身份认证和数据完整性三大功能,可以有效防止数据被监听或篡改,还能抵御 MITM(中间人)攻击。TLS 在实施加密过程中,需要用到非对称密钥交换和对称内容加密两大算法。

对称内容加密强度非常高,加解密速度也很快,只是无法安全地生成和保管密钥。在 TLS 协议中,应用数据都是经过对称加密后传输的,传输中所使用的对称密钥,则是在握手阶段通过非对称密钥交换而来。常见的 AES-GCM、ChaCha20-Poly1305,都是对称加密算法。

非对称密钥交换能在不安全的数据通道中,产生只有通信双方才知道的对称加密密钥。目前最常用的密钥交换算法有 RSA 和 ECDHE:RSA 历史悠久,支持度好,但不支持 PFS(Perfect Forward Secrecy);而 ECDHE 是使用了 ECC(椭圆曲线)的 DH(Diffie-Hellman)算法,计算速度快,支持 PFS。要了解更多 RSA 和 ECDHE 密钥交换的细节,可以阅读 Cloudflare 的 这篇文章 。

只有非对称密钥交换,依然无法抵御 MITM 攻击,还得引入身份认证机制。对于大部分 HTTPS 网站来说,服务端一般通过 HTTP 应用层的帐号体系就能完成客户端身份认证;而浏览器想要验证服务端身份,需要用到服务端提供的证书。

浏览器会在两个步骤中用到证书:1)证书合法性校验。确保证书是由合法 CA 签署,且能够用于当前网站;2)使用证书提供的非对称加密公钥,完成密钥交换和服务端认证。

证书合法性校验的原理,简单总结如下:

根据版本号、序列号、签名算法标识、发行者名称、有效期、证书主体名、证书主体公钥信息、发行商唯一标识、主体唯一标识、扩展等信息,生成 TBSCertificate(To Be Signed Certificate)信息; 签发数字签名:使用 HASH 函数对 TBSCertificate 计算得到消息摘要,再用 CA 的私钥进行加密,得到签名; 校验数字签名:使用相同的 HASH 函数对 TBSCertificate 计算得到消息摘要,与使用 CA 公钥解密签名得到内容相比较;

可以看到校验证书需要同时用到签名和非对称加密算法:目前必须使用 SHA-2 做为证书签名函数(没有打 XP SP3 补丁的 IE6 不支持);目前一般使用 RSA 算法对 TBSCertificate 进行非对称加密。可以通过 openssl 工具来查看证书签名算法:

$ openssl x509 -in chained.pem -noout -text | grep 'Signature Algorithm' Signature Algorithm: sha256WithRSAEncryption

大部分 CA 都有证书链,浏览器对于收到的多级证书,需要从站点证书开始逐级验证,直至出现操作系统或浏览器内置的受信任 CA 根证书。

浏览器还需要校验当前访问的域名是否存在于证书 TBSCertificate 的 Common Name 或 Subject Alternative Name 字段之中。

在 RSA 密钥交换中,浏览器使用证书提供的 RSA 公钥加密相关信息,如果服务端能解密,意味着服务端拥有证书对应的私钥,同时也能算出对称加密所需密钥。密钥交换和服务端认证合并在一起。

在 ECDHE 密钥交换中,服务端使用证书私钥对相关信息进行签名,如果浏览器能用证书公钥验证签名,就说明服务端确实拥有对应私钥,从而完成了服务端认证。密钥交换和服务端是完全分开的。

可用于 ECDHE 数字签名的算法主要有 RSA 和 ECDSA,也就是目前密钥交换 + 签名有三种主流选择:

RSA 密钥交换(无需签名); ECDHE 密钥交换、RSA 签名; ECDHE 密钥交换、ECDSA 签名;

以下是 Chrome 中这三种密钥交换方式的截图:


开始使用 ECC 证书

内置 ECDSA 公钥的证书一般被称之为 ECC 证书,内置 RSA 公钥的证书就是 RSA 证书。由于 256 位 ECC Key 在安全性上等同于 3072 位 RSA Key,加上 ECC 运算速度更快,ECDHE 密钥交换 + ECDSA 数字签名无疑是最好的选择。由于同等安全条件下,ECC 算法所需的 Key 更短,所以 ECC 证书文件体积比 RSA 证书要小一些。以下是本站的对比,可以看到 ECC 证书要小 1/3:


开始使用 ECC 证书

RSA 证书可以用于 RSA 密钥交换(RSA 非对称加密)或 ECDHE 密钥交换(RSA 非对称签名);而 ECC 证书只能用于 ECDHE 密钥交换(ECDSA 非对称签名)。

并不是所有浏览器都支持 ECDHE 密钥交换,也就是说 ECC 证书的兼容性要差一些。例如在 windows XP 中,使用 ECC 证书的网站只有 Firefox 能访问(Firefox 的 TLS 自己实现,不依赖操作系统);Android 平台中,也需要 Android 4+ 才支持 ECC 证书。

好消息是,Nginx 1.11.0 开始提供了对 RSA/ECC 双证书的支持。它的实现原理是:分析在 TLS 握手中双方协商得到的 Cipher Suite,如果支持 ECDSA 就返回 ECC 证书,否则返回 RSA 证书。

也就是说,配合最新的 Nginx,我们可以使用 ECC 证书为现代浏览器提供更好的体验,同时老旧浏览器依然会得到 RSA 证书,从而保证了兼容性。这一次,鱼与熊掌可以兼得。

如何申请

如果你的 CA 支持签发 ECC 证书,使用以下命令生成 CSR(Certificate Signing Request,证书签名请求)文件并提交给提供商,就可以获得 ECC 证书:

openssl ecparam -genkey -name secp256r1 | openssl ec -out ecc.key openssl req -new -key ecc.key -out ecc.csr

以上命令中可供选择的算法有 secp256r1 和 secp384r1,secp521r1 已被 Chrome 和 Firefox 废弃。

我目前在用的 Let's Encrypt,也支持签发 ECC 证书。我使用了 acme.sh 这个小巧的工具来签发证书,指定 -k ec-256 就可以将证书类型改为 ECC:

"/root/.acme.sh"/acme.sh --issue --dns dns_cx -d imququ.com -d www.imququ.com -k ec-256

目前 Let's Encrypt 只提供 RSA 中间证书,官方预计会在 2017 年 3 月底提供 ECC 中间证书( via )。

如何使用

有了 RSA/ECC 双证书之后,还需要安装 Nginx 1.11.x。这部分内容我之前详细写过,请点击查看。

一切准备妥当后,将证书配置改为双份即可:

ssl_certificate example.com.rsa.crt; ssl_certificate_key example.com.rsa.key; ssl_certificate example.com.ecdsa.crt; ssl_certificate_key example.com.ecdsa.key;

问题来了!使用 Cloudflare 提供的这份 Cipher Suites 配置,用 Chrome 测试发现并没有采用 ECC 证书,这是为什么呢?

# https://github.com/cloudflare/sslconfig/blob/master/conf ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on;

研究发现,Chrome 与服务端协商到的 Cipher Suites 是 ECDHE-RSA-AES128-GCM-SHA256 ,来自于 ssl_ciphers 配置中的 EECDH+AES128 这部分。我们通过 openssl 工具看一下 EECDH+AES128 具体包含哪些 Cipher Suites:

openssl ciphers -V 'EECDH+AES128' | column -t 0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD 0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD 0xC0,0x27 - ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256 0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256 0xC0,0x13 - ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1 0xC0,0x09 - ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1

可以看到,使用 RSA 做为签名认证算法(Au=RSA)的加密套件排到了前面,导致 Nginx 作出了错误判断。

知道原因就好办了,将这段配置改为 EECDH+ECDSA+AES128:EECDH+aRSA+AES128 ,再看一下:

openssl ciphers -V 'EECDH+ECDSA+AES128:EECDH+aRSA+AES128' | column -t 0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD 0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256 0xC0,0x09 - ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1 0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD 0xC0,0x27 - ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256 0xC0,0x13 - ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1

这下就没问题了。

并不是所有加密套件都需要把 ECDSA 和 aRSA 分开写,例如 EECDH+CHACHA20 就不需要,ECDSA 默认就在前面:

openssl ciphers -V 'EECDH+CHACHA20' | column -t 0xCC,0xA9 - ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=ECDSA Enc=ChaCha20-Poly1305 Mac=AEAD 0xCC,0xA8 - ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=RSA Enc=ChaCha20-Poly1305 Mac=AEAD

最终,我的 Cipher Suites 配置如下,供参考:

ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;

本文链接: https://imququ.com/post/ecc-certificate.html , 参与评论

-- EOF --

发表于 2016-08-27 15:10:18 ,并被添加「Certificate、HTTPS」标签。


Viewing all articles
Browse latest Browse all 12749

Trending Articles