如何在iOS上實現對HTTPS的支持

首先,需要明確你使用HTTP/HTTPS的用途,因爲OSX和iOS平臺提供了多種API,來支持不同的用途,官方文檔《Making HTTP and HTTPS Requests》有詳細的說明,而文檔《HTTPS Server Trust Evaluation》則詳細講解了HTTPS驗證相關知識,這裏就不多說了。本文主要講解我們最常用的NSURLConnection支持HTTPS的實現(NSURLSession的實現方法類似,只是要求授權證明的回調不一樣而已),以及怎麼樣使用AFNetworking這個非常流行的第三方庫來支持HTTPS。本文假設你對HTTP以及NSURLConnection的接口有了足夠的瞭解。

1. 驗證證書的API

相關的Api在Security Framework中,驗證流程如下:

1). 第一步,先獲取需要驗證的信任對象(Trust Object)。這個Trust Object在不同的應用場景下獲取的方式都不一樣,對於NSURLConnection來說,是從delegate方法-connection:willSendRequestForAuthenticationChallenge:回調回來的參數challenge中獲取([challenge.protectionSpace serverTrust])。

2). 使用系統默認驗證方式驗證Trust Object。SecTrustEvaluate會根據Trust Object的驗證策略,一級一級往上,驗證證書鏈上每一級數字簽名的有效性(上一部分有講解),從而評估證書的有效性。

3). 如第二步驗證通過了,一般的安全要求下,就可以直接驗證通過,進入到下一步:使用Trust Object生成一份憑證([NSURLCredential credentialForTrust:serverTrust]),傳入challenge的sender中([challenge.sender useCredential:cred forAuthenticationChallenge:challenge])處理,建立連接。

4). 假如有更強的安全要求,可以繼續對Trust Object進行更嚴格的驗證。常用的方式是在本地導入證書,驗證Trust Object與導入的證書是否匹配。更多的方法可以查看Enforcing Stricter Server Trust Evaluation,這一部分在講解AFNetworking源碼中會講解到。

5). 假如驗證失敗,取消此次Challenge-Response Authentication驗證流程,拒絕連接請求。

ps: 假如是自建證書的,則會跳過第二步,使用第三部進行驗證,因爲自建證書的根CA的數字簽名未在操作系統的信任列表中。

iOS授權驗證的API和流程大概瞭解了,下面,我們看看在NSURLConnection中的代碼實現:

使用NSURLConnection支持HTTPS的實現

// Now start the connection

NSURL * httpsURL = [NSURL URLWithString:@"https://www.google.com"];

self.connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL] delegate:self];



//回調

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    //1)獲取trust object

    SecTrustRef trust = challenge.protectionSpace.serverTrust;

    SecTrustResultType result;



    //2)SecTrustEvaluate對trust進行驗證

    OSStatus status = SecTrustEvaluate(trust, &result);

    if (status == errSecSuccess &&

        (result == kSecTrustResultProceed ||

        result == kSecTrustResultUnspecified)) {



        //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續連接

        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];

        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];



    } else {



        //5)驗證失敗,取消這次驗證流程

        [challenge.sender cancelAuthenticationChallenge:challenge];



  }

}

上面是代碼是通過系統默認驗證流程來驗證證書的。假如我們是自建證書的呢?這樣Trust Object裏面服務器的證書因爲不是可信任的CA簽發的,所以直接使用SecTrustEvaluate進行驗證是不會成功。又或者,即使服務器返回的證書是信任CA簽發的,又如何確定這證書就是我們想要的特定證書?這就需要先在本地導入證書,設置成需要驗證的Anchor Certificate(就是根證書),再調用SecTrustEvaluate來驗證。代碼如下

//先導入證書

NSString * cerPath = ...; //證書的路徑

NSData * cerData = [NSData dataWithContentsOfFile:cerPath];

SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData));

self.trustedCertificates = @[CFBridgingRelease(certificate)];

//回調

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    //1)獲取trust object

    SecTrustRef trust = challenge.protectionSpace.serverTrust;

    SecTrustResultType result;

    //注意:這裏將之前導入的證書設置成下面驗證的Trust Object的anchor certificate

    SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)self.trustedCertificates);

    //2)SecTrustEvaluate會查找前面SecTrustSetAnchorCertificates設置的證書或者系統默認提供的證書,對trust進行驗證

    OSStatus status = SecTrustEvaluate(trust, &result);

    if (status == errSecSuccess &&

        (result == kSecTrustResultProceed ||

        result == kSecTrustResultUnspecified)) {



        //3)驗證成功,生成NSURLCredential憑證cred,告知challenge的sender使用這個憑證來繼續連接

        NSURLCredential *cred = [NSURLCredential credentialForTrust:trust];

        [challenge.sender useCredential:cred forAuthenticationChallenge:challenge];



    } else {



        //5)驗證失敗,取消這次驗證流程

        [challenge.sender cancelAuthenticationChallenge:challenge];



  }

}

建議採用本地導入證書的方式驗證證書,來保證足夠的安全性。更多的驗證方法,請查看官方文檔《HTTPS Server Trust Evaluation》

2. 使用AFNetworking來支持HTTPS

AFNetworking是iOS/OSX開發最流行的第三方開源庫之一,其作者是非常著名的iOS/OSX開發者Mattt Thompson,其博客NSHipster也是iOS/OSX開發者學習和開闊技術視野的好地方。AFNetworking已經將上面的邏輯代碼封裝好,甚至更完善,在AFSecurityPolicy文件中,有興趣可以閱讀這個模塊的代碼;

AFNetworking上配置對HTTPS的支持非常簡單:

NSURL * url = [NSURL URLWithString:@"https://www.google.com"];

AFHTTPRequestOperationManager * requestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];

dispatch_queue_t requestQueue = dispatch_create_serial_queue_for_name("kRequestCompletionQueue");

requestOperationManager.completionQueue = requestQueue;

AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//allowInvalidCertificates 是否允許無效證書(也就是自建的證書),默認爲NO

//如果是需要驗證自建證書,需要設置爲YES

securityPolicy.allowInvalidCertificates = YES;

//validatesDomainName 是否需要驗證域名,默認爲YES;

//假如證書的域名與你請求的域名不一致,需把該項設置爲NO

//主要用於這種情況:客戶端請求的是子域名,而證書上的是另外一個域名。因爲SSL證書上的域名是獨立的,假如證書上註冊的域名是www.google.com,那麼mail.google.com是無法驗證通過的;當然,有錢可以註冊通配符的域名*.google.com,但這個還是比較貴的。

securityPolicy.validatesDomainName = NO;

//validatesCertificateChain 是否驗證整個證書鏈,默認爲YES

//設置爲YES,會將服務器返回的Trust Object上的證書鏈與本地導入的證書進行對比,這就意味着,假如你的證書鏈是這樣的:

//GeoTrust Global CA 

//    Google Internet Authority G2

//        *.google.com

//那麼,除了導入*.google.com之外,還需要導入證書鏈上所有的CA證書(GeoTrust Global CA, Google Internet Authority G2);

//如是自建證書的時候,可以設置爲YES,增強安全性;假如是信任的CA所簽發的證書,則建議關閉該驗證;

securityPolicy.validatesCertificateChain = NO;

requestOperationManager.securityPolicy = securityPolicy;

這就是AFNetworking的支持HTTPS的主要配置說明,AFHTTPSessionManager與之基本一致,就不重複了。

3. 總結

從2017年1月起,iOS必須支持HTTPS,所以HTTPS已經是大勢所趨了。

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