English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

iOSのHttps自署名証明書認証およびデータリクエストのエンキャッピング原理

要約: WWDC 2016開発者カンファレンスで発表した最終期限です:2017年1月1日 App StoreのすべてのアプリケーションはApp Transport Security(ATS)機能を有効にする必要があります。App Transport Security(ATS)は、AppleがiOS 9iOSでATS(App Transport Security)というプライバシープロテクション機能が導入され、明文HTTPリソースのロードを遮断し、接続はより安全なHTTPSを通して行わなければなりません。現在、Appleは開発者に一時的にATSをオフにすることを許可しており、HTTP接続を使用し続けることができますが、年末までにすべての公式ストアのアプリケーションはATSの強制使用が義務付けられます。

プロジェクトではAFNetworkingを使用しています 3.0以降のバージョンでは、ATSの原因により、iOSではHttpsで始まるリンクのみを使用することが許可されています。2016年12月30日以前、AppleはATSを迂回することが許可されていました。以下の図のように:

しかし、2017年1月1から始め、httpを使用してリソースをロードするアプリケーションを受け入れなくなるため、この記事ではAFNを使用して自署名証明書を認証する方法について説明します(注:CA機関で認証された証明書を使用する場合、認証は不要で、Httpsで始まるリンクを使用してデータアクセスとページのロードを行うことができます)。プロジェクトはGitHubにアップロードされています(ソースコードを参照する場合はリンクをクリックしてください):HttpsSignatureCertificate_jb51.rar

1、ルートクラスを作成します。ここではAKNetPackegeAFNと名付けます

 1>  .hファイル、必要なGetおよびPostメソッドを作成

#import <Foundation/Foundation.h>
typedef enum{
  AKNetWorkGET ,  /**< GETリクエスト */
  AKNetWorkPOST = 1 /**< POSTリクエスト */
}AKNetWorkType;
typedef void (^HttpSuccess)(id json);
typedef void (^HttpErro)(NSError* error);
@interface AKNetPackegeAFN : NSObject
+(instancetype)shareHttpManager;
/*
 *
 netWorkType:リクエスト方法 GET または POST
 signature:署名証明書を使用するかどうか、はいの場合は証明書名を直接書き込む、いいえの場合はnilを入力
 api:リクエストのURLインターフェース
 parameters:リクエストパラメータ
 sucess:リクエスト成功時の返り値
 fail:リクエスト失敗時の返り値
 *
 */
- (void)ネットワークタイプ:(AKNetWorkType)ネットワークタイプ シグネチャー:(NSString *)シグネチャーAPI:(NSString *)APIパラメータ:(NSDictionary *)パラメータ Success:(HttpSuccess)sucess Fail:(HttpErro)fail;
@end

2> .mファイル、ヘッダーファイルAFNetworking.hをインポートし、Manager属性を新規作成し、shareHttpManagerクラスメソッドを実装します

#import "AKNetPackegeAFN.h"
#import "AFNetworking.h"
@interface AKNetPackegeAFN()
@property (nonatomic,strong) AFHTTPSessionManager *manager;
@end
@implementation AKNetPackegeAFN
+(instancetype)shareHttpManager{
  static dispatch_once_t once = 0;
  static AKNetPackegeAFN *httpManager = nil;
  dispatch_once(&once, ^(void){
    httpManager = [[self alloc]init];
  });
  return httpManager;
}

2、GetとPostメソッドの実装

使用時にバックエンドから提供された証明書を.cer形式に変換し、プロジェクトのルートディレクトリにドラッグしてプロジェクトに追加します。例えば、バックエンドから提供された証明書の名前がKuture.crtの場合、証明書を受け取った後、ダブルクリックしてインストールし、キーチェーンを開き、Kutureという名前の証明書を右クリックしてエクスポートし、拡張子が.cerを選択し、確認してください。以下の図のように表示されます:

  -->     -->

-->

GETとPOST実装メソッドのラッピング

