iOS 9.0之后蘋果開始要求使用Https進行通信。ATS是iOS9和OS X El Capitan的一個新特性。開啟該功能后,ATS對使用NSURLConnection, CFURL或NSURLSession 等APIs 進行的網絡請求默認強制使用HTTPS加密傳輸,目標是提高Apple 操作系統以及應用程序的安全性。蘋果公司官方文章指出,https必須符合ATS要求,服務器必須支持傳輸層安全(TLS)協議1.2以上版本;證書必須使用SHA256或更高的哈希算法簽名,并使用2048位以上RSA密鑰或256位以上ECC算法等等。
https概要
- https協議基于http(超文本傳輸協議),在http通信基礎上對傳輸報文進行加密,主要是為了保證通信雙方的數據不被竊取。在通信時,服務器和客戶端各自提供自己的憑證,驗證自己的身份后進行通信. 這樣能減少不受信任的三方竊取信息。
- PKI(公鑰基礎設施),是HTTPS的基礎,PKI與非堆成秘鑰加密技術密切相關,包括消息摘要,數字簽名,和加密服務。而數字證書以及證書機構(CA -Certificate Authority)是PKI中重要的概念。
- 數字證書在https中也起著至關重要的作用, 它是一個計算機文件,一般由可信任的證書機構頒發,他包含了證書所有者的一般信息(公開秘鑰)。并且能夠證明這個公鑰確實是證書所有者所合法擁有的。這一點是有CA對數字證書的簽名來保證的(簽名用到了信息摘要以及非對稱加密算法,使用CA的私鑰加密),在這里我門需要使用CA的數字證書(包含CA的公鑰)來驗證簽名的合法性,那么我門如何驗證CA數字證書的合法性呢?CA的信任鏈可以解決這個問題。一個CA的證書是由上一級CA簽發的,因此它的合法性由它的上一級CA來驗證,其中最頂層的證書機構被稱為根CA,它的證書被稱為根證書,如果一個鏈條的根證書是合法可信的,那么我們就認為在這個鏈條上的所有CA以及它們所簽發的證書都是合法可信的。于是,最終的問題就是如何保證根證書的合法性呢?原來,我們所使用的基本計算機軟件,比如瀏覽器和服務器軟件,都會內置根CA的自簽名證書,只要我們使用的基本軟件可信,那么就能保證根CA證書的合法有效。
https圖解
- https雙向通信,只是在服務器身份確認后,提供客戶端憑證再進行一次客戶端的身份校驗過程。首先客戶端和服務器需要經過三次確認,生成二個隨機數,并將服務器的證書提供給客戶端。前面兩個隨機數因為時明文傳輸的,很容易給截取到,當客戶端驗證完服務器端的證書OK后,通過隨機數擴展算法,按一定的規則生成第三個隨機數,并用服務器的公鑰加密后發送給服務器.這樣客戶端和服務器均獲得三個隨機數,從而可以生成雙方約定的會話秘鑰。 客戶端再將自己的證書發送給服務器端進行驗證,只要服務器驗證通過,那么他們就開始使用這個隨機秘鑰進行數據加密。實際上如果, 客戶端和服務器端協議版本號不一致,客戶端偏好設置中的加密算法列表里沒有服務器支持的任何一種加密算法,客戶端的憑證或服務器的憑證無法匹配,服務器域名不正確,或者證書過期等,都會導致通信失敗,這時候根據指定的警報協議 將相關的失敗信息通知到接收方。
-
下圖為https的單向通信的主要流程,實際要比這個要復雜的多(忽略上面客戶端憑證的校驗過程)。
Snip20161204_8.png
https通信詳解
- HTTPS是工作于SSL層上的HTTP協議,SSL(安全套接層)工作于TCP層上,向應用層提供了兩個基本的安全服務,認證和保密。 SSL有三個子協議, 握手協議,記錄協議,警報協議。
- 握手協議:
- 建立安全能力: 由客戶端發起,向服務器發送Client Hello消息,其中包含SSL版本,客戶端隨機數(用于生成秘鑰),會話號,加密算法清單(客戶端所支持的加密算法),壓縮算法清單。服務器返回 Server Hello信息,其中包含SSL版本,服務器隨機數(用于生成秘鑰),會話號,選擇加密算法,選擇的壓縮算法。
- 服務器認證與秘鑰交換服務器是本階段發送信息的所有方,共三步
- 第一步 : 證書,服務器將數字證書以及CA證書鏈發給客戶端,客戶端由此獲得服務器公鑰;客戶端還可以再這一步驗證服務器是否可信.如果不可信則可以停止鏈接。并提醒用戶注意。
- 第二步 : 服務器請求客戶端證書,客戶端認證在SSL中是可選的,因此這一步也是可選的。
- 第三步 : 服務器握手完成,發送這個消息后,服務器等待客戶端的響應。
- 客戶端認證與秘鑰交換
- 客戶端是本階段的所有信息的發送方,分為三步
- 第一步 : 證書,客戶端將客戶端的數字證書發送給服務器,里面包含了客戶端的公鑰,通常來說這個證書應該是由服務提供著分發給客戶端,由指定的CA簽發的,因此服務器可以驗證客戶端證書的合法性,并決定是否繼續。
- 第二步 : 秘鑰交換,客戶端生成48字節的預備秘鑰,并用服務器端的公鑰加密,然后發送給服務器,這個預備秘鑰只有客戶端和服務器知道(與雙發在之后會話中使用的對成秘鑰相關),在這一步也間接的驗證了服務器的合法性.因為只有擁有與證書對應的私鑰才能解密出預備秘鑰。
- 第三步 : 證書驗證,客戶端還需要向服務器驗證自己是真正的客戶端(數字證書無法證明它就是客戶端,擁有閾公鑰對應的私鑰才是關鍵),為此客戶端把預備秘鑰,客戶隨機數,服務器的隨記數組和起來,用私鑰對結果進行簽名,發送給服務器,服務器利用客戶端公鑰就能夠的到原始的數據,用來驗證客戶端的真實性。(這里主要是對 客戶端隨記數,服務器隨記數,預備秘鑰)這個三個要素進行判定是否有人竊取和串改。
- 完成: 在這個階段,客戶端和服務器各自獨立生成相同的主秘鑰和對成秘鑰,主秘鑰和對成秘鑰只有它們自己知道。主秘鑰和對成秘鑰是由 預備秘鑰,客戶端隨機數和服務器隨機數組和后,經過消息摘要算法生成。
- 握手協議:
- SSL握手完成之后,就會進入回話階段:客戶端和服務器使用握手協議中生成的對成秘鑰進行加密和解密以保證通信的安全。
- 總的來說, 如果鑰完成HTTPS的雙向認證需要以下秘鑰和證書:
- 服務器端: 1.服務器私鑰, 2.由CA前發的含有服務器公鑰的數字證書, 3.CA的數字證書,在雙向驗證過程中通常服務器可以自己作為證書機構,并且由服務器CA前發服務器證書和客戶端證書。
- 客服端: 1.客戶端私鑰, 2. 由CA簽發的含有客戶端公鑰的數字證書。為了避免中間人攻擊,客戶端還需要內置服務器證書,用來驗證所連接的服務器是否是指定的服務器。(通常在一些服務器的網站或者app中,客服端憑證校驗是默認省略的,單向安全通信的,只對服務器的身份進行校驗)。
https實踐
- 在Apple Reference《Certificate,Key,and Trust Services Programming Guide》提供了https驗證的框架, security.該框架包含了 https通信中信任對象, 同時也提供https加密中SAH,MD5,RSA,AES等算法的支持。
- 基于AFNetworking https單向驗證,系統框架已經做了非常多的簡化步驟,在AFSecurity這個庫中,框架內部已經做好了相關的判定,比如根據是否為私有證書,將提供的 證書對象設置為錨證書,對服務器返回的 信任對象進行評估,如果通過則進行安全通信,反之則取消通信。
+ (AFSecurityPolicy*)configSecurityPolicy {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];//證書的路徑
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy* securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = YES; /**如果采用三方頒發的證書,則不使用自建證書驗證服務器,由三方機構驗證*/
securityPolicy.validatesDomainName = YES;
securityPolicy.pinnedCertificates = [NSSet setWithObject:certData];
mgr.securityPolicy = securityPolicy;
return securityPolicy;
}
- 基于AFNetworking雙向通信。
- 在AFNetworking中進行雙向通信,需要我們自己對挑戰認證進行定義,其中服務器身份驗證部分,我們仍然可以沿用上面的 security對象進行自動校驗,但是客戶端驗證,就需要我們進行實現了。
- AFNetworking框架已經為我們講調整驗證的方法是用回調Block抽離出來。在使用時候我們只需鑰注冊該回調block,在對應的block中實現當收到驗證服務器端憑證和需要提供客服端憑證的方法進行處理.相關代碼實現方式如下.
- 服務端驗證: 首先需要對挑戰challege對象的保護空間進行判定,獲取當前的驗證方式,如果是對服務器驗證,則取出serverTrust對象,并通過設定錨證書,與我們定義的服務器證書的信任對象進行比對和評估,判定是否正確,如果正確,則告訴challege挑戰發起者,下次繼續使用該憑證作為服務器的身份驗證。
- 客戶端驗證: 將客服端憑證對象,通常都是由p12文件生成, 先將它轉換為CFData類型,再通過 valueForKey的方式,一層一層的將其撥開,取到最后的秘鑰,并生成NSURLCredential憑證對象。
- AFNetworking框架最終生成的憑證對象,以及驗證方式,通過block的回調的方式在 delegate挑戰認證的方法中回調出去。
+ (void)configSSLChallage {
AFHTTPSessionManager* mgr = [self shareInstance];
__weak typeof(*&mgr)weakmgr = mgr;
[mgr setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *_credential =nil;
//server 端驗證:
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
if([weakmgr.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 {
//client 客戶端驗證:
NSLog(@"authorMethod:%@",challenge.protectionSpace.authenticationMethod);
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"client"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([PLMNetWorkTool extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
{
if (extractIdentityAndTrust((__bridge CFDataRef)(PKCS12Data), &identity, &trust)== noErr) {
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void*certs[] = {certificate};
CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
_credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
}
*credential = _credential;
return disposition;
}];
}
總結: https涉及的內容較多,如果不明白其中的具體原理,僅僅靠搬運一份代碼,那么一段出現問題將是非常致命的。理解其中的緣由對于我們應對https證書偽造,域名劫持,或是https會話秘鑰擴展,https安全通信雙層加固非常重要。
參考文獻: http://baike.baidu.com/link?url=QDGRYuHehLYwvUSNX_vH7xo8tQ8CJoDSSKeLP_PWzmohvk5E1E6RegmRGB0hERJW_0MqTBJn6sp6h65hLi_04a
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html