I have public key pinning code in my Swift iOS application that works great.
I'm now working on building unit tests that will test the public key pinning code without the need for network connectivity / real live server. I've got this almost working, but I can't figure out how to create a URLAuthenticationChallenge programmatically that has a non-nil serverTrust? All the Apple documentation states that this should be non-nil if your authentication method is NSURLAuthenticationMethodServerTrust. I'm using the p12 and cer files generated from my local machine to build the URLCredential in the example below. No matter what I do, challenge.protectionSpace.serverTrust always comes back nil.
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)
I have a Data extension to handle the 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))
}
}
}