近期收到同事反馈,在C#程序中通过HTTPClient请求一个HTTPS的地址时,在本地开发环境和测试环境均能正常执行,而部署到生产环境后发生异常且稳定复现,异常提示为:【请求被中止: 未能创建 SSL/TLS 安全通道 】,而且在生产环境用浏览器访问是没问题的。
自然的,根据异常信息先进行一圈Google(不用百度),基本都是在说ServicePointManager.SecurityProtocol的赋值,但是本次的程序中已经配置了全部协议。在了解了大概背景之后,基本断定该故障与源码本身的关系不大,仔细检查源码后也确实没有问题。大家也能看得出,很明显的环境不同导致的故障,那么宿主环境的差异就是我们优先要排查分析的线索。
1、关于HTTP、HTTPS、TLS的关系:HTTPS连接是由HTTP协议与TLS协议共同完成。
2、建立HTTPS连接不仅需要Client与Server双方的TLS协议版本号兼容,还需要Cipher Suites(密码套件)兼容。关于什么是Cipher Suites可以自行查阅资料,本文不详细展开说明。Cipher Suites的样子如图所示:
有了以上理论支撑,下面就开始对本次案例的站点和运行环境进行一一分析;
首先,通过在线工具验证目标站点SiteA自身的HTTPS连接功能是否OK:
截图报告显示SiteA支持TLS1.2、TLS1.3协议,同时也可以看到对应的密码套件。说明该站点自身配置没问题,这也符合同事的测试结果。
根据前面提到的知识点,下一步就需要判断连接的Client端(即C#应用)所支持的协议和密码套件。先看在程序中配置的是支持TLS全协议:
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | (SecurityProtocolType) 12288;
其次查看运行时部署的.NET Framework 4.8对TLS的兼容情况,如下图所示,环境对TLS1.2、TLS1.3均支持。
现在TLS协议看起来是兼容的,那么该如何查看C#应用所使用的密码套件?这里用到一个工具:IISCrypto.exe
生产环境服务器MA1的密码套件如下图所示。经过仔细对比,该OS没有与目标站点SiteA匹配的密码套件,因此无法建立连接,符合实际情况。
测试环境服务器T1的密码套件如下图所示。该OS中有2组和目标站点SiteA的TLS1.2密码套件匹配,因此可以建立链接,符合实际情况。
本地临时VMware虚拟机VM1的密码套件如下图所示,该OS中有多组和目标站点SiteA的TLS1.2密码套件匹配,因此可以建立链接,符合实际情况。
经过上述对比后,基本可以判定是由于生产环境的服务器密码套件与目标站点不匹配导致。
解决方案比较简单,使用IISCrypt工具把缺少的密码套件勾选,并择机重启服务器生效即可。
根据前面对几台服务器的密码套件的分析,兼容的密码套件都是最多是TLS1.2版本的,那是否真可以创建TLS1.3的连接呢?所幸我本地开发环境比较新,是Windows11,经过查看密码套件可以兼容TLS1.3:
使用工具在只选择TLS1.3协议的情况下,验证是可以成功请求的:
下图是生产环境服务器MA1上的Edge浏览器的密码套件支持情况。
通过该浏览器直接请求目标站点SiteA,不仅可以请求成功而且还是走的TLS1.3协议。
本文针对实际开发中遇到怪异现象,结合HTTPS的理论知识,透彻的分析了故障原因并最终解决。本文中还提到了几个实用的工具,可以更方便的查看相关细节。最后针对生产服务器镜像默认的密码套件问题,后续会继续与运维同事反馈并更新。