.NET Core如何配置TLS Cipher(套件)?

语言: CN / TW / HK

前言

前不久我发表了一篇关于TLS协议配置被我钻了空子,经过第三方合作伙伴验证,针对此TLS协议存在不安全套件,急催速速解决,那么我们本篇开始继续整活!第三方合作伙伴对平台安全严苛要求,我们已连续发版十几次进行处理,在此过程中使得我对安全有了进一步认识,具体认识则是在技术解决方案和密码学盲点两方面。下面我们来了解两个方面,可能没有完全深入,至少对作为开发者的我们而言,应已基本足够

.NET Core Cipher(套件)配置

如果没有项目上的苛刻要求,我断然也就无法在此方面展开研究和实践。本文具以.NET 5为例,只不过针对.NET Core 3或3.1通过工具扫描出的协议套件结果略有所差异,但不影响我们对安全套件的配置,我们使用OpenSSL生成自签名证书,后续我会发表文章讲解OpenSSL自签名证书等等

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
    {
        listenOptions.UseHttps("ssl.pfx", "123456", adapterOptions =>
        {
          adapterOptions.SslProtocols = SslProtocols.Tls12;
        });
    });
});

HTTPS结合TLS 协议1.0或1.1不安全,所以TLS协议需使用1.2+,这里我们如上述代码使用版本1.2,接下来我们将其部署在Linux上,然后安装nmap,通过nmap工具扫描(至于nmap是什么,可自行了解)

通过nmap扫描指定端口号并枚举其支持TLS 套件需要注意一点,我们可能搜罗出来大多数文章的命令结果一扫,压根没有结果,其实nmap只对指定端口扫描才有效(比如443等等),比如使用如下命令无效果

nmap --script ssl-enum-ciphers localhost -p 8000

若是其他端口,可使用如下命令进行扫描

nmap --script +ssl-enum-ciphers localhost -p 8000

最终我们扫描出来的结果如下:

AES-CBC模式在中SSL或者TLS中存在一些已知的安全漏洞,如BEAST攻击、Lucky 13攻击等,虽然TLS1.1、TLS1.2未受到BEAST攻击的影响、Lucky 13(影响涉及到TLS1.1/1.2)攻击也在Openssl等知名加密算法库得到了修复,但这些漏洞均暴漏出CBC模式在SSL/TLS协议实现时容易引入安全漏洞,HTTP/2中也明确将CBC模式加密套件列为黑名单

上述是在.NET 5 TLS 1.2默认行为,但第三方规定禁止使用AES-CBC即使扫描出来的套件强度为A,并给定了其支持的安全套件

在上述配置通过路径读取文件和使用密码启用HTTPS重载方法中,如下

public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string password, Action<HttpsConnectionAdapterOptions> configureOptions);

最后可针对连接做配置,在该类中有如下属性

public Action<ConnectionContext, SslServerAuthenticationOptions> OnAuthenticate { get; set; }

该输入第二个类参数里面,有针对套件的配置,如下(我也是结合github找了老半天才翻到)

所以最终我们配置支持的安全套件如下(诸多套件,闻所未闻,不打紧,下面会归纳总结):

webBuilder.ConfigureKestrel(serverOptions =>
{
  serverOptions.Listen(IPAddress.Any, 8000, listenOptions =>
  {
    listenOptions.UseHttps("ssl.pfx", "123456", adapterOptions =>
    {
      adapterOptions.SslProtocols = SslProtocols.Tls12;

      adapterOptions.OnAuthenticate = (connectionContext, authenticationOptions) =>
      {
        var ciphers = new List<TlsCipherSuite>()
        {
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
          TlsCipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
          TlsCipherSuite.TLS_PSK_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_PSK_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_PSK_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_PSK_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
          TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
        };

        authenticationOptions.EnabledSslProtocols = SslProtocols.Tls12;
        authenticationOptions.CipherSuitesPolicy = new CipherSuitesPolicy(ciphers);
      };
    });
  });
});

咳咳,到这里为止,我们满心欢喜,是不是就这样愉快结束了?接触到一个新的点时,一定一定要先看下解释,别一顿操作后不好使,耗费时间和劳力而全是无用功,最终才发现问题症结点所在,这也是我最近才有深刻体会

据我初步的了解应该是在.NET Core 3.0+才开始支持配置OpenSSL的套件,且版本必须是1.1.1+,但不支持Windows,仅支持Linux或OSX!同时在.NET Core 3.1以下版本默认协议是1.1或1.2,但在.NET 5默认协议变更为了1.3,其支持套件配置也与OpenSSL配置也有了一定关联。见链接《 https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0

当接触到某一知识点时(比如TLS),我认为稍微看下更高版本对此方面是否有变更很有必要,假设我们不知道.NET 5+默认变更为了1.3,当进行版本升级过后,如果第三方对接此前使用的是TLS 1.2,那么数据对接就会断层,业务必将受影响。仅我个人建议,至于其他我就管不到了~~~基于上述所述,若是在Windows开发环境,势必要在对应基础上判断操作系统

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
  ......
}

最终我们扫描结果如下,已将AES-CBC不安全套件给去除,完全满足安全要求

密码学基础

密码套件所用到的算法大致有四种:对称加密算法、非对称密钥交换算法、数字签名算法(DSA)、可选的基于散列的消息身份验证代码(HMAC)

说白了,密码学套件就是一组算法,开启HTTPS和TLS并协同使用密码学套件,才能使得传输更加安全

