122,IOS https(https解決了3個(gè)問題:數(shù)據(jù)加密,數(shù)據(jù)完整,數(shù)據(jù)真實(shí)。那么下一步就是如何解決這些問題,數(shù)據(jù)加密在發(fā)送數(shù)據(jù)前依賴SSL層對(duì)數(shù)據(jù)進(jìn)行加密,數(shù)據(jù)完整與真實(shí)性則要靠另一種關(guān)...

一、引言

本篇博客主要討論如何在客戶端與服務(wù)端之間進(jìn)行HTTPS網(wǎng)絡(luò)傳輸,為了深入理解網(wǎng)絡(luò)傳輸?shù)幕A(chǔ)原理,更加靈活的校驗(yàn)證書,博客的前半部分也將介紹一些HTTPS網(wǎng)絡(luò)傳輸原理。當(dāng)然,文章中有不正和疏漏之處,還望朋友不吝指正,感謝!

二、HTTP與HTTPS

我們都知道,HTTP是一種常用的網(wǎng)絡(luò)傳輸協(xié)議,它是基于TCP的一種應(yīng)用層協(xié)議,應(yīng)用層是什么樣的一個(gè)概念,通過下面這張示意圖可以很好的理解:

image.png

HTTP協(xié)議的網(wǎng)絡(luò)傳輸十分常見,例如網(wǎng)易的主頁(yè)http://www.163.com/。HTTP類型的網(wǎng)絡(luò)傳輸使用十分方便,但是其在安全性上卻有很大問題,列舉如下:

1.HTTP協(xié)議在傳輸數(shù)據(jù)時(shí)是明文的,任何人通過一個(gè)簡(jiǎn)單的抓包工具,就可以截獲到所有傳輸數(shù)據(jù)。

2.HTTP協(xié)議在傳輸數(shù)據(jù)時(shí)無(wú)法保證數(shù)據(jù)的完整,在截獲到明文數(shù)據(jù)后,很容易就可以將其篡改,這也是一些網(wǎng)頁(yè)總是被植入惡意廣告的原因。

3.HTTP協(xié)議在傳輸數(shù)據(jù)時(shí)無(wú)法保證真實(shí)性,這也是最恐怖的一點(diǎn)。誤入了域名欺騙的釣魚網(wǎng)站,極容易對(duì)用戶帶來財(cái)產(chǎn)損失。

基于上面3點(diǎn)安全性的考慮,一種更加安全的網(wǎng)絡(luò)傳輸協(xié)議勢(shì)必要推行,那就是HTTPS。

要理解HTTPS協(xié)議,首先需要明白什么是SSL/TLS。SSL全稱“Secure Sockets Layer”,意思為安全套接層。其實(shí)由網(wǎng)景公司為了解決HTTP傳輸協(xié)議在安全方面的缺陷而設(shè)計(jì)的。后來被標(biāo)準(zhǔn)化,更名為TLS,全稱“Transport Layer Security”,意思為傳輸層安全協(xié)議。

那么現(xiàn)在就好理解了,其實(shí)HTTPS就是將HTTP協(xié)議與TLS協(xié)議組合起來,在不改變HTTP協(xié)議原設(shè)計(jì)的基礎(chǔ)上,為其添加安全性校驗(yàn)并對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行加密。那么TLS究竟在網(wǎng)絡(luò)傳輸?shù)哪且粚舆M(jìn)行了處理了,下圖可以很好的表示:

image.png

三,https工作流程

image.png

