使用 Objective C for React Native 项目在 iOS 应用程序中固定 SSL 证书

问题描述 投票:0回答:1

我想使用 Objective C 为我的 React 本机项目嵌入 iOS 应用程序的客户端 SSL 证书。 我尝试了很多解决方案,但没有得到任何运气。

因此,我们需要嵌入客户端 SSL 证书 (.pem),以便我们的 API 能够正常工作。我在调用我们的 API 时收到 403 我正在共享我的代码。

**MyURLSessionDelegate.mm**

#import "MyURLSessionDelegate.h"
#import <Security/Security.h>

@implementation MyURLSessionDelegate

+ (instancetype)sharedInstance {
    static MyURLSessionDelegate *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyURLSessionDelegate alloc] init];
    });
    return sharedInstance;
}
  
- (NSURLSession *)configuredSession {
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSLog(@"MyURLSessionDelegate=====>working=====>line 16 : %@", sessionConfig);

    // Load the client certificate
    NSString *pathToCertificate = [[NSBundle mainBundle] pathForResource:@"xyz" ofType:@"pem"];
    NSLog(@"pathToCertificate ====> : %@", pathToCertificate);
    NSData *certificateData = [NSData dataWithContentsOfFile:pathToCertificate];
    NSLog(@"MyURLSessionDelegate=====>working=====>line 21 : %@", certificateData);
    NSString *base64Certificate = [certificateData base64EncodedStringWithOptions:0];
    NSLog(@"Base64 Encoded Certificate: %@", base64Certificate);
  


    if (!certificateData) {
        NSLog(@"Client certificate not found");
        return nil;
    }
    
    // Create a custom SSL configuration with the client certificate
    NSDictionary *sslSettings = @{
        (NSString *)kCFStreamSSLCertificates: @[certificateData],
        (NSString *)kCFStreamSSLValidatesCertificateChain: @NO // Disable certificate chain validation if needed
    };
    
    sessionConfig.TLSMinimumSupportedProtocol = kTLSProtocol1;
    sessionConfig.TLSMaximumSupportedProtocol = kTLSProtocol12;
    sessionConfig.TLSMinimumSupportedProtocol = kTLSProtocol12;
    sessionConfig.TLSMaximumSupportedProtocol = kTLSProtocol13;
    
    sessionConfig.connectionProxyDictionary = sslSettings;
    
    // Create and return NSURLSession with custom configuration and delegate
    return [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
}

#pragma mark - NSURLSessionDelegate Methods

// Implement NSURLSessionDelegate methods as needed

@end
**AppDelegate.mm**

#import "AppDelegate.h"

#import <Firebase.h>

#import <React/RCTBundleURLProvider.h>

#import <UserNotifications/UserNotifications.h>

#import <RNCPushNotificationIOS.h>

#import <TrustKit/TrustKit.h>

#import "MyURLSessionDelegate.h"

#import "SSLPinning.h"

#import "ClientSecurity.h"



@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{ 

  [FIRApp configure];

  self.moduleName = @"care_app";
  self.initialProps = @{};

  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

  center.delegate = self;

NSURLSession *session = [[MyURLSessionDelegate sharedInstance] configuredSession];

NSLog(@"session configuration: %@", session.configuration);


// Create the URL

NSURL *url = [NSURL URLWithString:@"https://test.example.com];



// Create the request

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

request.HTTPMethod = @"POST";



// Set the parameters

NSDictionary *parameters = @{@"username": @“example”, @"password": @“Example@123};

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

// Set the request body

[request setHTTPBody:postData];



// Set the content type

[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];



// Create a data task with the session

NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

    // Check for errors

    if (error) {

        NSLog(@"SS Error: %@", error);

        return;

    }

    // Log the raw response data

    NSLog(@"SS Raw Response Data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);


    // Parse the response data (assuming it's JSON for example)

    NSError *jsonError = nil;

    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];

    // Check for JSON parsing errors

    if (jsonError) {

        NSLog(@"SS JSON Error: %@", jsonError);

        return;

    }

    // Now you can work with the JSON response

    NSLog(@"SS Response: %@", json);

}];



// Resume the task to start the request

[dataTask resume];    

  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

我收到以下错误

`会话配置:

SS 原始响应数据:

 <!DOCTYPE html>

<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en-US"> <![endif]-->

<!--[if IE 7]>    <html class="no-js ie7 oldie" lang="en-US"> <![endif]-->

<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en-US"> <![endif]-->

<!--[if gt IE 8]><!--> <html class="no-js" lang="en-US"> <!--<![endif]-->

<head>

<title>Attention Required! | Cloudflare</title>

<meta charset="UTF-8" />

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

<meta name="robots" content="noindex, nofollow" />

<meta name="viewport" content="width=device-width,initial-scale=1" />

<link rel="stylesheet" id="cf_styles-css" href="/cdn-cgi/styles/cf.errors.css" />

<!--[if lt IE 9]><link rel="stylesheet" id='cf_styles-ie-css' href="/cdn-cgi/styles/cf.errors.ie.css" /><![endif]-->

<style>body{margin:0;padding:0}</style>





<!--[if gte IE 10]><!-->

<script>

  if (!navigator.cookieEnabled) {

    window.addEventListener('DOMContentLoaded', function () {

      var cookieEl = document.getElementById('cookie-alert');

      cookieEl.style.display = 'block';

    })

  }

</script>

<!--<![endif]-->

</head>

<body>

  <div id="cf-wrapper">

    <div class="cf-alert cf-alert-error cf-cookie-error" id="cookie-alert" data-translate="enable_cookies">Please enable cookies.</div>

    <div id="cf-error-details" class="cf-error-details-wrapper">

      <div class="cf-wrapper cf-header cf-error-overview">

        <h1 data-translate="block_headline">Sorry, you have been blocked</h1>

        <h2 class="cf-subheadline"><span data-translate="unable_to_access">You are unable to access</span> test.example.com</h2>

      </div><!-- /.header -->



      <div class="cf-section cf-highlight">

        <div class="cf-wrapper">

          <div class="cf-screenshot-container cf-screenshot-full">

            

              <span class="cf-no-screenshot error"></span>

            

          </div>

        </div>

      </div><!-- /.captcha-container -->



      <div class="cf-section cf-wrapper">

        <div class="cf-columns two">

          <div class="cf-column">

            <h2 data-translate="blocked_why_headline">Why have I been blocked?</h2>



            <p data-translate="blocked_why_detail">This website is using a security service to protect itself from online attacks. The action you just performed triggered the security solution. There are several actions that could trigger this block including submitting a certain word or phrase, a SQL command or malformed data.</p>

          </div>



          <div class="cf-column">

            <h2 data-translate="blocked_resolve_headline">What can I do to resolve this?</h2>



            <p data-translate="blocked_resolve_detail">You can email the site owner to let them know you were blocked. Please include what you were doing when this page came up and the Cloudflare Ray ID found at the bottom of this page.</p>

          </div>

        </div>

      </div><!-- /.section -->



      <div class="cf-error-footer cf-wrapper w-240 lg:w-full py-10 sm:py-4 sm:px-8 mx-auto text-center sm:text-left border-solid border-0 border-t border-gray-300">

  <p class="text-13">

    <span class="cf-footer-item sm:block sm:mb-1">Cloudflare Ray ID: <strong class="font-semibold">863c216f98813d2a</strong></span>

    <span class="cf-footer-separator sm:hidden">&bull;</span>

    <span id="cf-footer-item-ip" class="cf-footer-item hidden sm:block sm:mb-1">

      Your IP:

      <button type="button" id="cf-footer-ip-reveal" class="cf-footer-ip-reveal-btn">Click to reveal</button>

      <span class="hidden" id="cf-footer-ip">206.84.236.234</span>

      <span class="cf-footer-separator sm:hidden">&bull;</span>

    </span>

    <span class="cf-footer-item sm:block sm:mb-1"><span>Performance &amp; security by</span> <a rel="noopener noreferrer" href="https://www.cloudflare.com/5xx-error-landing" id="brand_link" target="_blank">Cloudflare</a></span>

    

  </p>

  <script>(function(){function d(){var b=a.getElementById("cf-footer-item-ip"),c=a.getElementById("cf-footer-ip-reveal");b&&"classList"in b&&(b.classList.remove("hidden"),c.addEventListener("click",function(){c.classList.add("hidden");a.getElementById("cf-footer-ip").classList.remove("hidden")}))}var a=document;document.addEventListener&&a.addEventListener("DOMContentLoaded",d)})();</script>

</div><!-- /.error-footer -->

    </div><!-- /#cf-error-details -->

  </div><!-- /#cf-wrapper -->

  <script>

  window._cf_translation = {};
</script>

</body>

</html>`

** 我的 info.plist 文件**

`<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>example.com</key> <!-- Replace with your domain -->
        <dict>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSTemporaryExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
            <key>NSTemporaryExceptionRequiresForwardSecrecy</key>
            <false/>
            <key>NSExceptionRequiresForwardSecrecy</key>
            <false/>
            <key>NSRequiresCertificateTransparency</key>
            <false/>
            <key>NSAllowsArbitraryLoads</key>
            <false/>
            <key>NSAllowsLocalNetworking</key>
            <false/>
        </dict>
    </dict>
</dict>
`

让我知道这里出了什么问题。

我已经尝试过上面的代码,但得到 403。

ios objective-c react-native mobile ssl-certificate
1个回答
0
投票

亲爱的Pranjali Wagh

首先我建议你不要直接使用

CERTIFICATE
,而是使用没有过期问题的
PUBLICK KEY

现在跟着我一步步集成SSL Pinning

  1. 在IOS中使用trustkit进行SSL Pinning,在podfile中添加:

    pod 'TrustKit', '1.6.5'

  2. 里面

    ios/AlmanaDoctorsApp/AppDelegate.m
    添加:

#import <TrustKit/TrustKit.h> // Import TrustKit
#import <TrustKit/TSKPinningValidator.h> // Import TSKPinningValidator
#import <TrustKit/TSKPinningValidatorCallback.h> // Import TSKPinningValidatorCallback

// in the end of Dynamic *t = [Dynamic new] before return

// TrustKit configuration
    void (^loggerBlock)(NSString *) = ^void(NSString *message)
    {
        NSLog(@"TrustKit log: %@", message);
    };
    [TrustKit setLoggerBlock:loggerBlock];

    NSDictionary *trustKitConfig =
    @{
      kTSKSwizzleNetworkDelegates: @YES,
      kTSKPinnedDomains: @{
              @"pa-api.almanahospital.com.sa" : @{
                      kTSKIncludeSubdomains: @YES,
                      kTSKEnforcePinning: @YES,
                      kTSKDisableDefaultReportUri: @YES,
                      kTSKPublicKeyHashes : @[
                              @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
                              @"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="
                              ],
                      },
              },
      };
    [TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
    [TrustKit sharedInstance].pinningValidatorCallback = ^(TSKPinningValidatorResult *result, NSString *notedHostname, TKSDomainPinningPolicy *policy) {
        if (result.finalTrustDecision == TSKTrustEvaluationFailedNoMatchingPin) {
            NSLog(@"TrustKit certificate matching failed");
            // Handle certificate matching failure
        }
    };

  1. @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
    替换为证书的实际
    PUBLICK KEY
    。并且
    BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=
    与您的备份
    PUBLICK KEY

注意:如果您没有备份公钥,只需使用

@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
添加您的公钥即可正常工作。

  1. 现在安装所有依赖项
    cd ios/
     pod update

希望它能如您所愿。

© www.soinside.com 2019 - 2024. All rights reserved.