为什么安卓会获取错误的SSL证书?(一个服务器,两个域名)

18

我有两个域名:foo.netbar.com。它们都有 SSL 证书,在所有桌面和移动浏览器中都能正常工作。它们托管于一个配置有 Nginx 的同一台服务器上。

然而,当我从本机 Android 应用程序中向域名发出请求时,它会以某种方式从错误的域名获取证书!这导致了一个 IO 异常:

request = new HttpPost("https://foo.net/api/v1/baz");
request.setHeader("Authorization", "user:pass");
response = httpClient.execute(request);

javax.net.ssl.SSLException: 证书中的主机名与<foo.net> != <bar.com> OR <bar.com> OR <www.bar.com> 不匹配

什么会导致 Android/Java 尝试使用来自bar.com 的证书,而其他措施似乎表明服务器已正确配置?在 Nginx 访问或错误日志中没有任何内容。我的 Android 项目中也没有提到bar.com

编辑:我不确定原因是什么,但似乎服务器正在使用bar.com的证书,用于服务器IP https://198.245.xx.xxx

3个回答

29
这个问题最可能的原因是服务器使用Server Name Indication来选择要发送的证书。如果客户端不支持SNI,则服务器无法在SSL/TLS握手期间(在发送任何HTTP流量之前)选择要发送的证书。当您想在同一IP地址和端口上使用多个证书时,需要SNI,但并非所有客户端都支持它(尤其是Windows XP上的任何版本的IE和许多移动浏览器)。
您还可以明显地使用Apache HTTP客户端库(而不是HttpsURLConnection,其中有些Android版本可以支持SNI。 Apache HTTP客户端库对SNI的支持相当新,肯定还没有进入Android堆栈。
您可能会发现本文中描述的解决方法有用(尽管似乎仅适用于Android 4.2+)。

另外两个选择:

  • 如果您控制服务器,则为每个主机使用不同的IP地址(以避免需要SNI)
  • 使用另一个HTTP客户端库(例如HttpsURLConnection)。

4

一种针对Apache的解决方案,更像是一个技巧:SSL证书是基于/etc/apache2/sites-enabled中的虚拟主机名加载的。因此,为了欺骗这个检查,请确保有问题的证书首先被加载(请记住,虚拟主机是按名称加载的)。


3
由于时间有限,我无法执行上面回答中建议的更改,因此已经没有太多希望了。您的建议是最后一个拯救今天局面的良策,并且起到了作用。将来我会请你喝一杯! - VH-NZZ

0

看起来foo.net的证书配置有误,正在使用与bar.com相同的主机名

尝试在foo.net上运行在线证书验证工具,例如https://www.digicert.com/help/,以确保。

我认为您需要使用正确的主机名重新生成foo.net的证书,或重新配置ngix以确保nginx为正确的主机提供正确的证书。


Digicert帮助工具显示两个域名的所有内容都是绿色的。"恭喜!证书已正确安装。" - hughes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接