Handle task-level server trust challenges with the same certificate pin
This commit is contained in:
@@ -9,6 +9,9 @@ final class PinnedDelegate: NSObject, URLSessionDelegate, URLSessionTaskDelegate
|
|||||||
|
|
||||||
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge,
|
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge,
|
||||||
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
#if DEBUG
|
||||||
|
print("MusicBridge TLS: received server authentication challenge")
|
||||||
|
#endif
|
||||||
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
|
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
|
||||||
challenge.protectionSpace.host == connection.endpoint.host,
|
challenge.protectionSpace.host == connection.endpoint.host,
|
||||||
challenge.protectionSpace.port == (connection.endpoint.port ?? 443),
|
challenge.protectionSpace.port == (connection.endpoint.port ?? 443),
|
||||||
@@ -19,18 +22,31 @@ final class PinnedDelegate: NSObject, URLSessionDelegate, URLSessionTaskDelegate
|
|||||||
let digest = SHA256.hash(data: SecCertificateCopyData(leaf) as Data)
|
let digest = SHA256.hash(data: SecCertificateCopyData(leaf) as Data)
|
||||||
.map { String(format: "%02X", $0) }.joined()
|
.map { String(format: "%02X", $0) }.joined()
|
||||||
guard digest == connection.fingerprint else {
|
guard digest == connection.fingerprint else {
|
||||||
|
#if DEBUG
|
||||||
|
print("MusicBridge TLS: fingerprint mismatch")
|
||||||
|
#endif
|
||||||
completionHandler(.cancelAuthenticationChallenge, nil); return
|
completionHandler(.cancelAuthenticationChallenge, nil); return
|
||||||
}
|
}
|
||||||
// Trust only this exact out-of-band certificate, while checking its validity dates.
|
// Trust only this exact out-of-band certificate, while checking its validity dates.
|
||||||
SecTrustSetAnchorCertificates(trust, [leaf] as CFArray)
|
SecTrustSetAnchorCertificates(trust, [leaf] as CFArray)
|
||||||
SecTrustSetAnchorCertificatesOnly(trust, true)
|
SecTrustSetAnchorCertificatesOnly(trust, true)
|
||||||
SecTrustSetPolicies(trust, SecPolicyCreateBasicX509())
|
SecTrustSetPolicies(trust, SecPolicyCreateBasicX509())
|
||||||
guard SecTrustEvaluateWithError(trust, nil) else {
|
var trustError: CFError?
|
||||||
|
guard SecTrustEvaluateWithError(trust, &trustError) else {
|
||||||
|
#if DEBUG
|
||||||
|
print("MusicBridge TLS: pinned certificate rejected: \(String(describing: trustError))")
|
||||||
|
#endif
|
||||||
completionHandler(.cancelAuthenticationChallenge, nil); return
|
completionHandler(.cancelAuthenticationChallenge, nil); return
|
||||||
}
|
}
|
||||||
completionHandler(.useCredential, URLCredential(trust: trust))
|
completionHandler(.useCredential, URLCredential(trust: trust))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func urlSession(_ session: URLSession, task: URLSessionTask,
|
||||||
|
didReceive challenge: URLAuthenticationChallenge,
|
||||||
|
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
urlSession(session, didReceive: challenge, completionHandler: completionHandler)
|
||||||
|
}
|
||||||
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask,
|
func urlSession(_ session: URLSession, task: URLSessionTask,
|
||||||
willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest,
|
willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest,
|
||||||
completionHandler: @escaping (URLRequest?) -> Void) {
|
completionHandler: @escaping (URLRequest?) -> Void) {
|
||||||
|
|||||||
Reference in New Issue
Block a user