【发布时间】:2017-07-03 11:10:25
【问题描述】:
首先是服务器端: 有一个内部可访问的 apache 服务器,其中包含多个虚拟主机。 对于 http (sans 's'!) 请求,我使用 IP 作为 URL 并在 Host-Header 字段中添加主机名。
效果很好。
但是当我建立 SSL 连接时,我遇到了似乎与 SNI 相关的问题。
我找到了这个Overriding TLS Chain Validation Correctly 并在我的代码中实现了它。
所以我更新了对
的信任func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
但我仍然从服务器获得 400。
更新:
URLSession 确实支持 SNI,但我的问题是我需要添加额外的或更改 SSLHandshake 中的 hostname。
所以首先对服务器做一个简短的描述: 服务器是一个 Apache 服务器,只能通过 IP 访问,但它有多个虚拟主机,可通过主机名在本地使用,但是当我从 iPhone 等外部设备连接到它们时,我必须在 @ 添加虚拟主机的主机名987654326@.
所以我要做的是:我使用基于 IP 的 URL 创建一个 URLRequest,然后添加带有主机名的 HOST Headerfield
if let url = URL("https://192.168.178.54:8890") {
var request = URLRequest(url: url)
request.addValue("foobar:8890", forHTTPHeaderField: "Host")
…
}
这适用于非安全的 http-Requests,但一旦使用 https,服务器就会返回“400 Bad request”并且 http-ssl-error.log 包含以下错误:
[Tue Jul 11 09:35:51 2017] [error] Hostname 192.168.178.54 provided via SNI and hostname foobar provided via HTTP are different
这是因为默认情况下 SSLHandshake 使用 URL 中提供的值,在我的例子中是 IP。
我现在想弄清楚的是如何在 SSLHandshake 中提供不同的主机名,或者提供类似自己的 DNS 解析器的东西,我仍然可以使用基于主机名的 URL,但 iOS 可以直接进行路由。
【问题讨论】:
-
SNI 由 NSURLSession 原生支持。您无需对代码进行任何修改即可使其正常工作。
openssl s_client -connect hostname.com:443对您的服务器有何评价? -
感谢您的评论。我的服务员的回答有点误导。是的,URLSession 支持 SNI,它在正常情况下工作,但我必须更改 SSLHandshake 中的主机名……而这对于 URLSession 是不可能的。我将更新我的 OP 以使其更清晰
-
发布一些关于如何更新挑战的代码。
-
这个问题与
func urlSession(_ session:didReceivechallenge:completionHandler:)无关,它与在 URLSession 启动之前发生的 SSL 握手有关。我打开了一个 TSI 并从 Apple 获得了听起来不太好的信息: API 级别的问题在于,URL 是“主机”由两个不同的系统组件(TLS 引擎和 HTTP 协议实现)检索,但您实际上只能控制其中一个(HTTP 实现)通过 URLProtocol) -
你有没有机会解决这个问题并记得是怎么做到的?
标签: ios swift nsurlsession sni nsurlprotocol