Configure mTLS for iOS
Mutual Transport Layer Security (mTLS) is a security mechanism that extends standard TLS by requiring both the server and the client device to authenticate each other when establishing a connection.
Use this feature when your infrastructure requires client certificate authentication in addition to standard server certificate validation.
To establish an mTLS connection, the feature must be configured on both the server and the client.
This article covers only the client-side configuration. To configure mTLS on the server side, follow the Web Service mTLS guide.
Preparing Client Certificate
The Face SDK does not generate, import, or store the client certificate. The host application must prepare SecIdentity, which contains a certificate and its private key.
The host application manages the certificate lifecycle. It can receive certificate material from a backend, mobile device management (MDM), a provisioning service, or another secure channel. Then, the host application creates or imports SecIdentity and stores it in Keychain.
For mTLS, the Face SDK requires URLCredential created from SecIdentity.
import Foundation
import Security
func makeIdentityFromP12(base64: String, password: String) -> SecIdentity? {
guard let p12Data = Data(base64Encoded: base64) else {
return nil
}
let options: [String: Any] = [
kSecImportExportPassphrase as String: password
]
var importedItems: CFArray?
let status = SecPKCS12Import(p12Data as CFData, options as CFDictionary, &importedItems)
guard status == errSecSuccess,
let items = importedItems as? [[String: Any]],
let identity = items.first?[kSecImportItemIdentity as String] as? SecIdentity else {
return nil
}
return identity
}
To load the client identity from Keychain, use the query that matches how the identity was saved by the host application:
import Security
func loadClientIdentityFromKeychain() -> SecIdentity? {
let query: [String: Any] = [
kSecClass as String: kSecClassIdentity,
kSecAttrLabel as String: "client-certificate",
kSecReturnRef as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else { return nil }
return item as? SecIdentity
}
Configuring mTLS
The host application must provide the Face SDK with a ready-to-use URLCredential for the client certificate authentication challenge.
Configure authenticationChallengeDelegate before starting SDK operations that can perform network requests:
import UIKit
import FaceSDK
import Security
final class ViewController: UIViewController, URLAuthenticationChallengeDelegate {
override func viewDidLoad() {
super.viewDidLoad()
FaceSDK.service.authenticationChallengeDelegate = self
}
func credential(for challenge: URLAuthenticationChallenge) -> URLCredential? {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate else {
return nil
}
guard let identity = loadClientIdentityFromKeychain() else {
return nil
}
return URLCredential(identity: identity, certificates: nil, persistence: .forSession)
}
private func loadClientIdentityFromKeychain() -> SecIdentity? {
let query: [String: Any] = [
kSecClass as String: kSecClassIdentity,
kSecAttrLabel as String: "client-certificate",
kSecReturnRef as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else { return nil }
return item as? SecIdentity
}
}
#import <UIKit/UIKit.h>
#import <FaceSDK/FaceSDK.h>
#import <Security/Security.h>
@interface ViewController () <RFSURLAuthenticationChallengeDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RFSFaceSDK.service.authenticationChallengeDelegate = self;
}
- (NSURLCredential * _Nullable)credentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if (![challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
return nil;
}
SecIdentityRef identity = [self copyClientIdentityFromKeychain];
if (identity == NULL) {
return nil;
}
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity
certificates:nil
persistence:NSURLCredentialPersistenceForSession];
CFRelease(identity);
return credential;
}
- (SecIdentityRef _Nullable)copyClientIdentityFromKeychain {
// Load SecIdentityRef from Keychain.
return NULL;
}
@end
If the identity is missing, invalid, expired, or unavailable, return nil from credential(for:). The Face SDK will cancel the authentication challenge, and the corresponding network request will fail.
Warning
authenticationChallengeDelegate stores the delegate as a weak reference.
The host app must maintain a strong reference to the delegate object.
Otherwise, the delegate may be deallocated, causing requests to an mTLS server to fail silently.
Server trust validation and certificate pinning are not changed by this API. Request interception logic is independent of mTLS and can be used with this API.