iOS開發HTTPS實現之信任SSL認證和自我簽署憑證

來源:互聯網
上載者:User

標籤:create   所有應用   關於   container   直接   設定   允許   set   append   

首先來分析一下什麼是HTTPS以及瞭解HTTPS對於iOS開發人員的意義

HTTPS 以及SSL/TSL
  • 什麼是SSL?

SSL(Secure Sockets Layer, 安全通訊端層),因為原先互連網上使用的 HTTP 協議是明文的,存在很多缺點,比如傳輸內容會被偷窺(嗅探)和篡改。 SSL 協議的作用就是在傳輸層對網路連接進行加密。

  • 何為TLS?

到了1999年,SSL 因為應用廣泛,已經成為互連網上的事實標準。IETF 就在那年把 SSL 標準化。標準化之後的名稱改為 TLS(Transport Layer Security,傳輸層安全性通訊協定)。SSL與TLS可以視作同一個東西的不同階段

  • HTTPS

簡單來說,HTTPS = HTTP + SSL/TLS, 也就是 HTTP over SSL 或 HTTP over TLS,這是後面加 S 的由來 。

HTTPS和HTTP異同:HTTP和HTTPS使用的是完全不同的串連方式,用的連接埠也不一樣,前者是80,後者是443。HTTP的串連很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路通訊協定,比HTTP協議安全。

在WWDC 2016開發人員大會上,蘋果宣布了一個期限:到2017年1月1日 App Store中的所有應用都必須啟用 App Transport Security安全功能。App Transport Security(ATS)是蘋果在iOS 9中引入的一項隱私保護功能,屏蔽明文HTTP資源載入,串連必須經過更安全的HTTPS。蘋果目前允許開發人員暫時可以繼續使用HTTP串連,但到年底所有官方商店的應用都必須強制性使用ATS。

以下是開發人員網站公告原文:

應用傳輸安全性通訊協定是與iOS9和OS X 10.11一同發布的,該協議需要應用程式通過HTTPS使用安全的網路連接,以提高使用者的資料和隱私安全。
在2016年WWDC上我們宣布在今年年底之前,提交到App Store的應用程式必須支援應用傳輸安全性通訊協定。為了給你們額外的時間去準備,這個到期日已被延長,當新的到期日確定的時候,我們將及時提供相關資訊。

2016年12月21日蘋果更新了到期日,宣布延期執行ATS支援要求Supporting App Transport Security。

 開發人員網站

** 此舉為開發人員提供了更多時間來做適配和支援。然而,對於iOS開發人員來說,儘早解決HTTPS請求的問題仍為上策。**

蘋果ATS對HTTPS認證的要求

啟用ATS必須符合以下標準,不滿足條件的HTTPS認證,ATS都會拒絕串連:

  • 伺服器所有的串連使用TLS1.2以上版本
  • HTTPS認證必須使用SHA256以上雜湊演算法簽名
  • HTTPS認證必須使用RSA 2048位或ECC 256位以上公開金鑰演算法
  • 使用前向加密技術

此外,蘋果ATS支援CT認證透明,要求開發人員使用支援CT認證透明度的SSL認證,確保SSL認證合法透明,防止中間人攻擊。

發送HTTPS請求信任SSL認證和自我簽署憑證,分為三種情況

1.如果你的app服務端安裝的是SLL頒發的CA,可以使用系統方法直接實現信任SSL認證,關於Apple對SSL認證的要求請參考:蘋果官方文檔CertKeyTrustProgGuide

這種方式不需要在Bundle中引入CA檔案,可以交給系統去判斷伺服器端的認證是不是SSL認證,驗證過程也不需要我們去具體實現。
範例程式碼:

    NSURL *URL = [NSURL URLWithString:URLString];    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];    //建立同步串連    NSError *error = nil;    NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];    NSString *receivedInfo = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];

當然,如果你需要同時信任SSL認證和自我簽署憑證的話還是需要在代碼中實現CA的驗證,這種情況在後面會提到。

2.基於AFNetWorking的SSL特定伺服器憑證信任處理,重寫AFNetWorking的customSecurityPolicy方法,這裡我建立了一個HttpRequest類,分別對GET和POST方法進行了封裝,以GET方法為例:

