How to trust self signed certificate programmatically ?

My app is using a self signed certificate and we are planning to use the same in production. To avoid asking user to install the certificate manually, can I add the certificate to the list of trusted anchors using SecTrustSetAnchorCertificates(_: _:). Is this a correct approach ?

Please find the code below:

  func addAnchorToTrust(trust: SecTrust, certificate: SecCertificate) -> SecTrust {
        let array: NSMutableArray = NSMutableArray()
        array.add(certificate)
        SecTrustSetAnchorCertificates(trust, array)
        return trust
    }

    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
        }
        let filePath = Bundle.main.path(forResource: "httpsPublicCertificate", ofType: "cer")

        guard let file = filePath, let savedCertificateData = NSData(contentsOfFile: file), let rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, savedCertificateData) else {
            completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
            return
        }
        let sslInput = addAnchorToTrust(trust: serverTrust, certificate: rootCert)
        var result: SecTrustResultType = SecTrustResultType.unspecified
        let error: OSStatus = SecTrustEvaluate(sslInput, &result)

        if (error != 0){
            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
        }
        let serverCertificateData = SecCertificateCopyData(serverCertificate)
        let serverCertificateDataPtr = CFDataGetBytePtr(serverCertificateData);
        let serverCertificateDataSize = CFDataGetLength(serverCertificateData);
        let serverCertificateNSData = NSData(bytes: serverCertificateDataPtr, length: serverCertificateDataSize)
        if serverCertificateNSData.isEqual(to: savedCertificateData as Data) {
            completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:serverTrust))
            return
        }
    }

    private func validate(trust: SecTrust, with policy: SecPolicy) -> Bool {
        let status = SecTrustSetPolicies(trust, policy)
        guard status == errSecSuccess else { return false }
        return SecTrustEvaluateWithError(trust, nil)

    }
Answered by DTS Engineer in 708950022

The communication is between app and a local accessory

OK, well, that’s a critical tidbit.

The situation with accessories on the local network is a tricky one. It’s something I’ve discussed with developers many times before, so I took the time today to write it up properly. Please have a read through TLS For Accessory Developers and post back here if you have any questions.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

My app is using a self signed [HTTPS] certificate and we are planning to use the same in production.

I strongly recommend against doing that because:

  • HTTPS certificates are cheap and easy to acquire.

  • Overriding HTTPS server trust evaluation is error prone, and errors in your code can result in security vulnerabilities.

  • Many of our high-level APIs, including URLSession which you seem to be using here, require you to apply an App Transport Security exception for this to work. See the NSAppTransportSecurity documentation for details.

    Note If you ship via the App Store, be aware that ATS exceptions may require justification to App Review.

  • Some of our APIs do not let you override HTTPS server trust evaluation at all. If you go down this path and, later on, you decide that you want to use one of those APIs, you’ve kinda painted yourself into a corner.


As to your technical question:

can I add the certificate to the list of trusted anchors using SecTrustSetAnchorCertificates(_: _:).

Yes.

Although the code you posted is far from ideal. I’m happy to help you fix it but, honestly, I’d be much happier if you just deleted it and got yourself a Real Certificate™.

If you want to continue developing your app while your server-side folks sort out the certificate issue, follow the advice in QA1948 HTTPS and Test Servers.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

The communication is between app and a local accessory

OK, well, that’s a critical tidbit.

The situation with accessories on the local network is a tricky one. It’s something I’ve discussed with developers many times before, so I took the time today to write it up properly. Please have a read through TLS For Accessory Developers and post back here if you have any questions.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

How to trust self signed certificate programmatically ?
 
 
Q