2017-07-12 13 views
10

Mam kod PIN do klucza publicznego w mojej aplikacji Swift iOS, która działa świetnie.Jak uzyskać programistyczną wartość niezerowej wartości serweraTrust podczas tworzenia obiektu URLAuthenticationChallenge?

Pracuję teraz nad budowaniem testów jednostkowych, które przetestują kod PIN klucza publicznego bez potrzeby połączenia sieciowego/serwera rzeczywistego. Mam to prawie działa, ale nie mogę dowiedzieć się, jak programowo utworzyć URLAuthenticationChallenge, który ma zerowy serverTrust? Cała dokumentacja Apple stwierdza, że ​​powinno to być zero, jeśli twoja metoda uwierzytelniania to NSURLAuthenticationMethodServerTrust. Używam plików p12 i cer generowanych z mojego komputera lokalnego do budowania URLCredential w poniższym przykładzie. Bez względu na to, co robię, challenge.protectionSpace.serverTrust zawsze wraca do zera.

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) 

Mam rozszerzenie danych do obsługi 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)) 
     } 
    } 
} 

Odpowiedz

0

Zawsze można przenieść didReceive challenge -method od klasy testowanej i połączyć go z jakimś protokołem komunikacyjnym. Następnie wywołaj jego metody, gdy jest to potrzebne. Na przykład:

protocol CheckerProtocol { 
    // or event simplier 
    func isOkCertificate(_ certificate:) -> Bool 
} 

Jeśli trzeba przetestować niektóre logiki z certyfikatem po prostu przekazać go do swojego uzależnienia sprawdzania bezpośrednio:

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) { ... } 
     } 
    } 
} 

Move kod sprawdzania

class Checker: CheckerProtocol { ... } 

Meenwhile w teście target:

let certificate = // init right/wrong one 
let checker = Checker() 
XCTAssert[True|False](checker.isOkCertificate(certificate)) 
+0

T Hank za odpowiedź, ale Twoja sugestia nadal nie pozwala mi przetestować kod wykonujący ekstrakcję certyfikatu i porównanie. Właśnie to próbuję przetestować. Kiedy dojdę do linii, na których wywołuję SecTrustEvaluate i SecTrustGetCertificateAtIndex (nie pokazany jako zastrzeżony kod), potrzebuję niezerowego serweraTrust, aby móc zbadać certyfikaty listkowe. Jeszcze raz dziękuję! –

+0

@JustinDomnitz zaktualizowano. Słuchaj, nie musisz używać tylko certyfikatu, aby połączyć dwie klasy razem. Konieczne jest używanie protokołów, aby można było zastąpić go próbkami w testach jednostkowych. – Astoria

+0

Dzięki za kontynuację. Muszę pomieszać twoją odpowiedź, żeby sprawdzić, czy to mi pomoże. Zaraz zrobię więcej notatek, gdy już to zrobię. W międzyczasie przyznałem ci nagrodę. Dziękuję Ci! –