如何以编程方式创建URLAuthenticationChallenge时,如何获取非零serverTrust值?

时间:2017-07-12 19:47:16

标签: ios iphone swift

我的Swift iOS应用程序中有公钥密码代码,效果很好。

我现在正致力于构建单元测试,测试公钥密码,而无需网络连接/实时服务器。我有这个几乎工作,但我无法弄清楚如何以编程方式创建一个具有非零服务器信任的URLAuthenticationChallenge?所有Apple文档都声明,如果您的身份验证方法是NSURLAuthenticationMethodServerTrust,那么这应该是非零的。我正在使用从本地计算机生成的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))
        }
    }
}

2 个答案:

答案 0 :(得分:0)

您始终可以将didReceive challenge - 方法从您正在测试的类中移开,并将其与某些协议通信相关联。然后在需要时调用它的方法。例如:

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

答案 1 :(得分:0)

我意识到这是一个比较老的问题,但是今天在为SSL固定代码编写测试时遇到了同样的问题。我通过子类化URLProtectionSpace

来解决它
private class TestURLProtectionSpace: URLProtectionSpace {
    var internalServerTrust: SecTrust?
    override var serverTrust: SecTrust? {
        return internalServerTrust
    }
}

let protectionSpace = TestURLProtectionSpace(
        host: "myhost.com",
        port: 443,
        protocol: "https",
        realm: nil,
        authenticationMethod: NSURLAuthenticationMethodServerTrust
)
protectionSpace.internalServerTrust = serverTrust