Last active
May 4, 2023 09:18
-
-
Save FabP93/3e579ded23e5f58698ec0f88794dc7a4 to your computer and use it in GitHub Desktop.
How to retrieve the SecTrust from the network call and compare it with your own certificate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { | |
guard | |
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, | |
let serverTrust = challenge.protectionSpace.serverTrust | |
else { | |
completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil) | |
return | |
} | |
guard | |
self.validate(trust: serverTrust, with: SecPolicyCreateBasicX509()), | |
let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) | |
else { | |
completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil) | |
return | |
} | |
// Here you can do all addition check that you want on `serverCertificate` for example compare the | |
// public keys or the data with the one pinned in your bundle. | |
// I implemented for you a couple of useful methods that you can use here for your all checks. | |
completionHandler(URLSession.AuthChallengeDisposition.useCredential, | |
URLCredential(trust:serverTrust)) | |
} | |
private func validate(trust: SecTrust, with policy: SecPolicy) -> Bool { | |
let status = SecTrustSetPolicies(trust, policy) | |
guard status == errSecSuccess else { return false } | |
return SecTrustEvaluateWithError(trust, nil) | |
} | |
private func publicKey(for certificate: SecCertificate) -> SecKey? { | |
var publicKey: SecKey? | |
var trust: SecTrust? | |
let trustCreationStatus = SecTrustCreateWithCertificates(certificate, SecPolicyCreateBasicX509(), &trust) | |
if let trust = trust, trustCreationStatus == errSecSuccess { | |
publicKey = SecTrustCopyPublicKey(trust) | |
} | |
return publicKey | |
} | |
private func certificateData(for certificates: [SecCertificate]) -> [Data] { | |
return certificates.map { SecCertificateCopyData($0) as Data } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
By setting the trust policy to
SecPolicyCreateBasicX509
the validation method is doing the check against a 'less' secure policy than the one probably provided in theserverTrust
(SSL policy). There are two options, useSecPolicyCreateSSL()
and provide the hostname manually so a policy that also checks the name for which the server certificate is used, or trust the policy included in the challenge'sserverTrust
.