SSL:Android 可能无法验证主机名,定义密码套件

信息安全 tls 安卓 密码
2021-08-17 04:49:00

我正在开发一个 Android 应用程序,它将一些 HTTPS 请求发送到我们自己的 Web 服务器(Linux、Nginx、来自“Let's Encrypt”的 SSL zertificate)。

这些请求在我的测试设备(Android 4.2.2 和 Android 6.0.1)上运行良好,但在另一台设备(5.1.1)上,请求引发了异常:

无法验证主机名 [my_domain]

在我的浏览器中查看证书,它似乎工作正常,使用以下连接:

TLS 1.2 AES_128_GCM ECDHE_RSA

在网上搜索任何帮助后,我终于尝试应用自定义 SSLSocketFactory,它强制客户端使用密码套件 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(我的其他测试设备已使用它,该请求在该设备上有效)。

长话短说:然后该请求在任何设备上都有效,这表明连接不成功是客户端的错误。

所以现在我的问题:

  • 为什么 SSL 验证在某些 Android 设备上有效,而在某些设备上无效?

  • 为什么异常说无法验证主机名,而证书有效并且证书中所述的主机名是正确的?

  • 将请求绑定到一个特定的密码套件是否不好?(查看这些列表,给定的密码套件应该可以在所有需要的 Android 设备(4.1.0 及更高版本)上使用)

我是 SSL 的新手,期待任何可以帮助我理解这一点的答案。

2个回答

有关 TLS(又名 SSL)的一些背景知识 - 当您尝试建立从客户端到服务器的安全连接时,服务器将向客户端发送其证书以向客户端证明其身份,以及它支持的密码套件列表。客户端通过确保它连接到的域是证书中规定的域来验证身份,并且该证书由有效的证书颁发机构签名。完成此操作后,客户端和服务器建立一些共享密钥,从中派生密钥并就密码套件达成一致。

您可能会注意到上面的密码套件(例如 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)是在建立服务器身份后协商的。这意味着如果在验证主机名时出现问题,它将在服务器向客户端发送其证书时发生,而不是在协商密码套件时发生。换句话说,此错误不应与服务器和客户端同意的密码套件相关联,因为它会在密码套件达成一致之前发生。

我的直觉是,这可能是 Android 上使用的 SSL 库向您提供的误导性异常消息。我不熟悉 Android 5“开箱即用”的 SSL 库附带的密码套件,但也许当服务器向客户端发送它支持的密码列表时,客户端看不到它支持的任何密码,这级联到一些误导性的异常逻辑。我建议查看您的服务器配置为支持哪些密码套件,并查看有关支持的密码套件的 Android 5 文档。鉴于您所说的,一件事似乎是正确的,您的证书不是这里的问题。

  • 签署证书的 CA 不是某些 android 证书存储,因此它不会验证。查看一些如何更新它的方法
  • 仅当可以建立完全信任路径(your_cert -> intermediate_CA1 -> middle_CA2.. -> 根 CA)时,证书才被视为有效(可以有 0 个或多个中间 CA,但根 CA 必须存在于该 android 版本证书存储中) . 您应该使用ssllabs(针对“A”且没有橙色/红色警告)和/或htbridge检查服务器端是否存在任何问题(例如,不发送中间证书)请注意,设备会缓存中间证书,因此即使您不更改密码,您的应用程序也可能会正常工作。或者它可能是特定密码在那个 android 版本中有错误。
  • 这是坏的。建议会不时更改,并非所有人都会一直更新他们的应用程序。你应该把它留给自动装置。如果需要最佳 SSL 密码安全性,则应在服务器端自动推荐/要求,而不是在客户端。它还有助于根据 android 版本提供不同级别的现有支持。简而言之:您应该只在您的服务器上更改首选密码顺序,并且在 android 应用程序上使用默认值。