1.Client發(fā)起一個(gè)HTTPS(比如https://juejin.im/user/5a9a9cdcf265da238b7d771c)的請(qǐng)求,根據(jù)RFC2818的規(guī)定,Client知道需要連接Server的443(默認(rèn))端口。

2.Server把事先配置好的公鑰證書(public key certificate)返回給客戶端。

3.Client驗(yàn)證公鑰證書:比如是否在有效期內(nèi),證書的用途是不是匹配Client請(qǐng)求的站點(diǎn),是不是在CRL吊銷列表里面,它的上一級(jí)證書是否有效,這是一個(gè)遞歸的過程,直到驗(yàn)證到根證書(操作系統(tǒng)內(nèi)置的Root證書或者Client內(nèi)置的Root證書)。如果驗(yàn)證通過則繼續(xù),不通過則顯示警告信息。

4.Client使用偽隨機(jī)數(shù)生成器生成加密所使用的對(duì)稱密鑰,然后用證書的公鑰加密這個(gè)對(duì)稱密鑰,發(fā)給Server。

5.Server使用自己的私鑰(private key)解密這個(gè)消息,得到對(duì)稱密鑰。至此,Client和Server雙方都持有了相同的對(duì)稱密鑰。

6.Server使用對(duì)稱密鑰加密“明文內(nèi)容A”,發(fā)送給Client。

7.Client使用對(duì)稱密鑰解密響應(yīng)的密文,得到“明文內(nèi)容A”。

8.Client再次發(fā)起HTTPS的請(qǐng)求,使用對(duì)稱密鑰加密請(qǐng)求的“明文內(nèi)容B”,然后Server使用對(duì)稱密鑰解密密文,得到“明文內(nèi)容B”。

四,HTTP 與 HTTPS 的區(qū)別

  • HTTP 是明文傳輸協(xié)議,HTTPS 協(xié)議是由 SSL+HTTP 協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,比 HTTP 協(xié)議安全。
image.png

關(guān)于安全性,用最簡(jiǎn)單的比喻形容兩者的關(guān)系就是卡車運(yùn)貨,HTTP下的運(yùn)貨車是敞篷的,貨物都是暴露的。而https則是封閉集裝箱車,安全性自然提升不少。

  • HTTPS比HTTP更加安全,對(duì)搜索引擎更友好,利于SEO,谷歌、百度優(yōu)先索引HTTPS網(wǎng)頁(yè);
  • HTTPS需要用到SSL證書,而HTTP不用;
  • HTTPS標(biāo)準(zhǔn)端口443,HTTP標(biāo)準(zhǔn)端口80;
  • HTTPS基于傳輸層,HTTP基于應(yīng)用層;
  • HTTPS在瀏覽器顯示綠色安全鎖,HTTP沒有顯示;

五、CA證書的作用,形象解釋

通過前面所介紹,我們知道HTTPS主要是為了解決3個(gè)問題:數(shù)據(jù)加密、數(shù)據(jù)完整、數(shù)據(jù)真實(shí)。那么下一步就是如何解決這些問題,數(shù)據(jù)加密在發(fā)送數(shù)據(jù)前依賴SSL層對(duì)數(shù)據(jù)進(jìn)行加密,數(shù)據(jù)完整與真實(shí)性則要靠另一種關(guān)鍵技術(shù):數(shù)字證書。

通過一個(gè)小例子可以很容易的理解證書的作用,這個(gè)例子的來源是<編程隨想>的作者,我這里暫且借用一下:A公司的a到B公司辦事,為了證明a確實(shí)是A公司的職員而不是商業(yè)間諜,A公司會(huì)為a提供一個(gè)帶有公章的證明,當(dāng)B公司看到這個(gè)證明時(shí),就可以信任辦事員a。對(duì)比網(wǎng)絡(luò)傳輸,這個(gè)證明就是證書,證書可以保證這個(gè)網(wǎng)站的真實(shí)性。我們繼續(xù)往后分析,當(dāng)B公司與越來越多的公司進(jìn)行商業(yè)合作時(shí),就又有新的問題出現(xiàn)了,比如C公司的c來B公司辦事,就需要拿C公司帶公章的證明,D公司的d來B公司辦事就需要拿D公司帶公章的證明...這樣一來,B公司要存放好多公司的公章和證明的模板,才能夠完成校驗(yàn)。這樣未免也太麻煩了,對(duì)應(yīng)到網(wǎng)絡(luò)傳輸中,客戶端就是B公司,各個(gè)網(wǎng)站都有自己的證書文件,這樣客戶端需要安裝信任大量的證書,為了解決這樣的問題,就有了第三方CA機(jī)構(gòu)。第三方CA機(jī)構(gòu)是由大家公認(rèn)信任的機(jī)構(gòu),例如R公司為第三方信任機(jī)構(gòu),其業(yè)務(wù)是為其他公司提供公章證明,這樣一來,B公司只要保有這個(gè)R公司的公章證明副本,其他A,C,D公司的辦事員也只需要從R公司申請(qǐng)到一個(gè)公章證明就可以到B公司來交流業(yè)務(wù)了。

CA的全稱是“Certificate Authority”,意為證書授權(quán)中心。大部分CA機(jī)構(gòu)頒發(fā)的證書都是需要付費(fèi)的,CA機(jī)構(gòu)頒發(fā)的證書一般都是根證書,根證書也比較容易理解,首先證書是有鏈?zhǔn)叫湃侮P(guān)系的,例如Y證書是由CA機(jī)構(gòu)頒發(fā)的根證書,由這個(gè)Y證書還可以創(chuàng)建出許多子證書,子證書可以繼續(xù)創(chuàng)建子證書,只要根證書是受信任的,其下所有的子證書都是受信任的,如下圖:

