iOS 升級HTTPS通過ATS你所要知道的


轉載自: http://www.jianshu.com/p/2d72ef8dbf5a

            http://www.jianshu.com/p/fca4d1c9e4cd


由於蘋果規定2017年1月1日以後,所有APP都要使用HTTPS進行網絡請求,否則無法上架,因此研究了一下在iOS中使用HTTPS請求的實現。網上搜索了一些比較有用資料,大家可以參考下

蘋果強制升級的HTTPS不僅僅是在接口HTTP上加個S那麼簡單:
它所有滿足的是iOS9中新增App Transport Security(簡稱ATS)特性:
那滿足ATS我們需要做什麼呢
1.必須是蘋果信任的CA證書機構頒發的證書
2.後臺傳輸協議必須滿足: TLS1.2 (這很重要, 後面的自制證書滿足這個條件是前提)
3.簽字算法只能是下面的一種:

TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

4.證書必須使用SHA256或者更好的哈希算法進行簽名,要麼是2048位或者更長的RSA密鑰,要麼就是256位或更長的ECC密鑰。

目前有兩種升級到HTTPS得方法:
1.第三方認證的頒發CA證書(推薦)
2.自己製作證書(這種不知道能不能滿足蘋果的審覈)

一: 第三方認證的頒發CA證書

證書到底長什麼樣子呢? 取個栗子:
大家請打開https://www.baidu.com
然後看到


百度的證書分析


那些證書機構頒發的證書能用:蘋果官方信任證書

收費SSL證書: 網上百度一大把, 收費還挺貴的,自己可以多找幾個對比一下
免費SSL證書: 除了收費的CA證書機構, 你還可以去騰訊雲申請免費的SSL證書, 教程免費在騰訊雲申請SSL證書的方法
沃通(WoSign)免費的SSL證書最近被蘋果封殺了, 能不能用大家可以看一下蘋果的公告: 您的蘋果手機輕點“設置”>“通用”>“關於本機”>"證書信任設置">"進一步瞭解被信任的證書"去了解

檢測你的接口是否滿足蘋果的ATS要求, 有以下兩種方法:

1.騰訊雲提供的檢測頁面檢測


騰訊雲的檢測頁面


2 終端輸入 nsurl --ats-diagnostics --verbose 你的接口地址
大家可以參考這篇文章,裏面的說的很明白:
關於iOS9中的App Transport Security相關說明及適配(更新於2016.7.1)
裏面會詳細說明你的證書哪點不符合ATS要求
當然下面自己製作證書去實現HTTPS的,檢測不通過的,所以我覺得審覈會被拒
這種方法配置好了, 在手機端就什麼都不用配置就可以請求了

二: 自己製作證書

蘋果官方信任證書裏說到有三種證書:

1 可信的根證書用於建立信任鏈,以驗證由可信的根簽署的其他證書,例如,與 Web 服務器建立安全連接。當 IT 管理員創建 iPhone、iPad 或 iPod touch 的配置描述文件時,無需提供這些可信的根證書。
2 始終詢問的證書不受信任,但不受阻止。使用其中一個證書時,系統將提示您選擇是否信任該證書。
3 已阻止的證書視爲被盜用,將不再受信任。

自制證書我覺得應該就是屬於第二種情況, 所以這種方法我也不知道能不能通過蘋果的審覈, 只是提供一個方法給大家參考, 看到網上有人說可以,有人說不可以, 不到1月1號,自己沒試過都不敢說大話
這種方式拿到後臺的接口用谷歌瀏覽器打開跟百度的證書是有區別的


自己製作證書


很明顯沒有綠鎖, 當打開的時候會詢問是否連接這個不受信任的連接纔會進一步打開, 下面就來一步步的實現(包括怎麼製作證書)
iOS使用自簽名證書實現HTTPS請求
iOS Https協議 自簽證書訪問數據參考這個例子的時候,博主自帶的Demo AFN框架請求不了數據, 我用了最新AFN版本的成功返回數據
還可以參考一下
iOS 10 適配 ATS app支持https通過App Store審覈

我在利用原生的代碼測試時遇到的問題

