How to use an Intune-delivered SCEP certificate for mTLS in iOS app using URLSessionDelegate?

I am working on implementing mTLS authentication in my iOS app (Apple Inhouse & intune MAM managed app). The SCEP client certificate is deployed on the device via Intune MDM. When I try accessing the protected endpoint via SFSafariViewController/ASWebAuthenticationSession, the certificate picker appears and the request succeeds. However, from within my app (using URLSessionDelegate), the certificate is not found (errSecItemNotFound).

The didReceive challenge method is called, but my SCEP certificate is not found in the app. The certificate is visible under Settings > Device Management > SCEP Certificate.

  1. How can I make my iOS app access and use the SCEP certificate (installed via Intune MDM) for mTLS requests?
  2. Do I need a special entitlement, keychain access group, or configuration in Intune or Developer account to allow my app to use the certificate?

Here is the sample code I am using:

final class KeychainCertificateDelegate: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate else {
            completionHandler(.performDefaultHandling, nil)
            return
        }
        
        // Get the DNs the server will accept
        guard let expectedDNs = challenge.protectionSpace.distinguishedNames else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        var identityRefs: CFTypeRef? = nil
        let err = SecItemCopyMatching([
            kSecClass: kSecClassIdentity,
            kSecMatchLimit: kSecMatchLimitAll,
            kSecMatchIssuers: expectedDNs,
            kSecReturnRef: true,
        ] as NSDictionary, &identityRefs)
        
        if err != errSecSuccess {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        guard let identities = identityRefs as? [SecIdentity],
              let identity = identities.first
        else {
            print("Identity list is empty")
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        let credential = URLCredential(identity: identity, certificates: nil, persistence: .forSession)
        completionHandler(.useCredential, credential)
    }
}

func perform_mTLSRequest() {
    guard let url = URL(string: "https://sample.com/api/endpoint") else {
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    request.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
    
    let delegate = KeychainCertificateDelegate()
    let session = URLSession(configuration: .ephemeral, delegate: delegate, delegateQueue: nil)
    
    let task = session.dataTask(with: request) { data, response, error in
        guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
            print("Bad response")
            return
        }
        if let data = data {
            print(String(data: data, encoding: .utf8)!)
        }
    }
    task.resume()
}
Answered by Device Management Engineer in 857738022

Using DDM's declarative app configuration, a device management service can provision an identity for an app using SCEP. The app uses ManagedApp framework to receive that identity.

However I think anaspoovalloor is going to run into two problems: First, Intune doesn't currently support declarative app configuration. Second, anaspoovalloor mentioned "MAM". Usually when you discuss MAM in relation to Microsoft device management it means the device isn't actually enrolled via MDM. Instead it's usually just using a Microsoft SDKs and/or the Authenticator app.

So if you want to have an identity provisioned for your app, please request support for declarative app config / ManagedApp framework from Intune, and check whether your target devices are actually enrolled via MDM.

Right. System credentials go into a keychain access group that you don’t have access to. That’s why Safari and SFSafariViewController work, but your code fails.

This has been a problems for a long time [1] but we finally have a solution, namely, the ManagedApp framework. This allows your app to receive credentials from your MDM system securely.

AFAIK there’s no integration between this and SCEP, so you’d need to update your server to accept these client identities in addition to the SCEP ones.

Share and Enjoy

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

[1] Check out the publication date on QA1745 Making Certificates and Keys Available To Your App!

Using DDM's declarative app configuration, a device management service can provision an identity for an app using SCEP. The app uses ManagedApp framework to receive that identity.

However I think anaspoovalloor is going to run into two problems: First, Intune doesn't currently support declarative app configuration. Second, anaspoovalloor mentioned "MAM". Usually when you discuss MAM in relation to Microsoft device management it means the device isn't actually enrolled via MDM. Instead it's usually just using a Microsoft SDKs and/or the Authenticator app.

So if you want to have an identity provisioned for your app, please request support for declarative app config / ManagedApp framework from Intune, and check whether your target devices are actually enrolled via MDM.

App is Intune MDM managed apps.

How to use an Intune-delivered SCEP certificate for mTLS in iOS app using URLSessionDelegate?
 
 
Q