image.png

我們可以打開開源中國(guó)博客的主頁(yè):https://www.oschina.net/blog。在Chrome瀏覽器地址欄左邊可以查看證書信息,如下:

image.png

點(diǎn)擊證書信息,可以看到完整的證書鏈,如下圖:

image.png

從圖中可以看到,根證書是由CA機(jī)構(gòu)VerSign公司頒發(fā)的。此處還可以看到當(dāng)前證書是否有效以及過期時(shí)間,如果證書無(wú)效則說明此網(wǎng)頁(yè)信息有可能被篡改過,用戶在訪問時(shí)就要小心了。

除了CA機(jī)構(gòu)可以簽發(fā)證書外,個(gè)人其實(shí)也是可以創(chuàng)建證書的,當(dāng)然個(gè)人創(chuàng)建的證書也是不被信任的,我們姑且把這類證書叫做自簽名證書,如果用自簽名證書搭建了HTTPS的服務(wù),則客戶端需要安裝對(duì)應(yīng)的證書信任,才可以進(jìn)行此服務(wù)的訪問。后面我們會(huì)進(jìn)一步討論自簽名證書的使用。

六、搭建一個(gè)本地的HTTPS服務(wù)

使用Node.js可以快速的搭建前端服務(wù),我們這里使借助Express框架來搭建本地的HTTPS服務(wù),用于測(cè)試我們后邊將要進(jìn)行HTTPS通訊。Express搭建搭建項(xiàng)目模板的過程在以前的一篇博客中有詳細(xì)的介紹,這里就不再重復(fù)了,地址如下:

使用Express搭建前端項(xiàng)目:https://my.oschina.net/u/2340880/blog/794928

根據(jù)前面所述,搭建HTTPS服務(wù)需要有證書憑證,兩種證書我們可以選擇,一種是CA機(jī)構(gòu)簽發(fā)的證書,還有一種是我們自己制作的自簽名證書,在Mac電腦上打開鑰匙串訪問應(yīng)用,打開其中的證書助理,如下圖所示:
image.png

選擇其中的為您自己創(chuàng)建證書選項(xiàng),如下圖:

image.png

在之后的界面中,輸入證書的名稱,選擇證書類型,如下圖所示:

image.png

上面,我把證書的名字創(chuàng)建成了琿少,身份類型選擇的是自簽名的根證書,證書類型選擇SSL服務(wù)器,之后點(diǎn)擊創(chuàng)建即可完成證書的創(chuàng)建。

創(chuàng)建完成后,在鑰匙串訪問的登錄證書中,可以看到已經(jīng)有了琿少這個(gè)自簽名的證書,如下圖:

image.png

在證書上點(diǎn)擊右鍵,選擇導(dǎo)出選項(xiàng),名字我將其取名為huishao,文件類型要選擇.p12,如下圖所示:

image.png

點(diǎn)擊存儲(chǔ)后,需要設(shè)置一個(gè)訪問密碼,這個(gè)密碼將來將用于從.p12文件中獲取證書和密鑰,如下圖所示:

image.png

之后,系統(tǒng)有可能會(huì)讓你再次輸入一個(gè)密碼,將入下圖所示,注意,這里需要輸入的是系統(tǒng)的登錄密碼:

