我的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))

10-08 03:22