There’s one big picture problem here: When you get an authentication challenge you don’t care about, you must resolve it using .performDefaultHandling
. Using .cancelAuthenticationChallenge
will result in the connection failing unnecessarily if the system sends you a new type of challenge. For an outline of how to set up your challenge handler correctly, see the first snippet in TLS For Accessory Developers
Beyond that, your code to evaluate trust — the code equivalent to shouldAllowHTTPSConnection(trust:)
in my snippet — has problems big and small. Let’s start with the small ones:
-
It’s not entirely clear why you’re calling SecTrustEvaluate
up front. That’s always going to succeed and you never look at the trust result (secreult
).
-
data
and size
are unnecessary. You can write this:
let certificateOne = SecCertificateCopyData(serverCertificate) as Data
-
It’s better to use URLs rather than paths.
-
And Data
rather than NSData
.
-
There’s no point checking filePath
for nil
. The only reason that can happen is if your app is built incorrectly, so you might as well force unwrap and learn about the problem early.
The big one is that you’re not actually increasing security. You create certificateOne
and certificateTwo
but you never use them. What you want is something like this:
func shouldAllowHTTPSConnection(chain: [SecCertificate]) async throws -> Bool {
let anchor = Bundle.main.certificateNamed("phillips-hue-cert")!
let policy = SecPolicyCreateBasicX509()
let trust = try secCall { SecTrustCreateWithCertificates(chain as NSArray, policy, $0) }
let err = SecTrustSetAnchorCertificates(trust, [anchor] as NSArray)
guard err == errSecSuccess else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil) }
return SecTrustEvaluateWithError(trust, nil)
}
func shouldAllowHTTPSConnection(trust: SecTrust) async -> Bool {
guard let chain = SecTrustCopyCertificateChain(trust) as? [SecCertificate] else { return false }
do {
return try await shouldAllowHTTPSConnection(chain: chain)
} catch {
return false
}
}
This allows the connection if and only if the server has a certificate that was issued by the phillips-hue-cert
root. The use of the basic X.509 policy means that it ignores any name info in the server’s certificate.
This code relies on two helpers:
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
IMPORTANT The following requires the certificate to be in DER format. You can convert your PEM to DER using the openssl
command-line tool.
extension Bundle {
func certificateNamed(_ name: String) -> SecCertificate? {
guard
let certURL = self.url(forResource: name, withExtension: "cer"),
let certData = try? Data(contentsOf: certURL),
let cert = SecCertificateCreateWithData(nil, certData as NSData)
else {
return nil
}
return cert
}
}