1

I have a react-native project and I have to create Native module for https request with .p12 certification but I never use Objective-C (it's a little complicated) or Swift I found a class for https request with certification it is but I didn't use this cause I don't have a .h file and my project folder;

MyBridge.h

#import "React/RCTBridgeModule.h"

@interface MyFirstBridge : NSObject <RCTBridgeModule>

@end

MyBridge.m

#import "MyFirstBridge.h"
#import <React/RCTLog.h>

@implementation MyFirstBridge

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(sendGetRequest:(NSString *)urllocation:(NSString *)location)
{
  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:@"GET"];
[request setURL:[NSURL URLWithString:url]];

NSError *error = nil;
NSHTTPURLResponse *responseCode = nil;

NSData *oResponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&responseCode error:&error];

 if([responseCode statusCode] != 200){
    NSLog(@"Error getting %@, HTTP status code %i", url, [responseCode statusCode]);
    return nil;
}

  callback(@[[NSNull null], [[NSString alloc] initWithData:oResponseData encoding:NSUTF8StringEncoding]]);
}

@end

It works as basic HTTP get request but when I tried https service I need to pin a certificate for each request. How can I send a HTTPS request for this case?

Cœur
  • 37,241
  • 25
  • 195
  • 267
errorau
  • 2,121
  • 1
  • 14
  • 38
  • Did you check [this](https://github.com/MaxToyberman/react-native-ssl-pinning) – Dhruv Apr 19 '19 at 08:10
  • yes i did @Dhruv but it didn't work. my another question https://stackoverflow.com/questions/55595544/react-native-https-request-with-p12-or-pfx-certificate-best-way – errorau Apr 19 '19 at 08:12
  • Why not to use above library and code in react native? why do you want to write native code ? – Dhruv Apr 19 '19 at 08:17
  • Cause I need to write client certification authentication . https://github.com/facebook/react-native/issues/10885 – errorau Apr 19 '19 at 08:24
  • Try it using [TrustKit](https://stackoverflow.com/questions/40240321/how-can-i-implement-ssl-certificate-pinning-while-using-react-native) – Dhruv Apr 19 '19 at 09:15

2 Answers2

3

I guess by using .p12 certificate, you are referring to establishing a mutual authentication between the client and the server. Basically, you have to go through the following steps (objective-c):

  • Create security objects required to authenticate the server (verify its signature against the root CA signature) and authenticate the client (provide the client certificate to the server to verify its signature as well). Load a .cer file of the CA and a .p12 file of the client.
  • Define the URL resource you want to retrieve and create NSURLConnection
  • Specify the authentication methods you want to handle (using NSURLConnectionDelegate callbacks)
  • Handle the authentication challenge (using NSURLConnectionDelegate callbacks)

Load Cert files (root CA Cert of the server + client key and certificate)

rootCertRef contains the CA Certificate (root certificate of the CA that signed the server certificate)

identity (SecIdentityRef) contains the client key and certificate required to authenticate the client with the server.

NSData *rootCertData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”rootCert” ofType:@”cer”]];
SecCertificateRef rootCertRef = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData);

NSData *p12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@“clientCert" ofType:@"p12"]];
NSArray *item = nil;
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@“password", kSecImportExportPassphrase, nil];
SecPKCS12Import((CFDataRef) p12Data , (CFDictionaryRef)dict, (CFArrayRef *)item);
SecIdentityRef identity = (SecIdentityRef)[[item objectAtIndex:0] objectForKey:(id)kSecImportItemIdentity];

Configure the URL (you already did it)

// Create the request.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.com"]];

Create NSURLConnection >> set the delegate to self that must implement NSURLConnectionDelegate to be able to do customer authentication

// Create url connection and fire request asynchronously
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];

Enable server and client authentication in callback canAuthenticateAgainstProtectionSpace

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
  if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
    return YES;
  if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodClientCertificate])
    return YES;
  return NO;
}

Perform the mutual authentication requested by the server

-(void) connection:didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

  //Authenticate the server
  if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) { // Verify method

    SecTrustRef trust = [[challenge protectionSpace] serverTrust];         // Create trust object
    NSArray *trustArray = [NSArray arrayWithObjects:rootCertRef, nil];   // Add as many certificates as needed
    SecTrustSetAnchorCertificates(trust, (CFArrayRef) trustArray );        // Set trust anchors

    SecTrustResultType trustResult;                                        // Store trust result in this
    SecTrustEvaluate(trust, trustResult);                                  // Evaluate server trust
    if(trust_result == kSecTrustResultUnspecified) {
      NSURLCredential *credential = [NSURLCredential credentialForTrust:trust];
      [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
  } else {
    // handle error;
  }

  //Send client identity to server for client authentication
  if([[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:nil   persistence:NSURLCredentialPersistenceNone];
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
  }
}
Hichem BOUSSETTA
  • 1,791
  • 1
  • 21
  • 27
2

I have this in swift due to lack of time i can't convert it in objective-C right now i hope you will convert it yourself,

Set URL Session Delegate too when setting request,

   fileprivate func SSLCertificateCreateTrustResult(_ serverTrust: SecTrust)->SecTrustResultType {
    let certificate: SecCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
    let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
    var certName = "localServerCert"
    if serverUrl.contains(find: "uniqueNameinURL"){
        certName = "liveServerCert"
    }
    let cerPath: String = Bundle.main.path(forResource: certName, ofType: "der")!
    let localCertificateData = NSData(contentsOfFile:cerPath)!

    let certDataRef = localCertificateData as CFData
    let cert = (SecCertificateCreateWithData(nil, certDataRef))
    let certArrayRef = [cert] as CFArray
    SecTrustSetAnchorCertificates(serverTrust, certArrayRef)
    SecTrustSetAnchorCertificatesOnly(serverTrust, false)
    let trustResult: SecTrustResultType = SecTrustResultType.invalid
    return trustResult
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    if challenge.protectionSpace.authenticationMethod == (NSURLAuthenticationMethodServerTrust) {
        let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
        var localCertificateTrust = SSLCertificateCreateTrustResult(serverTrust)
        SecTrustEvaluate(serverTrust, &localCertificateTrust)
        if localCertificateTrust == SecTrustResultType.unspecified || localCertificateTrust == SecTrustResultType.proceed
        {
            let credential:URLCredential = URLCredential(trust: serverTrust)
            challenge.sender?.use(credential, for: challenge)
            completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))

        } else {
            let properties = SecTrustCopyProperties(serverTrust)
            completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
        }
    }
    else
    {
        completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil);
    }
}

OR you may follow the following URL

iOS: Pre install SSL certificate in keychain - programmatically

Abu Ul Hassan
  • 1,340
  • 11
  • 28
  • It's worth noting that this urlSession function is an optional part of the URLSessionDelegate protocol. You will need to define it in a class that conforms to this and is set to be the delegate for your URLSession. – Barry Jones Aug 11 '22 at 19:02
  • I think the additional context I provided will be helpful to some readers. – Barry Jones Aug 14 '22 at 22:46