瑞瑞哥的博客

折腾X25519的两三事

折腾X25519的两三事

为什么我想试着折腾X25519

我前几天手贱,没事升级了chrome到55版本,作为一个懂一些web安全和只懂一点点密码学的人,没事喜欢打开F12随便看看,然后我看到谷歌官网有如下内容:

谷歌55版本security页面X25519.jpg

瞬间对这个感兴趣了,这是什么?然后点开origin,刷新页面:

谷歌55版本security页面的origin可以查看Key_Exchange.jpg

可以看出X25519是一个密钥交换方面的内容,当我具体谷歌那些内容的时候,发现看的都不太懂。本身英语是可以看懂技术文档的,但是X25519相关文献涉及到密码学的知识,看着还是很吃力,于是向微博上一位对安全和密码学很有研究的牛人求助(@比尔盖子):

Curve25519/Ed25519/X25519 是著名密码学家 Daniel J. Bernstein 在 2006 年独立设计的椭圆曲线加密/签名/密钥交换算法,和现有的任何椭圆曲线算法都完全独立。特点是:第一完全开放设计,算法各参数的选择直截了当,非常明确,没有任何可疑之处,相比之下目前广泛使用的椭圆曲线是 NIST 系列标准,方程的系数是使用来历不明的随机种子 c49d3608 86e70493 6a6678e1 139d26b7 819f7e90 生成的,非常可疑,疑似后门;第二,一个椭圆曲线加密算法就算在数学上是安全的,在实用上也并不一定安全,有很大的概率通过缓存、时间、恶意输入摧毁安全性,而 25519 系列椭圆曲线经过特别设计,尽可能的将出错的概率降到了最低,可以说是实践上最安全的加密算法。例如,任何一个 32 位随机数都是一个合法的 X25519 公钥,因此通过恶意数值攻击是不可能的,算法在设计的时候刻意避免的某些分支操作,这样在编程的时候可以不使用 if,减少了不同 if 分支代码执行时间不同的时序攻击概率,相反,NIST 系列椭圆曲线算法在实际应用中出错的可能性非常大,而且对于某些理论攻击的免疫能力不高,Bernstein 对市面上所有的加密算法使用 12 个标准进行了考察,25519 是几乎唯一满足这些标准的 http://t.cn/RMGmi1g ;第三,25519 系列曲线是目前最快的椭圆曲线加密算法,性能远远超过 NIST 系列,而且具有比 P-256 更高的安全性;第四,Daniel J. Bernstein 是世界著名的密码学家,他在大学曾经开设过一门 UNIX 系统安全的课程给学生,结果一学期下来,发现了 UNIX 程序中的 91 个安全漏洞;他早年在美国依然禁止出口加密算法时,曾因为把自己设计的加密算法发布到网上遭到了美国政府的起诉,他本人抗争六年,最后美国政府撤销所有指控,目前另一个非常火的高性能安全流密码 ChaCha20 也是出自 Bernstein 之手;第五,25519 系列曲线自 2006 年发表以来,除了学术界无人问津,2013 年爱德华·斯诺登曝光棱镜计划后,该算法突然大火,大量软件,如 OpenSSH 都迅速增加了对 25519 系列的支持,如今 25519 已经是大势所趋,可疑的 NIST 曲线迟早要退出椭圆曲线的历史舞台,目前,RFC 增加了 SSL/TLS 对 X25519 密钥交换协议的支持,而新版 OpenSSL 1.1 也加入支持,是摆脱老大哥的第一步,下一步是将 Ed25519 做为可选的 TLS 证书签名算法,彻底摆脱 NIST。


作者注:网上有人说时间是2005年,这个不重要。

非常感谢比尔盖子给的回复

我们打开上文中提到的链接,也能够看出各个曲线的安全程度:

SafeCurves

于是乎我想搞一搞X25519,折腾一下玩一玩。

升级Nginx

之前的Nginx是按照屈屈的文章里面弄的,Nginx完整篇 , 它里面用的openssl 是1.0.2的,在这种情况下如果想要启用X25519,需要升级到1.1.0c,具体步骤和他的一样,只是把wget下载的tar包换成高版本的即可。

如果编译时候报错和brotli有关,请加上--with-cc-opt=-Wno-deprecated-declarations

升级到最新的openssl之后就可以按照这篇文章里的介绍,启用X25519了。

理论上主要修改这一行就行了:

1
2
#ssl_ecdh_curve secp384r1;
ssl_ecdh_curve X25519;

理论上修改后重启Nginx即可完成

更新cipher_suite