image.png

完成上面操作后,我們已經(jīng)將一個(gè).p12文件導(dǎo)出到了桌面。那么這個(gè).p12文件到底是個(gè)什么東西呢,它和證書之間又有什么關(guān)系呢,其實(shí).p12文件一個(gè)復(fù)合文件,其中包裝了私鑰與證書信息,使用OpenSSL工具可以將其中的信息進(jìn)行提取,搭建一個(gè)HTTPS的服務(wù)器需要兩個(gè)文件,分別問證書文件和私鑰文件,下面我們來從.p12文件中提取這些需要的文件。

打開終端,cd到huishao.p12文件所在的目錄下,使用如下命令可以將.p12文件中的私鑰分解出來:

openssl pkcs12 -in huishao.p12 -nocerts -out privateKey.pem -nodes

之間會(huì)要求輸入導(dǎo)出.p12文件時(shí)所設(shè)置的密碼。

使用如下命令將.p12文件中的證書分解出來:

openssl pkcs12 -in huishao.p12 -nokeys -out cert.pem -nodes

之間也會(huì)要求輸入導(dǎo)出.p12文件時(shí)所設(shè)置的密碼。完成上面兩部操作后,可以看到當(dāng)前文件夾下多了兩個(gè)文件,分別為cert.pem與privateKey.pem,他們分別是證書文件與密鑰文件,將他們拷貝到Express項(xiàng)目的bin文件夾下,使得Express項(xiàng)目的結(jié)構(gòu)看起來如下圖所示:

下面我們來配置Express項(xiàng)目。

在生成好的Express項(xiàng)目中的www文件的末尾添加如下代碼:

/*
HTTPS
*/
var fs = require('fs');
var https = require('https');
/*
密鑰文件
*/
var privatekey = fs.readFileSync('./privateKey.pem', 'utf8');  
/*
證書文件
*/
var certificate = fs.readFileSync('./cert.pem', 'utf8');  
var options={key:privatekey, cert:certificate};  
var serverHttps = https.createServer(options, app);  
/*
綁定端口
*/
serverHttps.listen(8080,function () {
    console.log('Https server listening on port ' + 8080);
}); 

用終端在bin文件夾下運(yùn)行 node www,效果如下:

image.png

在瀏覽器打開:https://localhost:8080/users,如果服務(wù)器搭建成功,Chrome中會(huì)出現(xiàn)如下效果:

image.png

點(diǎn)擊高級(jí),點(diǎn)擊其中的繼續(xù)訪問,可以正常獲取到服務(wù)器返回的數(shù)據(jù)。到此,我們的HTTPS服務(wù)就搭建成功了。

六、iOS開發(fā)中通過配置info.plist文件來允許HTTP協(xié)議類型的通訊

前面扯了太多,終于提到重點(diǎn)部分了。Apple在iOS9中就已經(jīng)漏出一些強(qiáng)制HTTPS通訊的端倪,只是給了開發(fā)者一些過渡,在iOS10及以后的審核機(jī)制中,Apple對(duì)于強(qiáng)制HTTPS的推動(dòng)將會(huì)越來越強(qiáng),如何讓自己的應(yīng)用程序盡快的適配HTTPS相關(guān)的標(biāo)準(zhǔn),是iOS開發(fā)者必須面對(duì)的任務(wù)。

  通過前面的分析我們了解,CA機(jī)構(gòu)簽發(fā)的證書是被默認(rèn)信任的,這就是說,如果你的公司比較有錢,愿意花錢從CA機(jī)構(gòu)申請(qǐng)一個(gè)付費(fèi)的證書,那么很幸運(yùn),你的iOS工程是不需要做任何修改的,這些CA機(jī)構(gòu)簽發(fā)的證書是默認(rèn)受信任的,因此你可以直接在程序中進(jìn)行HTTPS類型的請(qǐng)求,所需要修改的只是將請(qǐng)求url改成https開頭。但是另一種情況,無(wú)論出于什么原因,你的后臺(tái)服務(wù)用的是自簽名的證書,就想我們上面搭建的HTTPS服務(wù)一樣,如果在不做任何處理的情況下在項(xiàng)目中訪問這樣的服務(wù),就會(huì)出現(xiàn)問題了,原因是我們自己創(chuàng)建的自簽名證書是不受信任的,系統(tǒng)默認(rèn)拒絕了請(qǐng)求,示例如下:
