本文來自 http://www.2cto.com/kf/201611/570823.html ?
蘋果官方在2017年將會(huì)強(qiáng)制要求使用HTTPS,當(dāng)然,很多開發(fā)者可能會(huì)誤解會(huì)所有在應(yīng)用里面的請求都必須使用HTTPS红且,其實(shí)不完全是這樣跃赚。蘋果官方文檔提高了很多細(xì)節(jié)的東西瞄摊,告訴開發(fā)者如何讓自己的應(yīng)用適配HTTPS箱季,本文將從原理,官方文檔川背,以及作者的實(shí)踐經(jīng)驗(yàn)出發(fā)贰拿。詳細(xì)介紹如何iOS中使用HTTS以及使用時(shí)需要注意的細(xì)節(jié)問題。
HTTPS基本原理
首先我們以網(wǎng)絡(luò)傳輸?shù)?個(gè)層來快速的弄懂HTTP和HTTPS的基本區(qū)別熄云。
HTTP傳輸:
HTTPS傳輸:
一眼就可以看出區(qū)別了吧膨更,其實(shí)HTTPS就是在HTTP的基礎(chǔ)上,在傳輸層和會(huì)話層之間了一個(gè)SSL層缴允,簡單來說都作用是負(fù)責(zé)數(shù)據(jù)的加解密荚守,從而保證了數(shù)據(jù)都安全。
SSL(Security Socket Layer 安全套接層) 最初1994年Netscape開發(fā),專門用于保護(hù)Web通訊.保護(hù)瀏覽器和服務(wù)器之間的通信,在客戶和服務(wù)器之間提供服務(wù)器鑒別练般、可選客戶鑒別和加密通信信道矗漾。使用TCP提供一種可靠的端對端的安全服務(wù)。
版本和歷史
1.0薄料,不成熟 2.0敞贡,基本上解決了Web通訊的安全問題
Microsoft公司發(fā)布了PCT(Private Communication Technology),并在IE中支持 3.0摄职,1996年發(fā)布誊役,增加了一些算法,修改了一些缺陷
TLS 1.0(Transport Layer Security傳輸層安全協(xié)議, 也被稱為SSL 3.1)谷市,1997年IETF發(fā)布了Draft蛔垢,同時(shí),Microsoft宣布放棄PCT迫悠,與Netscape一起支持TLS 1.0
1999年鹏漆,發(fā)布RFC 2246(The TLS Protocol v1.0)
蘋果現(xiàn)在要求使用的是TLS1.2。待會(huì)我會(huì)繼續(xù)提到這個(gè)問題创泄。
SSL的主要功能
客戶端驗(yàn)證服務(wù)器 客戶段與服務(wù)器選擇彼此支持的算法 服務(wù)器驗(yàn)證客戶端(可選) 使用公開密鑰算法產(chǎn)生共享的密鑰
當(dāng)然艺玲,HTTPS比較難以理解的還是它的協(xié)議,尤其是握手協(xié)議鞠抑,考慮到篇幅的原因板驳,這里不過多的講解。
官方相關(guān)文檔解讀
App Transport Security (ATS)在iOS 9.0之后就默認(rèn)開啟了碍拆。也就是我嗎之前請求HTTPS的時(shí)候NSAllowsArbitraryLoads這個(gè)key的值默認(rèn)設(shè)置為NO了,而且這個(gè)鍵現(xiàn)在在NSAppTransportSecurity字典的第一層。
NSAppTransportSecurity : Dictionary {
NSAllowsArbitraryLoads : Boolean
NSAllowsArbitraryLoadsForMedia : Boolean
NSAllowsArbitraryLoadsInWebContent : Boolean
NSAllowsLocalNetworking : Boolean
NSExceptionDomains : Dictionary {
: Dictionary {
NSIncludesSubdomains : Boolean
NSExceptionAllowsInsecureHTTPLoads : Boolean
NSExceptionMinimumTLSVersion : String
NSExceptionRequiresForwardSecrecy : Boolean// Default value is YES
NSRequiresCertificateTransparency : Boolean
}
}
}
除了NSExceptionRequiresForwardSecrecy默認(rèn)的值時(shí)YES感混。其它的默認(rèn)的值都是NO端幼。
NSAppTransportSecurity字典分兩個(gè)層級(jí)配置,前面四個(gè)
NSAllowsArbitraryLoads弧满,NSAllowsArbitraryLoadsForMedia,
NSAllowsArbitraryLoadsInWebConten婆跑, NSAllowsLocalNetworking 是對整個(gè)APP全局的配置。如果我們需要對某個(gè)域名有區(qū)分的對待就需要在NSExceptionDomains里面進(jìn)行相應(yīng)的配置庭呜。
NSAllowsArbitraryLoads
設(shè)置為YES的話滑进,就會(huì)使得除了開發(fā)者在NSExceptionDomains里面配置的域名以外所有的網(wǎng)絡(luò)連接不受限制。
如果你設(shè)置為YES的話募谎,需要在提審核的時(shí)候說明這樣做的原因扶关。
NSAllowsArbitraryLoadsForMedia
設(shè)置為YES的話,所有在APP里面使用AV Foundation framework加載的視頻都不會(huì)被限制数冬。如果不設(shè)置的話节槐,就僅用于加載已加密的媒體,例如由FairPlay或安全HLS保護(hù)的文件拐纱,并且不包含個(gè)人信息铜异。
如果你設(shè)置為YES的話,同樣也是需要在提審核的時(shí)候說明你這樣做的原因秸架。
NSAllowsArbitraryLoadsInWebContent
如果你設(shè)置為YES的話,系統(tǒng)會(huì)禁用對來自Web視圖的請求的所有ATS限制揍庄,也就是你的WebView的請求不不一定需要HTTPS,APP就可以使用嵌入式瀏覽器來顯示任意內(nèi)容东抹,但是應(yīng)用的其他部分還是需要用ATS蚂子。
如果你設(shè)置為YES的話,同樣也是需要在提審核的時(shí)候說明你這樣做的原因府阀。
NSAllowsLocalNetworking
設(shè)置為YES的話就允許加載本地資源缆镣。
NSExceptionDomains
NSExceptionDomains其實(shí)是相當(dāng)于NSAllowsArbitraryLoads的一個(gè)子集。后者是全局的作用试浙,而前者主要是用于對某些域名的限制作用董瞻。他的主要作用其實(shí)就是用于們自簽名的證書,具體使用細(xì)節(jié)我會(huì)在后面具體介紹田巴。
NSExceptionDomains字典里面各鍵的值意義如下钠糊。
NSIncludesSubdomains
默認(rèn)為NO,如果設(shè)置為YES,則表示當(dāng)前設(shè)置域名的所有子域名也使用同樣的配置
NSExceptionAllowsInsecureHTTPLoads
允許不安全的HTTP請求壹哺,這里所謂的不安全抄伍,不代表改變了 Transport Layer Security (TLS)或是事HTTPS的請求。所謂的不安全主要是因?yàn)槭褂米院灻淖C書管宵,沒有經(jīng)過CA認(rèn)證所以蘋果并不知道是不是安全的截珍,如果開發(fā)者允許那么蘋果也允許加載攀甚。
設(shè)置為YES,在審核的時(shí)候你需要說明原因
NSExceptionMinimumTLSVersion
這個(gè)屬性用于表面你的HTTPS的TLS版本,因?yàn)樘O果默認(rèn)是支持TLS1.2岗喉,所以如果你使用了較低的版本你你需要自己指明秋度。
設(shè)置為這個(gè)鍵后,在審核的時(shí)候你也需要說明原因。
NSExceptionRequiresForwardSecrecy
如果設(shè)置為NO钱床,則允許不支持完全前向保密(PFS)的TLS密碼(對于指定的域名)荚斯。 默認(rèn)值為YES。關(guān)于完全正向保密查牌,可以看這篇文章TLS完美前向保密(perfect forward secrecy)翻譯事期。
NSRequiresCertificateTransparency
如果設(shè)置為YES,則對于命名域的服務(wù)器證書纸颜,需要有效的簽名證書透明度的時(shí)間戳兽泣。 默認(rèn)值為NO。
以上這么多內(nèi)容懂衩,對于iOS開發(fā)者來自說最重要的信息就是有五個(gè)鍵撞叨,如果開發(fā)者不使用默認(rèn)值,則需要在審核的時(shí)候進(jìn)行說明浊洞。這五個(gè)鍵是
NSAllowsArbitraryLoads
NSAllowsArbitraryLoadsForMedia
NSAllowsArbitraryLoadsInWebContent
NSExceptionAllowsInsecureHTTPLoads
NSExceptionMinimumTLSVersion
這對于嵌入了很多網(wǎng)頁或是視頻的APP來說感覺會(huì)比較麻煩一些牵敷。
使用CA頒發(fā)證書的開發(fā)者
如果想要簡單實(shí)用,公司又壕氣法希,那就推薦使用這種方法枷餐。就是花錢買一個(gè)CA機(jī)構(gòu)頒發(fā)的證書,也可以是CA機(jī)構(gòu)授權(quán)二級(jí)或是三級(jí)機(jī)構(gòu)頒發(fā)的證書苫亦,國內(nèi)很多的頒發(fā)證書的公司毛肋,可以直接找他們買。
證書的算法使用RSA還是圓錐曲線(ECC)并沒有太大區(qū)別屋剑,我見過的大部分是RSA算法的徒溪。
弄好證書后然痊,丟給后臺(tái)鳍鸵,讓他們搭建HTTPS的服務(wù)器铅协,理論上iOS端只需要修改地址為HTTPS的地址就可以適配成功。但是還是做一些配置來保證萬無一失吧巍膘。
如果項(xiàng)目使用的是AFNetWorking的話厂财。只需要如下幾句代碼就可以搞定。
NSString *urlString = @"https://www.apple.com";
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
[securityPolicy setValidatesDomainName:YES];
manager.securityPolicy = securityPolicy;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
// request
[manager GET:urlString
parameters:nil
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id? _Nullable responseObject) {
NSDictionary * array = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"OK === %@",array);
NSString *htmlString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(@"%@",htmlString);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"error ==%@",error.description);
}];
不過這里最值得關(guān)注的是AFSSLPinningModeNone,我們點(diǎn)進(jìn)去看AF的源碼的時(shí)候峡懈,就會(huì)發(fā)現(xiàn)這是一個(gè)枚舉璃饱,主要包括了三種類型。
enum{
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
}
`AFSSLPinningModeNone`
Do not used pinned certificates to validate servers.
`AFSSLPinningModePublicKey`
Validate host certificates againstpublickeys of pinned certificates.
`AFSSLPinningModeCertificate`
Validate host certificates against pinned certificates.
*/
AFSSLPinningModeNon表示不做SSL pinning肪康,只跟瀏覽器一樣在系統(tǒng)的信任機(jī)構(gòu)列表里驗(yàn)證服務(wù)端返回的證書荚恶。若證書是信任機(jī)構(gòu)簽發(fā)的就會(huì)通過撩穿,若是自己服務(wù)器生成的證書,是不會(huì)通過的裆甩。
AFSSLPinningModeCertificate表示用證書綁定方式驗(yàn)證證書冗锁,需要客戶端保存有服務(wù)端的證書拷貝,這里驗(yàn)證分兩步嗤栓,第一步驗(yàn)證證書的域名/有效期等信息,第二步是對比服務(wù)端返回的證書跟客戶端返回的是否一致箍邮。
AFSSLPinningModePublicKey是用證書綁定方式驗(yàn)證茉帅,客戶端要有服務(wù)端的證書拷貝,只是驗(yàn)證時(shí)只驗(yàn)證證書里的公鑰锭弊,不驗(yàn)證證書的有效期等信息堪澎。只要公鑰是正確的,就能保證通信不會(huì)被竊聽味滞,因?yàn)橹虚g人沒有私鑰樱蛤,無法解開通過公鑰加密的數(shù)據(jù)。
也就是如果你使用后面兩種模式你的工程里面需要導(dǎo)入cer證書文件剑鞍。這個(gè)文件的路徑隨意昨凡,AFNetWorking會(huì)自動(dòng)替你尋找。如果你覺得不放心也可以使用下面的代碼直接指定文件蚁署。
NSData *certData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"12306"ofType:@"cer"]];
NSSet *cerSet? = [NSSet setWithObject:certData];
if(certData){
[securityPolicy setPinnedCertificates:cerSet];
}
這樣就可以去測試看看能不能跑起來了~
使用自簽名的證書
使用自簽名的證書比使用CA的要復(fù)雜一些便脊。國內(nèi)最著名的使用自簽名證書的就是12306了。我們可以看看12306的證書文件光戈。
首先我們需要下載它證書哪痰,對于網(wǎng)絡(luò)上支持HTTPS的網(wǎng)站我們都可以通過下列方式獲取相應(yīng)的證書。
openssl s_client -connect kyfw.12306.cn:443/dev/null| openssl x509 -outform DER >12306.cer
openssl s_client -connect www.apple.com:443/dev/null| openssl x509 -outform DER > apple.cer
執(zhí)行命令后就會(huì)在當(dāng)前目錄下生成一個(gè)12306.cer的文件久妆,我們把文件拷貝到Xcode里面可以查看相應(yīng)的證書信息晌杰。
這個(gè)SRCA是12306自己搞定一個(gè)證書機(jī)構(gòu),也是沒有CA認(rèn)證過的筷弦。所以也算是一個(gè)自簽名證書肋演。
為了方便測試,我這里使用12306的證書來講解自簽證書配置奸笤。首先我不作任何配置使用如下代碼測試網(wǎng)絡(luò)能否聯(lián)通
NSString *urlString = @"https://kyfw.12306.cn/otn";
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
[securityPolicy setAllowInvalidCertificates:YES];
[securityPolicy setValidatesDomainName:YES];
manager.securityPolicy = securityPolicy;
//用于指定文件
//??? NSData *certData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"12306" ofType:@"cer"]];
//??? NSSet *cerSet? = [NSSet setWithObject:certData];
//??? if(certData){
//??????? [securityPolicy setPinnedCertificates:cerSet];
//??? }
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:urlString
parameters:nil
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id? _Nullable responseObject) {
NSDictionary * array = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"OK === %@",array);
NSString *htmlString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(@"%@",htmlString);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"error ==%@",error.description);
}];
可以看到結(jié)果如下
我們并不能正常訪問1230網(wǎng)站惋啃,我們使用源碼的方式打開plist文件
然后在里面添加如下配置
NSAppTransportSecurity
NSExceptionDomains
kyfw.12306.cn
NSIncludesSubdomains
NSExceptionRequiresForwardSecrecy
NSExceptionAllowsInsecureHTTPLoads
再次運(yùn)行程序訪問12306.可以看到成功加載到了數(shù)據(jù)
關(guān)于這個(gè)配置文件怎么配置呢。如果不是很清楚的話监右,建議使用蘋果提供的工具
/usr/bin/nscurl --ats-diagnostics [--verbose] URL
比如我測試蘋果的網(wǎng)站(我只截取了部分信息)
hly:Desktop jianquan$? /usr/bin/nscurl --ats-diagnostics --verbose https://www.apple.com
Starting ATS Diagnostics
Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://www.apple.com.
A test will"PASS"ifURLSession:task:didCompleteWithError: returns a nil error.
================================================================================
Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
Result : PASS
---
TLSv1.1with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains =???? {
"www.apple.com"=???????? {
NSExceptionAllowsInsecureHTTPLoads =true;
NSExceptionMinimumTLSVersion ="TLSv1.1";
NSExceptionRequiresForwardSecrecy =false;
};
};
}
Result : PASS
---
---
TLSv1.0with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains =???? {
"www.apple.com"=???????? {
NSExceptionAllowsInsecureHTTPLoads =true;
NSExceptionMinimumTLSVersion ="TLSv1.0";
NSExceptionRequiresForwardSecrecy =false;
};
};
}
Result : PASS
---
Result : PASS表明我可以使用推薦的鍵值對來適配HTTPS边灭。對于自己的域名我們也可以先讓后臺(tái)配置好HTTPS之后,然后以上方法健盒,就能更快更好的適配了绒瘦。
對于自簽的證書我們按照上面的教程配置就好称簿。但是你會(huì)問我,BB可半天惰帽,證書在那里呢憨降?別急下面一節(jié)我專門講解如何生成自己的證書。
生成自簽名的證書
我在大二的時(shí)候?qū)戇^一篇文章该酗,專門研究了數(shù)字證書的相關(guān)東西數(shù)字證書及其簡單數(shù)字簽名的實(shí)現(xiàn)(java實(shí)現(xiàn))
首先請確保你的電腦安裝了jdk授药,沒有的話請到官網(wǎng)下載安裝。Mac或是Windows下生成證書的方式都是一樣的呜魄。java的跨平臺(tái)性—悔叽。
安裝好java后,打開終端,生成相應(yīng)的證書
keytool -genkey -alias JoySeeDog -keyalg RSA -keysize1024-keystore JoySeeDog -validity365
這里面可以配置的參數(shù)包括如下爵嗅,可以根據(jù)自己需求使用娇澎。
-alias?????????????????? 要處理的條目的別名
-keyalg ??????????????? 密鑰算法名稱
-keysize ????????????? 密鑰位大小
-sigalg ??????????????? 簽名算法名稱
-destalias ????????? 目標(biāo)別名
-dname ????????????????? 唯一判別名
-startdate ????????? 證書有效期開始日期/時(shí)間
-ext ??????????????????? X.509擴(kuò)展
-validity ???????????? 有效天數(shù)
-keypass?????????????????? 密鑰口令
-keystore ??????????? 密鑰庫名稱
-storepass???????????????? 密鑰庫口令
-storetype ????????? 密鑰庫類型
-providername ??? 提供方名稱
-providerclass ? 提供方類名
-providerarg?????????????? 提供方參數(shù)
-providerpath ??????? 提供方類路徑
-v????????????????????????????? 詳細(xì)輸出
-protected通過受保護(hù)的機(jī)制的口令
然后會(huì)提示你輸入相應(yīng)的信息,如下圖所示
然后可以使用如下命令查看相應(yīng)的信息睹晒。
keytool -list -v -keystore JoySeeDog
相關(guān)信息如下
確認(rèn)證書的相關(guān)信息沒有問題后趟庄,就可以導(dǎo)出相應(yīng)的證書了。
-rfc??????????????????????????? 以 RFC 樣式輸出
-alias?????????????????? 要處理的條目的別名
-file ??????????????? 輸出文件名
-keystore ??????????? 密鑰庫名稱
-storepass???????????????? 密鑰庫口令
-storetype ????????? 密鑰庫類型
-providername ??? 提供方名稱
-providerclass ? 提供方類名
-providerarg?????????????? 提供方參數(shù)
-providerpath ??????? 提供方類路徑
-v????????????????????????????? 詳細(xì)輸出
-protected通過受保護(hù)的機(jī)制的口令
可以看到當(dāng)前目錄下出現(xiàn)了一個(gè)JoySeeDog和一個(gè)JoySeeDog.cer文件伪很。準(zhǔn)確的說這個(gè)JoySeeDog文件在第一步就出現(xiàn)了戚啥,保存好這兩個(gè)文件。以后也許會(huì)用到是掰。
把證書拖入Xcode虑鼎,可以看到嶄新的證書做好了~