+ (void)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))success failure:(void (^)(NSError *))failure {    // 1.獲得要求管理者    AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];    // 2.申明返回的結果是text/html類型    mgr.responseSerializer = [AFHTTPResponseSerializer serializer];    // 3.設定逾時時間為10s    mgr.requestSerializer.timeoutInterval = 10;        // 加上這行代碼,https ssl 驗證。    if(openHttpsSSL) {        [mgr setSecurityPolicy:[self customSecurityPolicy]];    }        // 4.發送GET請求    [mgr GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObj){        if (success) {            success(responseObj);        }    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {        if (error) {            failure(error);        }    }];}
+ (AFSecurityPolicy*)customSecurityPolicy {    // /先匯入認證    NSString *cerPath = [[NSBundle mainBundle] pathForResource:certificate ofType:@"cer"];//認證的路徑    NSData *certData = [NSData dataWithContentsOfFile:cerPath];        // AFSSLPinningModeCertificate 使用認證驗證模式    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];        // allowInvalidCertificates 是否允許無效認證(也就是自建的認證),預設為NO    // 如果是需要驗證自建認證,需要設定為YES    securityPolicy.allowInvalidCertificates = YES;        //validatesDomainName 是否需要驗證網域名稱,預設為YES;    //假如認證的網域名稱與你請求的網域名稱不一致,需把該項設定為NO;如設成NO的話,即伺服器使用其他可信任機構頒發的認證,也可以建立串連,這個非常危險,建議開啟。    //置為NO,主要用於這種情況:用戶端請求的是子網域名稱,而認證上的是另外一個網域名稱。因為SSL認證上的網域名稱是獨立的,假如認證上註冊的網域名稱是www.google.com,那麼mail.google.com是無法驗證通過的;當然,有錢可以註冊萬用字元的網域名稱*.google.com,但這個還是比較貴的。    //如置為NO,建議自己添加對應網域名稱的校正邏輯。    securityPolicy.validatesDomainName = NO;        securityPolicy.pinnedCertificates = @[certData];        return securityPolicy;}

其中的cerPath就是app bundle中憑證路徑,certificate為認證名稱的宏,僅支援cer格式,securityPolicy的相關配置尤為重要,請仔細閱讀customSecurityPolicy方法並根據實際情況設定其屬性。

這樣,就能夠在AFNetWorking的基礎上使用HTTPS協議訪問特定伺服器,但是不能根信任認證的CA檔案,因此這種方式存在風險,讀取pinnedCertificates中的認證數組的時候有可能失敗,如果認證不符合,certData就會為nil。

3.更改系統方法,發送非同步NSURLConnection請求。

- (void)getDataWithURLRequest {    //connection    NSString *urlStr = @"https://developer.apple.com/cn/";    NSURL *url = [NSURL URLWithString:urlStr];    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];    NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];    [connection start];}

重點在於處理NSURLConnection的didReceiveAuthenticationChallenge代理方法,對CA檔案進行驗證,並建立信任連接。

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {        return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];}- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {     /*    //直接驗證伺服器是否被認證(serverTrust),這種方式直接忽略認證驗證,直接建立串連,但不能過濾其它URL串連,可以理解為一種折衷的處理方式,實際上並不安全,因此不推薦。    SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];    return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]                  forAuthenticationChallenge: challenge];     */    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:@"cloudwin" ofType:@"cer"];//自我簽署憑證            NSData* caCert = [NSData dataWithContentsOfFile:cerPath];                        NSString *cerPath2 = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"cer"];//SSL認證            NSData * caCert2 = [NSData dataWithContentsOfFile:cerPath2];                        NSCAssert(caCert != nil, @"caCert is nil");            if(nil == caCert)                break; /* failed */                        NSCAssert(caCert2 != nil, @"caCert2 is nil");            if (nil == caCert2) {                break;            }                        SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);            NSCAssert(caRef != nil, @"caRef is nil");            if(nil == caRef)                break; /* failed */                        SecCertificateRef caRef2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert2);            NSCAssert(caRef2 != nil, @"caRef2 is nil");            if(nil == caRef2)                break; /* failed */                        NSArray *caArray = @[(__bridge id)(caRef),(__bridge id)(caRef2)];                    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");            }            /* https://developer.apple.com/library/ios/technotes/tn2232/_index.html */            /* https://developer.apple.com/library/mac/qa/qa1360/_index.html */            /* 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            return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]                          forAuthenticationChallenge: challenge];                    } while(0);    }        // Bad dog    return [[challenge sender] cancelAuthenticationChallenge: challenge];    }

這裡的關鍵在於result參數的值,根據官方文檔的說明,判斷(result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed)的值,若為1,則該網站的CA被app信任成功,可以建立資料連線,這意味著所有由該CA簽發的各個伺服器憑證都被信任,而訪問其它沒有被信任的任何網站都會串連失敗。該CA檔案既可以是SLL也可以是自簽名。

NSURLConnection的其它代理方法實現

#pragma mark -- connect的非同步代理程式方法-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {    NSLog(@"請求被響應");    _mData = [[NSMutableData alloc]init];}-(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data {    NSLog(@"開始返回資料片段");        [_mData appendData:data];}-(void)connectionDidFinishLoading:(NSURLConnection *)connection {    NSLog(@"連結完成");    //可以在此解析資料    NSString *receiveInfo = [NSJSONSerialization JSONObjectWithData:self.mData options:NSJSONReadingAllowFragments error:nil];    NSLog(@"received data:\\\\n%@",self.mData);    NSLog(@"received info:\\\\n%@",receiveInfo);}//連結出錯-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {    NSLog(@"error - %@",error);}

至此,HTTPS信任認證的問題得以解決,這不僅是為了響應Apple強制性使用ATS的要求,也是為了實際生產環境安全性的考慮,HTTPS是未來的趨勢,建議儘早支援。

如需參考Demo請移步本人在Github上的開源項目

iOS開發HTTPS實現之信任SSL認證和自我簽署憑證

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.