-(void)normalHttps{
    NSURLRequest * req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://localhost:8080/users"]];
    NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession * session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
    [[session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@,%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],error);
    }] resume];
}

運(yùn)行工程后可以看到,并沒有獲取到相關(guān)數(shù)據(jù),Xcode提示為:

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

好了,那么我們先不管HTTPS的問題,如果我們直接對(duì)HTTP協(xié)議的服務(wù)進(jìn)行請(qǐng)求,會(huì)不會(huì)有問題呢,將代碼修改如下:

-(void)normalHttps{
    NSURLRequest * req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3000/users"]];
    NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession * session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
    [[session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@,%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],error);
    }] resume];
}

需要注意:Express在進(jìn)行項(xiàng)目模板的創(chuàng)建時(shí),會(huì)默認(rèn)幫我們綁定一個(gè)3000端口的HTTP服務(wù)。

運(yùn)行工程后,可以發(fā)現(xiàn)HTTP協(xié)議的請(qǐng)求也無(wú)法訪問,報(bào)錯(cuò)如下:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

其意思大致是說應(yīng)用程序傳輸安全要求強(qiáng)制使用HTTPS類型的服務(wù),但是開發(fā)者可以通過配置info.plsit文件來回避這一政策。這就是我們這節(jié)的重點(diǎn),通過文件配置的方式來跳過應(yīng)用安全傳輸協(xié)議。

在iOS9之后,開發(fā)者可以在Info.plist文件中添加如下鍵:NSAppTransportSecurity。這個(gè)鍵用來配置APP傳輸安全的相關(guān)策略,是字典類型,其中可以設(shè)置的鍵有五個(gè),如下:

NSAllowsArbitraryLoads:布爾值,默認(rèn)為NO,設(shè)置為YES則代表除了NSExceptionDomains中設(shè)置的域名外,其他所有請(qǐng)求的協(xié)議類型都不受限制,也就是說可以支持HTTP類型的請(qǐng)求,這個(gè)鍵的作用域是全局的,App內(nèi)所有的請(qǐng)求都受影響,但是如果開發(fā)者設(shè)置為了YES,在提交審核時(shí)需要說明原因。

NSAllowsArbitraryLoadsForMedia:布爾值,默認(rèn)為NO,設(shè)置為YES的話,則應(yīng)用程序內(nèi)所有的媒體數(shù)據(jù)的加載將不受協(xié)議類型的限制,同樣如果開發(fā)者設(shè)置為了YES,則在提交審核時(shí)需要說明原因。

NSAllowsArbitraryLoadsInWebContent:布爾值,默認(rèn)為NO。如果設(shè)置為YES,則應(yīng)用程序內(nèi)所有WebView的請(qǐng)求加載不受協(xié)議類型的限制,開發(fā)者設(shè)置為了YES,則在提交審核時(shí)需要說明原因。

NSAllowsLocalNetworking:布爾值,默認(rèn)為NO,如果設(shè)置為YES,則在加載本地資源時(shí)不受安全傳輸協(xié)議的限制。

NSExceptionDomains:字典,其主要對(duì)某些特殊域名做限制。其中結(jié)構(gòu)可以表示如下:

NSAppTransportSecurity : Dictionary {
    NSAllowsArbitraryLoads : Boolean
    NSAllowsArbitraryLoadsForMedia : Boolean
    NSAllowsArbitraryLoadsInWebContent : Boolean
    NSAllowsLocalNetworking : Boolean
    //對(duì)某些域名做特殊限制
    NSExceptionDomains : Dictionary {
        <domain-name-string> : Dictionary {
            NSIncludesSubdomains : Boolean
            NSExceptionAllowsInsecureHTTPLoads : Boolean
            NSExceptionMinimumTLSVersion : String
            NSExceptionRequiresForwardSecrecy : Boolean   // Default value is YES
            NSRequiresCertificateTransparency : Boolean
        }
    }
}

