Skip to content

Instantly share code, notes, and snippets.

@FabP93
Last active May 4, 2023 09:18
Show Gist options
  • Save FabP93/3e579ded23e5f58698ec0f88794dc7a4 to your computer and use it in GitHub Desktop.
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
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 }
}
}
@pbartolome
Copy link

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 the serverTrust (SSL policy). There are two options, use SecPolicyCreateSSL() 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's serverTrust.

@FabP93
Copy link
Author

FabP93 commented Oct 9, 2020

Thanks @pbartolome, I'll try it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment