I am developing a React Native app for Android & iOS both. I want to embed the client certificate into my React Native app. I have Cloudflare's client certificate in pem format & its private key in pem format. My server has rules enabled so API calls with valid certificates can only work.
I have successfully implemented Android. and my API calls are working for Android.
But for iOS, I am still not able to do it. The embedding certificate with the URLSession part is left.
My React native app uses axios library for API calls. how can I also embed the certificate with my app to work for iOS?
I have done some implementation & tried calling API directly using NSURLSession. So my request is getting success. I am getting 200 response. That means I am successfully able to import certificate data.
My
MyURLSessionDelegatefile is in Swift & myAppDelegatecode is in Objective C++. I have done bridging.
Here is the code:
MyURLSessionDelegate.swift
import Foundation
public class MyURLSessionDelegate: NSObject, URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod
!= NSURLAuthenticationMethodClientCertificate {
completionHandler(.performDefaultHandling, nil)
return
}
guard let file = Bundle.main.url(forResource: "CERTIFICATE-NAME“, withExtension: "p12"),
let p12Data = try? Data(contentsOf: file) else {
// Loading of the p12 file's data failed.
completionHandler(.performDefaultHandling, nil)
return
}
let password = "password" // Obviously this should be stored or entered more securely.
let p12Contents = PKCS12(pkcs12Data: p12Data, password: password)
guard let identity = p12Contents.identity else {
// Creating a PKCS12 never fails, but interpretting th contained data can. So again, no identity? We fall back to default.
completionHandler(.performDefaultHandling, nil)
return
}
let credential = URLCredential(identity: identity,
certificates: nil,
persistence: .none)
challenge.sender?.use(credential, for: challenge)
completionHandler(.useCredential, credential)
}
}
private class PKCS12 {
let label: String?
let keyID: NSData?
let trust: SecTrust?
let certChain: [SecTrust]?
let identity: SecIdentity?
public init(pkcs12Data: Data, password: String) {
let importPasswordOption: NSDictionary
= [kSecImportExportPassphrase as NSString: password]
var items: CFArray?
let secError: OSStatus
= SecPKCS12Import(pkcs12Data as NSData,
importPasswordOption, &items)
guard secError == errSecSuccess else {
if secError == errSecAuthFailed {
NSLog("Incorrect password?")
}
fatalError("Error trying to import PKCS12 data")
}
guard let theItemsCFArray = items else { fatalError() }
let theItemsNSArray: NSArray = theItemsCFArray as NSArray
guard let dictArray
= theItemsNSArray as? [[String: AnyObject]] else {
fatalError()
}
func f<T>(key: CFString) -> T? {
for dict in dictArray {
if let value = dict[key as String] as? T {
return value
}
}
return nil
}
self.label = f(key: kSecImportItemLabel)
self.keyID = f(key: kSecImportItemKeyID)
self.trust = f(key: kSecImportItemTrust)
self.certChain = f(key: kSecImportItemCertChain)
self.identity = f(key: kSecImportItemIdentity)
}
}
AppDelegate.mm
#import "AppDelegate.h"
#import <Firebase.h>
#import <React/RCTBundleURLProvider.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import <TrustKit/TrustKit.h>
#import “MyApp-Swift.h" // Import the Swift module header
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[FIRApp configure];
self.moduleName = @"MyApp”;
self.initialProps = @{};
MyURLSessionDelegate *myURLSessionDelegate = [[MyURLSessionDelegate alloc] init];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:configuration delegate:myURLSessionDelegate delegateQueue:nil];
NSURL *url = [NSURL URLWithString:@"https://example.example.com/rest/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
// Set the parameters
NSDictionary *params = @{
@"username": @“User123”,
@"password": @“User@123”
};
// Convert parameters to data
NSError *error = nil;
NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
if (!error) {
request.HTTPBody = postData;
} else {
NSLog(@"Error encoding parameters: %@", error);
}
NSURLSessionDataTask *task = [urlSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response or error here
if (error) {
NSLog(@"Error: %@", error);
return;
}
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSLog(@"Response status code: %ld", (long)httpResponse.statusCode);
} else {
NSLog(@"Invalid response");
return;
}
if (data) {
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Response: %@", responseString);
}
}];
[task resume];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
@end