NSIncludesSubdomains:布爾值,這個(gè)鍵的作用是設(shè)置此域名下的所有子域名是否采用和父域名相同的配置。

NSExceptionAllowsInsecureHTTPLoads:布爾值,設(shè)置是否允許此域名使用自簽名的證書進(jìn)行請(qǐng)求,默認(rèn)為NO,如果設(shè)置為YES,則在提交時(shí)需要說明原因。

NSExceptionMinimumTLSVersion:設(shè)置所使用的TLS版本。

NSExceptionRequiresForwardSecret:設(shè)置為NO,則不允許向前加密方式。

NSRequiresCertificateTransparency:如果設(shè)置為YES,則服務(wù)端的證書要有有效的透明時(shí)間戳。

七、iOS中使用自簽名的證書進(jìn)行HTTPS請(qǐng)求校驗(yàn)

通過Info.plist文件我們是可以繞過安全傳輸協(xié)議的,但是不幸的是,從文檔上看,無(wú)論開發(fā)者通過哪種方式來繞過安全傳輸協(xié)議,Apple都要求開發(fā)者在提審時(shí)提供合適的理由,這就是說:如果你使用了HTTP協(xié)議的請(qǐng)求,沒有充足理由的話,你的App有很大的可能被審核拒絕。因此,更加保險(xiǎn)的一種方式是將所有的服務(wù)都換成HTTPS協(xié)議的,如果有CA證書,當(dāng)然完事大吉,如果沒有,我們也可以通過驗(yàn)證自簽名證書的方式來適配HTTPS協(xié)議。

在進(jìn)行HTTPS請(qǐng)求時(shí),服務(wù)端會(huì)先將證書文件返回給客戶端,如果客戶端的證書信任列表中包含這個(gè)證書,則此請(qǐng)求可以正常進(jìn)行,如果沒有,則請(qǐng)求會(huì)被拒絕。因此,在iOS中適配自簽名證書的HTTPS請(qǐng)求實(shí)際上就是將這個(gè)自簽名的證書安裝進(jìn)客戶端的信任列表。iOS中需要使用的證書是der格式的,可以使用如下命令將pem格式的證書轉(zhuǎn)換成der格式的證書:

openssl x509 -inform PEM -in cert.pem -outform DER -out cert.der

將生成的cert文件添加進(jìn)工程中,修改請(qǐng)求如下:

-(void)normalHttps{
    NSURLRequest * req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://localhost:8080/users"]];
    NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession * session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionTask * task = [session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@,%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],error);
    }];
    [task resume];
}

除此之外,需要實(shí)現(xiàn)一個(gè)SURLSessionDelegate的協(xié)議方法如下:

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@"證書認(rèn)證");
    //先判斷證書是否有效
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
        //證書驗(yàn)證請(qǐng)求
        SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
        /**
         *  導(dǎo)入多張CA證書(Certification Authority,支持SSL證書以及自簽名的CA)
         */
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"cert" ofType:@"der"];//自簽名證書
        NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
        //可以添加多張證書
        NSArray *caArray = @[caCert];
        //驗(yàn)證規(guī)則
        NSMutableArray *policies = [NSMutableArray array];
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
        NSMutableArray *pinnedCertificates = [NSMutableArray array];
        //進(jìn)行自簽名證書的添加
        for (NSData *certificateData in caArray) {
            [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
        }
        SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
        SecTrustResultType result = -1;
        //通過本地導(dǎo)入的證書來驗(yàn)證服務(wù)器的證書是否可信
        SecTrustEvaluate(serverTrust, &result);
        NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        return [[challenge sender] useCredential: credential
                      forAuthenticationChallenge: challenge];
        
    }
}

如上修改后,再次運(yùn)行工程,可以看到已經(jīng)成功請(qǐng)求到了HTTPS自簽名證書服務(wù)提供的數(shù)據(jù):

image.png

介于篇幅過長(zhǎng),關(guān)于NSURLAuthenticationChallenge相關(guān)類的更多探討和常用網(wǎng)絡(luò)庫(kù)AFNetworking中HTTPS的適配,下篇博客會(huì)繼續(xù)介紹。