我的个人博客目前还是自己折腾状态,也就几个朋友偶尔会看看,谷歌百度都不收录的,所以Nginx配置基本不考虑兼容性和性能,前一阵子想试试AES256,发现可能是OpenSSL不支持,现在更新了版本,一不做二不休继续玩玩。

将原来的套件ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;改成 ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;,可以看出我这个加密套件基本上什么都不支持,仅仅是因为个人玩玩的。

另外一开始我想弄得是ECDHE-ECDSA-AES256-GCM-SHA256加密套件,后来发现不支持,我还在v2ex上提了这个问题:为什么对称加密的摘要位数要比 AES 的大 。当然你也可以直接:

1
openssl ciphers  | grep ECDHE-RSA-AES256-GCM-SHA256

无法访问

我的网站主证书是ECC 384位,备用证书是RSA 2048位的。弄了半天之后,兴高采烈地打开自己网站,发现无法访问,报的错是:

ecc_with_x25519_not_supported.png

火狐也报了同样的错误,这里就不贴图了。然后我试了RSA 2048位的备用证书,发现就可以访问,同时也能显示X25519。

后来发现只要注释掉ssl_ecdh_curve X25519;这一行即可,对于ECC证书会自动使用X25519参数,对于RSA证书会用默认的P-256,具体如下表格,都是以Chrome为准:

ECC-384 RSA
注释掉ssl_ecdh_curve X25519 P-256
ssl_ecdh_curve X25519; 访问不了 X25519
ssl_ecdh_curve secp384r1; P-384 没试过

注:SSLlab在Key Exchange 这一项对X25519评了90分,我原来spec384r1是100分,这个都不重要

2017年1月8日 19点补充

  1. 发现Nginx 配置文件里的ssl_ecdh_curve可以写多个参数,形如ssl_ecdh_curve X25519:secp384r1;,暂时把我的改成这个样子了,有功夫的话我继续研究一下

    以下内容是我把ssl_ecdh_curve置为空的情况,可以看到除了谷歌其他都是用的默认的:

    不设置曲线的ssllab检测结果.jpg

    而以下内容是ssl_ecdh_curve X25519:secp384r1;的情况:

    用了两个曲线的ssllab检测结果

    支持X25519的都是谷歌系的,这也在我们的预料之中。

    注意:如果你把X25519放在前面,那么谷歌的蜘蛛将没法爬取你的网页;而如果你把它放在后面,那就等于没有X25519,所以目前开启这个就没法被爬取了。

  2. 我随便在网上翻了翻,看到h2o项目的一个issue里有人(TheDecryptor commented on 11 Jun 2016)提到:

    I’ve been experimenting with this a bit recently, it seems EC selection follows the cipher preference option, so if you’re not forcing the server preference you leave curve selection up to the client.

    And from my testing, Firefox/Chrome/Safari/Edge all prefer P-256 over P-384 (And Chrome prefers x25519 over everything). LibreSSL also enables a bunch of curves that browsers won’t use, like sect571r1.

    中文意思就是谷歌浏览器最喜欢X25519,而所有浏览器更喜欢P-256相比于P-384。这个目前和我验证的内容有点接近,但是不清楚Nginx配置文件里的具体用法。

  3. OpenSSL在Github的issue上面说,1.1版本支持curve25519,但是curve448仍然不支持,E-521也是一样不支持。

  4. 我发现让大部分国产浏览器无法访问我的网站的罪魁祸首可能是加密套件太新了不支持,而不是密钥交换的问题,因为上文图中说了,不填ssl_ecdh_curve的话会默认secp256的。

2017年1月9日 20点补充

https://github.com/openssl/openssl/issues/2188 我提的issue

然后他们回复说这是一个已知的problem,而且和https://github.com/openssl/openssl/issues/2033 是一个内容。

具体原因是支持的曲线会同时影响ECDHE和ECDSA,但是实际上本应该各自分开的,在我的例子中也能看出这一点。解决方案就是要么不填,要么手动制定多个(推荐后者)。

说在最后

我定位问题的时候,认为火狐浏览器的那个证书参数就是Key Exchange 参数,现在想想可能是错误的,如下图所示:

firefox_no_key_ex_param.jpg

现在想想可能是搞错了。如果有谁知道以下这两个问题,也请告诉我或者留言:

  • ECC 384位证书和P-384到底有关系吗?强行换成X25519有风险吗?或者换句话说,这个P-384到底是证书层面的东西还是密钥交换里的一个参数?
  • 火狐浏览器在哪里看key exchange的信息? 谷歌浏览器已知可以在origin里面看。

请不吝赐教