我的Swift iOS应用程序中有公用 key 固定代码,效果很好。
我现在正在构建单元测试,该单元测试无需网络连接/实时服务器即可测试公钥固定代码。我几乎可以正常工作了,但是我不知道如何以编程方式创建具有非null serverTrust的URLAuthenticationChallenge? Apple的所有文档都声明,如果您的身份验证方法是NSURLAuthenticationMethodServerTrust,则此字段应为非null。在下面的示例中,我使用从本地计算机生成的p12和cer文件来构建URLCredential。不管我做什么,challenge.protectionSpace.serverTrust始终会归零。
let protectionSpace = URLProtectionSpace(host: "mockSession",
port: 0,
protocol: "https",
realm: nil,
authenticationMethod: NSURLAuthenticationMethodServerTrust)
var urlCredential:URLCredential?
if let p12Data = try? Data(contentsOf: URL(fileURLWithPath: Bundle.init(for: type(of: self)).path(forResource: "cm7justindomnit", ofType: "p12") ?? "")),
let cerData = try? Data(contentsOf: URL(fileURLWithPath: Bundle.init(for: type(of: self)).path(forResource: "cm7justindomnit", ofType: "cer") ?? "")){
let options: NSDictionary = [kSecImportExportPassphrase:"password"]
var items: CFArray?
let _ = SecPKCS12Import(p12Data as CFData, options, &items)
if let items = items {
let objectsData = Data.init(from: CFArrayGetValueAtIndex(items, 0))
let objects = objectsData.toArray(type: CFDictionary.self).first
let secIdentityData = Data.init(from: CFDictionaryGetValue(objects, Unmanaged.passUnretained(kSecImportItemIdentity).toOpaque()))
if let secIdentity = secIdentityData.toArray(type: SecIdentity.self).first {
if let secCertifiate = SecCertificateCreateWithData(kCFAllocatorDefault, cerData as CFData) {
urlCredential = URLCredential(identity: secIdentity, certificates: [secCertifiate], persistence: .forSession)
}
}
}
}
let challenge = URLAuthenticationChallenge(protectionSpace: protectionSpace,
proposedCredential: urlCredential,
previousFailureCount: 0,
failureResponse: nil,
error: nil,
sender: self)
我有一个数据扩展来处理UnsafeBufferPointers。
extension Data {
init<T>(from value: T) {
var value = value
self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
func to<T>(type: T.Type) -> T {
return self.withUnsafeBytes { $0.pointee }
}
func toArray<T>(type: T.Type) -> [T] {
return self.withUnsafeBytes {
[T](UnsafeBufferPointer(start: $0, count: self.count/MemoryLayout<T>.stride))
}
}
}
最佳答案
您始终可以将didReceive challenge
-method从正在测试的类中移开,并将其与某些协议(protocol)通信进行链接。然后仅在需要时调用其方法。例如:
protocol CheckerProtocol {
// or event simplier
func isOkCertificate(_ certificate: ) -> Bool
}
如果您需要使用证书测试某些逻辑,只需将其直接传递给您的检查程序依赖项即可:
if let serverTrust = challenge.protectionSpace.serverTrust {
var secresult = SecTrustResultType(kSecTrustResultInvalid)
let status = SecTrustEvaluate(serverTrust, &secresult)
if status == errSecSuccess {
if let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
if self.checker.isOkCertificate(certificate) { ... }
}
}
}
将检查代码移至
class Checker: CheckerProtocol { ... }
同时在您的测试目标中:
let certificate = // init right/wrong one
let checker = Checker()
XCTAssert[True|False](checker.isOkCertificate(certificate))