八,CA證書與自簽名證書的異同

如果你想要構(gòu)建一個(gè)成功的網(wǎng)站,安全是關(guān)鍵因素之一,對(duì)于需要從訪問者那里收集PIA(personally identifiable information,個(gè)人識(shí)別信息)的網(wǎng)站而言,尤其如此。
考慮一個(gè)需要輸入社會(huì)保險(xiǎn)號(hào)的網(wǎng)站,或更常見的,需要向其添加信用卡信息以完成購(gòu)買行為的電子商務(wù)網(wǎng)站,在這樣的網(wǎng)站上,安全不僅僅是來自那些訪問者的期望,更是成功的關(guān)鍵。
如果你正在構(gòu)建一個(gè)電子商務(wù)網(wǎng)站,首先就需要一個(gè)安全證書以便保證服務(wù)器的數(shù)據(jù)安全,對(duì)于證書的選擇,即可以創(chuàng)建自簽名證書,也可以從證書頒發(fā)機(jī)構(gòu)(CA)獲得由其簽名的證書,讓我們看看這兩種證書的異同。

CA簽名的證書和自簽名證書的相似性
無(wú)論你的證書是由CA簽名的,還是自己簽名的,有一件事是完全相同的:你會(huì)得到一個(gè)安全的網(wǎng)站。通過HTTPS/SSL連接發(fā)送的數(shù)據(jù)將被加密,第三方無(wú)法竊聽。
既然自簽名證書也能做到這一點(diǎn),那為何要向CA付款呢?
CA告訴你的客戶:此服務(wù)器信息已由”信任源點(diǎn)“驗(yàn)證,最常用的CA是Verisign。CA會(huì)驗(yàn)證你的域名的所有權(quán)并頒發(fā)證書,這就能保證網(wǎng)站是安全而且合法的。
使用自簽名證書的問題是,幾乎每一個(gè)Web瀏覽器都會(huì)檢查HTTPS連接是否由可信的CA簽名,如果該連接是自簽名的,則會(huì)將其標(biāo)記為潛在風(fēng)險(xiǎn)并彈出錯(cuò)誤消息,你的客戶對(duì)該站點(diǎn)信任度就會(huì)降低。
簡(jiǎn)要總結(jié):CA簽名的證書兼具“身份證明”和“加密”雙重功能,而由于自證身份不可信,自簽名證書就只有加密功能,用于無(wú)需身份證明的場(chǎng)合。

在何種情況下可以使用自簽名證書?
由于它們提供了相同的保護(hù)能力,所以能在任何使用CA簽名證書的場(chǎng)合中使用自簽名證書,但在某些場(chǎng)合特別適用自簽名證書。例如,自簽名證書非常適合測(cè)試HTTPS服務(wù)器,你不必僅僅為測(cè)試網(wǎng)站就要支付CA簽名證書的費(fèi)用,只需提醒測(cè)試人員他們的瀏覽器可能彈出警告信息。
也可以在需要輸入隱私信息的情況下使用自簽名證書,例如:

●用戶名和密碼表單
●收集個(gè)人(非財(cái)務(wù))信息
當(dāng)然,只有那些了解并信任你的人才會(huì)使用這樣的網(wǎng)站。
所以你看到了,歸根結(jié)底就是“信任”二字。當(dāng)你使用自簽名證書時(shí),你是在對(duì)客戶說:“請(qǐng)相信我——我就是我說的我”;當(dāng)你使用由CA簽名的證書時(shí),你是在說:“請(qǐng)相信我——因?yàn)閂erisign可以證明我的身份”。
如果你在做電子商務(wù),就需要一個(gè)CA簽名證書。
如果你使用自簽名證書只是為了客戶登錄你的網(wǎng)站,那么他們可能會(huì)原諒你,但如果要求他們輸入信用卡或Paypal的信息,那么你真的需要一個(gè)CA簽名的證書,因?yàn)榇蠖鄶?shù)人信任CA簽名的證書,如果沒有它,就不會(huì)通過HTTPS服務(wù)器做生意。所以如果你想在你的網(wǎng)站上賣東西,那就投資于證書吧,這只是做生意的成本。