@interface ViewController () <NSURLSessionDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {


}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

   NSURLSessionDataTask *task =  [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
   [task resume];
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
    NSLog(@"接收到服務器響應");
    //注意:這裏需要使用completionHandler回調告訴系統應該如何處理服務器返回的數據
    //默認是取消
    /**
     NSURLSessionResponseCancel = 0,            默認的處理方式,取消
     NSURLSessionResponseAllow = 1,             接收服務器返回的數據
     NSURLSessionResponseBecomeDownload = 2,    變成一個下載請求
     NSURLSessionResponseBecomeStream           變成一個流
     */
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data {
    NSLog(@"獲取到服務段數據");
    NSLog(@"%@",[self jsonToDictionary:data]);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error {
    NSLog(@"請求完成%@", error);
}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"證書認證");
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
        do
        {
            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
            NSCAssert(serverTrust != nil, @"serverTrust is nil");
            if(nil == serverTrust)
                break; /* failed */
            /**
             *  導入多張CA證書(Certification Authority,支持SSL證書以及自簽名的CA),請替換掉你的證書名稱
             */
            NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];//自簽名證書
            NSData* caCert = [NSData dataWithContentsOfFile:cerPath];

            NSCAssert(caCert != nil, @"caCert is nil");
            if(nil == caCert)
                break; /* failed */

            SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
            NSCAssert(caRef != nil, @"caRef is nil");
            if(nil == caRef)
                break; /* failed */

            //可以添加多張證書
            NSArray *caArray = @[(__bridge id)(caRef)];

            NSCAssert(caArray != nil, @"caArray is nil");
            if(nil == caArray)
                break; /* failed */

            //將讀取的證書設置爲服務端幀數的根證書
            OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
            NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
            if(!(errSecSuccess == status))
                break; /* failed */

            SecTrustResultType result = -1;
            //通過本地導入的證書來驗證服務器的證書是否可信
            status = SecTrustEvaluate(serverTrust, &result);
            if(!(errSecSuccess == status))
                break; /* failed */
            NSLog(@"stutas:%d",(int)status);
            NSLog(@"Result: %d", result);

            BOOL allowConnect = (result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed);
            if (allowConnect) {
                NSLog(@"success");
            }else {
                NSLog(@"error");
            }

            /* kSecTrustResultUnspecified and kSecTrustResultProceed are success */
            if(! allowConnect)
            {
                break; /* failed */
            }

#if 0
            /* Treat kSecTrustResultConfirm and kSecTrustResultRecoverableTrustFailure as success */
            /*   since the user will likely tap-through to see the dancing bunnies */
            if(result == kSecTrustResultDeny || result == kSecTrustResultFatalTrustFailure || result == kSecTrustResultOtherError)
                break; /* failed to trust cert (good in this case) */
#endif

            // The only good exit point
            NSLog(@"信任該證書");

            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
            return [[challenge sender] useCredential: credential
                          forAuthenticationChallenge: challenge];

        }
        while(0);
    }

    // Bad dog
    NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
    completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,credential);
    return [[challenge sender] cancelAuthenticationChallenge: challenge];
}

- (NSDictionary *)jsonToDictionary:(NSData *)jsonData {
    NSError *jsonError;
    NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&jsonError];
    return resultDic;
}

@end

下面說說我在配置自己製作證書過程中遇到的問題:
1.轉換證書: 把後臺給你的.crt證書轉化爲.cer後綴
終端命令行openssl x509 -in 你的證書.crt -out 你的證書.cer -outform der

2.利用系統的方法來不到- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { NSLog(@"證書認證");}這個方法的時候, 是因爲後臺的傳輸協議還沒升級到TLS1.2, 叫後臺升級後就可以來到驗證證書的這個方法了.

3.拖入證書讀取不出證書數據
參考: https的證書錯誤,錯誤碼-1012問題及解決方案

SDWebImage: 項目中大家用到AFN請求網絡數據, 升級驗證SSL證書的方案相信你看完上面的參考文章已經沒問題了, 我給出的代碼, 自定義網絡請求也沒問題了, 還有就是SDWebImage框架的請求HTTPS的圖片時,大家可以繞過證書驗證去加載圖片[imageView sd_setImageWithURL:[NSURL URLWithString:urlString] placeholderImage:self.placeholder options:SDWebImageAllowInvalidSSLCertificates];

恩, 這就是這幾天升級HTTPS覺得有幫助的參考和總結.希望幫到你





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章