【问题标题】:Using ip address for common name in server certificate does not work in Android?在服务器证书中使用 IP 地址作为通用名称在 Android 中不起作用?
【发布时间】: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 作为主机名。 如果您这样做了,那么主机名必须在CNSAN 中。您应该在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


【解决方案1】:

如果我使用存储服务器证书的服务器密钥库,其公用名是真实域,用于与服务器建立连接,它可以正常工作,但是如果我使用 ip 地址代替公用名,它就不起作用,

它应该是这样工作的。 IP 地址必须作为 IP 类型的主题替代名称给出。不幸的是,不同的浏览器以不同的方式处理这个问题,而且通常与标准背道而驰。有些接受通用名称的 IP,有些则不接受。有些人希望地址作为主题替代部分中的 DNS 条目而不是 IP 条目。因此,为了安全起见,您应该使用 IP 和 DNS 类型的主题备用名称。

我们可以构建自己的主机名验证器,它可以为主机名添加异常

不要这样做。如果您忽略主机名,则验证将减少为仅检查信任链,这意味着任何由受信任的 CA 签名的证书都可用于对任何其他主机进行透明的中间人攻击。即使您仅禁用 IP 地址的名称检查,一旦用户通过 IP 访问站点,仍然可以使用任何有效证书。

【讨论】:

  • 感谢您的解释!顺便说一句,你知道 okhttp 在哪里处理这个主机验证器?我找不到处理这部分的代码。
  • @Qing:我对 OkHttp 几乎一无所知,但简短搜索会返回 OkHTTPClient.setHostnameVerifier
  • 我只是用android studio调试源,正确的验证器应该是:com.android.okhttp.internal.tls.OkHostnameVerifier,我可以看到主机名是ip地址,它会做以下检查:
  • for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) { if (ipAddress.equalsIgnoreCase(altName)) { return true; } } 返回错误;
  • 我想就像你说的检查 ip 的主题备用名称,如果不是它返回 false!
猜你喜欢
  • 1970-01-01
  • 2019-09-11
  • 1970-01-01
  • 2012-02-17
  • 1970-01-01
  • 2016-02-20
  • 2014-04-03
  • 1970-01-01
  • 2016-02-07
相关资源
最近更新 更多