截屏2021-01-20 下午3.29.16.png

有TrustAsia Technologies,Inc 的簽發(fā)機(jī)構(gòu),就是

HTTPS請(qǐng)求

(1)證書轉(zhuǎn)換
進(jìn)到證書路徑,執(zhí)行 openssl x509 -in 證書.crt -out 證書.cer -outform der語(yǔ)句,這樣就可以得到cer類型的證書了。雙擊,導(dǎo)入電腦。

(2)證書放入工程

a.可以直接把轉(zhuǎn)換好的cer文件拖動(dòng)到工程中。
b.可以在鑰匙串內(nèi),找到你導(dǎo)入的證書,單擊右鍵,導(dǎo)出項(xiàng)目,就可以導(dǎo)出.cer文件的證書了

(3)配置Info.plist

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

(6)使用AFNetworking 3.x版本請(qǐng)求HTTPS

a.校驗(yàn)證書
    // 初始化單例類
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.securityPolicy.SSLPinningMode = AFSSLPinningModeCertificate;
    // 設(shè)置證書模式
    NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];
    NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
    manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:cerData, nil]];
    // 客戶端是否信任非法證書
    mgr.securityPolicy.allowInvalidCertificates = YES;
    // 是否在證書域字段中驗(yàn)證域名
    [mgr.securityPolicy setValidatesDomainName:NO];
b.不校驗(yàn)證書
    // 初始化單例類
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    // 設(shè)置非校驗(yàn)證書模式
    manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    manager.securityPolicy.allowInvalidCertificates = YES;
    [manager.securityPolicy setValidatesDomainName:NO];

iOS開發(fā)中實(shí)現(xiàn)支持HTTPS,有兩種方法:一是后臺(tái)那邊都處理好了,移動(dòng)端直接可以使用HTTPS接口,二是后臺(tái)給移動(dòng)端一個(gè)服務(wù)器證書cer 文件,這時(shí)我們就需要將cer文件導(dǎo)入到我們的工程中,以下是實(shí)現(xiàn)方法

1. 雙擊證書,這時(shí)證書已經(jīng)添加到了鑰匙串中
2. 將cer 文件拖入工程中
3. 如果使用的是AFNetwotking 的話,在代碼中添加以下代碼
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
 
 //證書
 AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
 manager.securityPolicy = securityPolicy;
 // 2.設(shè)置證書模式
 NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"tomcat" ofType:@"cer"]; //tomcat是cer文件的名稱
 NSData * cerData = [NSData dataWithContentsOfFile:cerPath];
 manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:cerData, nil]];
 // 客戶端是否信任非法證書
 manager.securityPolicy.allowInvalidCertificates = YES;
 // 是否在證書域字段中驗(yàn)證域名
 [manager.securityPolicy setValidatesDomainName:NO];
至此就已經(jīng)完成支持HTTPS了

在一般的項(xiàng)目中,可以直接判斷如果允許自簽名證書的話,直接完全信任服務(wù)器證書,如果不允許自簽名證書的話,直接拿本地的證書和服務(wù)器的證書比較

+ (void)setupFreshNetwork:(BOOL)allowInvalidCertificates{

AFHTTPSessionManager *sessionManager =[ [AFHTTPSessionManager alloc] init];

if (!allowInvalidCertificates) {
        sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
        sessionManager.securityPolicy.validatesDomainName = YES;
        sessionManager.securityPolicy.allowInvalidCertificates = NO;
    } else {
        
        //是否允許無(wú)效證書(也就是自建的證書),默認(rèn)為NO 如果是需要驗(yàn)證自建證書,需要設(shè)置為YES
        sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];  //完全信任服務(wù)器證書;
        sessionManager.securityPolicy.validatesDomainName = NO;
        sessionManager.securityPolicy.allowInvalidCertificates = YES;
    }
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
    NSSet <NSData *> *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]];
    return [self policyWithPinningMode:pinningMode withPinnedCertificates:defaultPinnedCertificates];
}

這個(gè)函數(shù)自動(dòng)在本地的目錄查找.cer 文件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容