目标:
使用URLProtocol测试SSL固定。
问题:
无法以预期的方式对URLProtectionSpace进行子类化。永远不会调用服务器信任属性,并且即使调用了自定义类的初始化程序,alamofire auth回调也仅接收URLProtectionSpace类类型而不是我的类。
配置: [使用Alamofire]
let sessionConfiguration: URLSessionConfiguration = .default
sessionConfiguration.protocolClasses?.insert(BaseURLProtocol.self, at: 0)
let sessionManager = AlamofireSessionBuilderImpl(configuration: sessionConfiguration).default
// overriding the auth challenge in Alamofire in order to test what is being called
sessionManager.delegate.sessionDidReceiveChallengeWithCompletion = { session, challenge, completionHandler in
let protectionSpace = challenge.protectionSpace
guard protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
protectionSpace.host.contains("myDummyURL.com") else {
// otherwise it means a different challenge is encountered and we are only interested in certificate validation
completionHandler(.performDefaultHandling, nil)
return
}
guard let serverTrust = protectionSpace.serverTrust else {
completionHandler(.performDefaultHandling, nil)
return
}
guard let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
return completionHandler(.cancelAuthenticationChallenge, nil)
}
let serverCertificateData = SecCertificateCopyData(serverCertificate) as Data
// cannot find the local certificate
guard let localCertPath = Bundle.main.path(forResource: "cert", ofType: "der"),
let localCertificateData = NSData(contentsOfFile: localCertPath) else {
return completionHandler(.cancelAuthenticationChallenge, nil)
}
guard localCertificateData.isEqual(to: serverCertificateData) else {
// the certificate received from the server is invalid
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
}
BaseURLProtocol定义:
class BaseURLProtocol: URLProtocol {
override class func canInit(with request: URLRequest) -> Bool {
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
debugPrint("--- request loading \(request)")
guard request.url?.host?.contains("myDummyURL.com") ?? false else {
debugPrint("--- caught untargetted request --- skipping ---host is \(request.url?.host)")
return
}
let protectionSpace = CertificatePinningMockURLProtectionSpace(host: "https://myDummyURL.com", port: 443, protocol: nil, realm: nil, authenticationMethod: NSURLAuthenticationMethodServerTrust)
let challenge = URLAuthenticationChallenge(protectionSpace: protectionSpace, proposedCredential: nil, previousFailureCount: 0, failureResponse: nil, error: nil, sender: self)
client?.urlProtocol(self, didReceive: challenge)
}
}
CertificatePinningMockURLProtectionSpace: [使用Alamofire ServerTrustPolicy获取证书]
-serverTrust属性永远不会被调用。我还重写了URLProtectionSpace的所有其他属性,除了调用了init外,什么都没有。
class CertificatePinningMockURLProtectionSpace: URLProtectionSpace { private static let expectedHost = "myDummyURL.com" override init(host: String, port: Int, protocol: String?, realm: String?, authenticationMethod: String?) { debugPrint("--- super init will be called") super.init(host: host, port: port, protocol: `protocol`, realm: realm, authenticationMethod: authenticationMethod) } override var serverTrust: SecTrust? { guard let certificate = ServerTrustPolicy.certificates(in: .main).first else { return nil } let policy: SecPolicy = SecPolicyCreateSSL(true, CertificatePinningMockURLProtectionSpace.expectedHost as CFString) var serverTrust: SecTrust? SecTrustCreateWithCertificates(certificate, policy, &serverTrust) return serverTrust }
}
测试语句:
sessionManager.request("https://myDummyURL.com").responseString(completionHandler: { response in debugPrint("--- response is \(response)") done() })
URLProtectionSpace是否可以成功重写并作为模拟提供给URLProtocol中的URLProtocolClient?
目标:使用URLProtocol测试SSL固定。问题:无法以预期方式子类化URLProtectionSpace。永远不会调用服务器信任属性,并且仅调用alamofire auth回调...
许多这些源自Core Foundation的“类”都非常耐子类化,因此这也就不足为奇了。它可能基本上只是一个带有引擎盖下的魔术胶的结构。 :-)