- (void)ネットワークタイプ:(AKNetWorkType)ネットワークタイプ シグネチャー:(NSString *)シグネチャーAPI:(NSString *)APIパラメータ:(NSDictionary *)パラメータ Success:(HttpSuccess)sucess Fail:(HttpErro)fail{
  //証明書検証モードを有効にします
  AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
  //自署名証明書の使用を許可するかどうか
  signature == nil ? (void)(securityPolicy.allowInvalidCertificates = NO):(securityPolicy.allowInvalidCertificates = YES);
  //ドメイン名の検証が必要かどうか
  securityPolicy.validatesDomainName = NO;
  _manager = [[AFHTTPSessionManager alloc]initWithBaseURL:[NSURL URLWithString:api]];
  _manager.responseSerializer = [AFJSONResponseSerializer serializer];
  _manager.securityPolicy = securityPolicy;
  _manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"application/xml",@"text/xml",@"text/json",@"text/plain",@"text/javascript",@"text/html", nil];
  if (signature != nil){
    __weak typeof(self) weakSelf = self;
    [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {
      //サーバーのトラストオブジェクトを取得します
      SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
      //自署名証明書のインポート
      NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"あなたの証明書名" ofType:@"cer"];
      NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
      if (!cerData) {
        NSLog(@"==== .cerファイルはnilです====");
        return 0;
      }
      NSArray *cerArray = @[cerData];
      weakSelf.manager.securityPolicy.pinnedCertificates = cerArray;
      SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerData);
      NSCAssert(caRef != nil, @"caRef is nil");
      NSArray *caArray = @[(__bridge id)(caRef)];
      NSCAssert(caArray != nil, @"caArray is nil");
      //読み取った証明書をserverTrustの根証明書として設定
      OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
      SecTrustSetAnchorCertificatesOnly(serverTrust, NO);
      NSCAssert(errSecSuccess == status, @"SectrustSetAnchorCertificates failed");
      //質問認証の処理方法を選択
      NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
      __autoreleasing NSURLCredential *credential = nil;
      //NSURLAuthenTicationMethodServerTrust質問認証方式
      if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        //基于客户端的安全策略来决定是否信任该服务器,不信任则不响应质询
        if ([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
          //创建质询证书
          credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
          //确认质询方式
          if (credential) {
            disposition = NSURLSessionAuthChallengeUseCredential;
          } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
          }
        } else {
          //取消挑战
          disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
      } else {
        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
      }
      return disposition;
    }
  }
  if (netWorkType == 0){
    [_manager GET:api parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
      if (sucess){
        sucess(responseObject);
      else{
        NSLog(@"リンクエラーまたはネットワークが存在しない");
      }
    } * _Nullable task, NSError * _Nonnull error) {
      fail(error);
    }
  }else if (netWorkType == 1){
    [_manager POST:api parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
      if (sucess){
        sucess(responseObject);
      else{
        NSLog(@"リンクエラーまたはネットワークが存在しない");
      }
    } * _Nullable task, NSError * _Nonnull error) {
      fail(error);
    }
  }  
}

2  使用方法は、データの取得や送信が必要なクラスで AKNetPackegeAFN.h を直接インポートし、メソッドを実装するだけで良いです。以下のようになります:

//オブジェクトの作成
  //自署名証明書の場合、使用する前にAKNetPackegeAFNのメソッドに証明書をバインドする必要があります(証明書はプロジェクトにドラッグ&ドロップして配置してください)
  /*
   *
   netWorkType:リクエスト方法 GET または POST
   signature:署名証明書を使用するかどうか、はいの場合は証明書名を直接書き込む、いいえの場合はnilを入力
   api:リクエストのURLインターフェース
   parameters:リクエストパラメータ
   sucess:リクエスト成功時の返り値
   fail:リクエスト失敗時の返り値
   *
   */
  AKNetPackegeAFN *netHttps = [AKNetPackegeAFN shareHttpManager];
  [netHttps netWorkType:リクエストタイプ Signature:証明書名 API:リクエストURL Parameters:パラメータ Success:^(id json) {
    NSLog(@"Json:%@",json);
  } *error) {
    NSLog(@"Error:%@",error);
  }

これでこの記事はすべて終わりです。皆様の学習に役立つことを願っています。また、ナイアラートゥートラリアムのサポートを多くいただければ幸いです。

声明:この記事の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーが自発的に貢献し、自己でアップロードしたものであり、このサイトは所有権を持ちません。また、人工的な編集は行われていません。著作権侵害の可能性のある内容が見つかった場合は、notice#wまでメールをお送りください。3codebox.com(メールを送信する際には、#を@に置き換えてください。報告を行い、関連する証拠を提供してください。一旦確認がとりたいとすると、このサイトは即座に侵害する可能性のあるコンテンツを削除します。)

おすすめ