我一直在研究这个问题,发现了一些有趣的东西。 如果我使用服务器密钥库存储服务器证书和通信名称为真实域来建立与服务器的连接,它可以正常工作,但如果我使用 IP地址代替通用名称它不起作用,但只是在Android设备自制的应用程序(不是Android设备中的桌面浏览器或浏览器应用程序).noted我用openssl创建这两个证书/密钥库。
,事实证明这个例外是主机名未经验证
但奇怪的是在桌面或Android设备的浏览器中都很好
经过调查我发现实际上我们可以构建自己的主机名验证器,它可以为主机名添加异常,但android的默认验证器是如何工作的?它必须是一些代码,跳过 ip address 作为通用名称并返回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;
}
答案 0 :(得分:5)
如果我使用存储服务器证书的服务器密钥库,其中commomn名称是用于与服务器建立连接的真实域,则它可以正常工作,但是如果我使用ip地址而不是通用名称则不起作用,
它应该如何运作。 IP地址必须作为IP类型的主题替代名称给出。不幸的是,不同的浏览器以不同的方式处理这种情况并且经常违反标准。有些人接受普通名字的知识产权,其他人则不接受。有些人希望地址作为主题备选部分中的DNS条目而不是IP条目。为了安全起见,您应该使用IP和DNS两种类型的主题替代名称。
我们可以构建自己的主机名验证程序,它可以为主机名添加例外
不要这样做。如果忽略主机名,则验证将简化为仅检查信任链,这意味着由可信CA签名的任何证书都可用于针对任何其他主机的透明中间人攻击。即使您仅对IP地址禁用名称检查,一旦用户通过IP访问站点,仍可以使用任何有效证书。