【发布时间】:2015-06-14 20:56:29
【问题描述】:
我一直在调查这个问题,发现了一些有趣的东西。 如果我使用一个服务器密钥库,它将具有公用名称的服务器证书存储为 real domain 来建立与服务器的连接,它工作正常,但是如果我使用 ip address 代替通用名称不起作用,但只是在 android 设备自制应用程序中(不是桌面浏览器或 android 设备中的浏览器应用程序)。注意我使用 openssl 创建这两个证书/密钥库。
事实证明这个异常是主机名未验证
但奇怪的是桌面或安卓设备的浏览器都很好
经过调查,我发现实际上我们可以构建自己的主机名验证器,它可以为主机名添加异常,但是android的默认验证器是如何工作的?它必须是一些代码跳过ip地址作为通用名称并返回false。
我查看了okhttp的源代码发现这行代码抛出了异常
但我找不到自定义主机名验证器的代码。
谁能给我一些提示?
谢谢~
更新:: 在我在 android studio 中调试后,在运行时它实际上是 OkHostnameVerifier
它检查主机名是否是IP地址,如果是则检查证书中的所有主题备用名称,如果找到匹配则返回true,反之亦然。
private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) {
for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) {
if (ipAddress.equalsIgnoreCase(altName)) {
return true;
}
}
return false;
}
【问题讨论】:
-
您不应使用
CN作为主机名。 如果您这样做了,那么主机名必须在CN和SAN中。您应该在CN中添加一个友好名称,因为它会显示给用户。无法避免将主机名(或 IP 地址)放入SAN。详情请参阅How to create a self-signed certificate with openssl?(包括对各种标准的引用) -
另外,IP 地址的使用正在被反对。将来,它可能会引起麻烦。例如,您不能将 IP 地址用于HTTP Strict Transport Security (HSTS),也不能将它们用于Host Public Key Pinning with Overrides (HPKP)。
-
如果存在 dNSName 类型的 subjectAltName 扩展,则必须将其用作身份。否则,必须使用证书主题字段中的(最具体的)通用名称字段。尽管使用通用名称是现有的做法,但它已被弃用,并且鼓励证书颁发机构使用 dNSName 代替。 [...] 在某些情况下,URI 被指定为 IP 地址而不是主机名。在这种情况下,iPAddress subjectAltName 必须出现在证书中,并且必须与 URI 中的 IP 完全匹配。
标签: android ssl https openssl okhttp