我们知道算法都是公开的,唯一不同的是算法依赖于密钥,只有对接双方知道,从而保护加密过后的密文而不能被窥探,那么接下来我们拆分讲解上述几种算法

非对称加密算法,说白了就是一方拥有公钥,另一方拥有私钥,它是浏览器和服务器开始时通过TLS握手时协商作为加密套件使用,至于HTTPS的其余部分可使用商定的密码套件进行使用

最初通过TLS握手时所使用的最主要的三种算法是:

DHE:Diffie-Hellman Ephemeral (密钥交换算法)

RSA:以其发明者Rivest-Shamir-Adleman的名字而命名

ECDHE:Elliptic-curve Diffie–Hellman(椭圆曲线交换:翻译而来)

对称算法,说白了就是双方都知道的单一密钥,相比较非对称加密算法而言,它的计算速度更快,但并不适用于作为Web证书,因为浏览器和服务器不知道也完全不信任彼此,因此无法共享密钥,然而,在进行初始握手协议之后,再使用对称加密算法非常合适即创建一个共享的密钥以在HTTPS通信的其他期间使用

最常见的四种对称加密算法是:

AES:Advanced Encryption Standard

AES-GCM:AES Galois/Counter

AES-CCM:AES Counter with CBC-MAC(密码块链接消息认证码)

ChaCha20:又名Salsa20

密码强度是一种安全度量,加密文本在遭受攻击时有多安全?算法的强度与密钥的长度相关,所以更长的密钥更强大,密钥长度表示为位数,常用的值为128 和 256,对称算法由它们的首字母缩略词和它们的密钥长度来标识,比如像 AES128 或 AES256。

说了这么一大堆,好像就了解到几个算法名称,要是脱离本文的内容确实仅在脑后留下些许印象,那么接下来我们结合配置的密码套件来进行实践理解,我们拿上述配置的第一个密码套件来进行阐述,其余同理,如下

 TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256

上述TlsCipherSuite是一个枚举,我们仅看枚举内容,将TLS去掉剩余则是DHE_RSA_WITH_AES_128_GCM_SHA256

我们再结合上述阐述的非对称和对称加密算法将其拆分,则是DHE、RSA、AES-128-GCM

从最终拆分的结果来看,实际上HTTPS使用了一对算法而非单一算法,其中一种非对称加密DHE和RSA用于初始期间TLS握手,而另一种对称加密算法AES-128-GCM用于数据传输期间的加密和解密!如此一来对于HTTPS使用对称和非对称加密算法则形成多种组合

但简单地选择一对非对称/对称算法不足以完全识别密码套件,所以还需要指定用于确保身份验证和完整性的规则。比如通过LetsEncrypt证书机构来颁发证书。更深层次的密码学东东这里就不再展开了,我也就探究于此

.NET Core中配置的密码套件与OpenSSL所支持套件吻合,如下截图一部分:

那么问题来了,上述我们配置了多种套件,结果通过工具扫描时有三种被支持的套件被扫描出,浏览器和服务器初次进行TLS握手时,到底选择哪一种呢?那么我们接下来通过访问并查看Web证书能否找到蛛丝马迹,事先声明:以下部分内容为我个人推测,至于理论上是否如真如我所言未可知

通过查看证书反推,我们通过OpenSSL创建自签名证书所使用的是RSA加sha256算法,然后再看如下图可知所使用最终套件

结合这二者是不是就可以说明所使用的密码套件对应OpenSSL配置则是:ECDHE-RSA-AES128-GCM-SHA256,也就是对应通过nmap工具扫描出来支持的三种套件中的第一个

ECDHE-RSA-AES128-GCM-SHA256结论由上述证书和安全查看而言,因为是sha256RSA所以密码套件最后则是SHA256,果真是如此,我能否自圆其说?要是我们将SHA256套件删除,是不是页面就不支持,Web页面无法访问呢?

经过上述修改和通过nmap扫描出结果如下:

看来与证书上所言算法无关,最终回到我通过自创建证书命令

openssl genrsa -out ca-key.key 3072

只能匹配到所使用RSA非对称加密算法,OpenSSL可指定加密方式

但当我指定aes128或其他位时,其模式其实是CBC而非GCM

最终查看是否支持GCM

openssl aes-256-gcm

其实OpenSSL支持GCM,只不过不能用过命令行来进行操作,具体原因官方有解释。分析了这么多,貌似没啥用,目前大致能确定的是: 密码套件的支持还是需要浏览器和服务器来进行握手协商

比如在谷歌浏览器中就可设置是否启用TLS 1.3

到目前为止,我们大致知道了HTTPS所使用的是一组算法,握手期间用非对称加密算法,数据传输期间用对称加密算法!那么整个期间,猜测是结合生成的证书,然后遍历配置套件进行握手,握手成功后再进行数据传输

大致握手和数据传输过程,简单描述如下:

客户端向服务端发送消息,我使用TLS 1.2中的ECDHE-RSA-AES128-GCM-SHA256以及还有其他套件,你能处理吗?服务端向客户端回复消息,ECDHE-RSA-AES128-GCM-SHA256能处理,然后向客户端发出公钥证书。客户端向服务端回复消息,证书是合法的,客户端生成密钥6p7vUjFz紧接着使用服务端的公钥证书进行加密,通知服务端以后数据传输用指定的密钥即共享密钥

当自定义配置所支持套件时,需要额外注意一点的是,千万别同时指定TLS 1.2和TLS 1.3

总结

啊,又到了总结的地方,貌似没啥可总结的,具体相关了解到的或体会到的已在文中有阐述,我们下节再会