我有一台服务器1.1.1.1服务于几个主机A.com和B.com。 所以每个域都有自己的证书。
当我使用基于主机的网址时:
URL url = new URL("https://B.com/23o8PS");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
Certificate[] certificates = ((HttpsURLConnection) conn).getServerCertificates();
System.out.println(certificates.length + " " + certificates[0].toString());
我可以成功获得B.com的证书。但是,如果我使用基于IP的URL(通过添加主机字段来指示主机),如下所示:
URL url = new URL("https://1.1.1.1/23o8PS");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Host", "B.com");
conn.connect();
Certificate[] certificates = ((HttpsURLConnection) conn).getServerCertificates();
System.out.println(certificates[0].toString());
我刚收到A.com的证书,就好像它是从服务器返回的默认证书一样。这似乎无法根据HttpURLConnection的HOST HEADER字段设置SNI。
有任何建议我如何处理这种情况?
答案 0 :(得分:0)
Server Name Identification (SNI)是TLS握手的一部分。它在一开始就通过,用于确定使用哪个服务器证书。该握手在发送任何HTTP请求之前发生 - 如果没有,则HTTP请求将以未加密的方式发送。
根据RFC 4366:
3.1。服务器名称指示
TLS没有为客户端提供一种机制来告诉服务器它正在联系的服务器的名称。客户可能希望提供此信息以促进与在单个底层网络地址上托管多个“虚拟”服务器的服务器的安全连接。
...
目前,唯一支持的服务器名称是DNS主机名;但是,这并不意味着TLS对DNS的任何依赖性,并且将来可能会添加其他名称类型(通过更新此文档的RFC)。 TLS可以将提供的服务器名称视为不透明数据,并将名称和类型传递给应用程序。
“HostName”包含客户端所理解的服务器的完全限定DNS主机名。主机名使用UTF-8编码[UTF8]表示为字节字符串,没有尾随点。
...
“HostName”中不允许使用文字IPv4和IPv6地址。
您不能将IP地址用于SNI。
这是有道理的 - SNI的全部目的是在单个IP地址上支持多个虚拟主机。
答案 1 :(得分:0)
我想要的是为请求而不是DNS指定IP。
所以我找到了解决这个问题的另一种方法。
我自定义了一个SSLSocketFactory,覆盖了 createSocket 方法并指定了 SSLSocket 的IP地址。这也适用于具有多个域的单个IP 场景。