//點擊請求數(shù)據(jù)
- (void)btnAction{
NSString*urlString=@"https://192.168.30.91/";
AFSecurityPolicy*securityPolicy = [AFSecurityPolicypolicyWithPinningMode:AFSSLPinningModeNone];
//是否允許,NO--不允許無效的證書...
//因為大多數(shù)都是自己給自己頒布的證書,對于蘋果來說是不被信任的,是無效的,所以要設(shè)置YES.
securityPolicy.allowInvalidCertificates=YES;
//設(shè)置證書如果不設(shè)置,可以自動讀取工程里的cer文件
// 1.zhengshu.cer ca.cer(一個是服務(wù)器證書,一個服務(wù)器CA根證書,哪一個都可以)
//service.cer 2.服務(wù)器證書
//securityPolicy.pinnedCertificates=set;
//validatesDomainName是否需要驗證域名映之,默認為YES谚殊;
//假如證書的域名與你請求的域名不一致芥永,需把該項設(shè)置為NO奄侠;如設(shè)成NO的話,即服務(wù)器使用其他可信任機構(gòu)頒發(fā)的證書迷雪,也可以建立連接限书,這個非常危險,建議打開章咧。
//置為NO倦西,主要用于這種情況:客戶端請求的是子域名,而證書上的是另外一個域名慧邮。因為SSL證書上的域名是獨立的调限,假如證書上注冊的域名是www.google.com,那么mail.google.com是無法驗證通過的误澳;當(dāng)然,有錢可以注冊通配符的域名*.google.com秦躯,但這個還是比較貴的忆谓。
//如置為NO,建議自己添加對應(yīng)域名的校驗邏輯踱承。
//一.zhengshu.cer[https://192.168.30.91/](https://192.168.30.91/)無需設(shè)置infoplist
//二.service.cer[https://192.168.30.55:8443/IEMS_APP/](https://192.168.30.55:8443/IEMS_APP/)//模擬器必須設(shè)置info.plist YES真機不用設(shè)置info.plist
//兩種都可以加密數(shù)據(jù)安全,防止中間人抓包工具
securityPolicy.validatesDomainName=NO;
AFHTTPSessionManager* manager = [AFHTTPSessionManagermanager];
manager.responseSerializer.acceptableContentTypes=[NSSetsetWithObjects:@"text/html",nil];
manager.requestSerializer.cachePolicy=NSURLRequestReloadIgnoringLocalCacheData;
__weaktypeof(self)weakSelf =self;
//重寫這個方法就能提供客戶端驗證
[managersetSessionDidBecomeInvalidBlock:^(NSURLSession*_Nonnullsession,NSError*_Nonnullerror) {
NSLog(@"setSessionDidBecomeInvalidBlock");
}];
[managersetSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session,NSURLAuthenticationChallenge*challenge,NSURLCredential*__autoreleasing*_credential) {
NSURLSessionAuthChallengeDispositiondisposition =NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasingNSURLCredential*credential =nil;
if([challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodServerTrust]) {
if([manager.securityPolicyevaluateServerTrust:challenge.protectionSpace.serverTrustforDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];
if(credential) {
disposition =NSURLSessionAuthChallengeUseCredential;
}else{
disposition =NSURLSessionAuthChallengePerformDefaultHandling;
}
}else{
disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}else{
// client authentication
SecIdentityRefidentity =NULL;
SecTrustReftrust =NULL;
NSString*p12 = [[NSBundlemainBundle]pathForResource:@"client.key"ofType:@"p12"];
NSFileManager*fileManager =[NSFileManagerdefaultManager];
if(![fileManagerfileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData*PKCS12Data = [NSDatadataWithContentsOfFile:p12];
if([[weakSelfclass]extractIdentity:&identityandTrust:&trustfromPKCS12Data:PKCS12Data])
{
SecCertificateRefcertificate =NULL;
SecIdentityCopyCertificate(identity, &certificate);
constvoid*certs[] = {certificate};
CFArrayRefcertArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
credential =[NSURLCredentialcredentialWithIdentity:identitycertificates:(__bridgeNSArray*)certArraypersistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
*_credential = credential;
returndisposition;
}];
manager.securityPolicy= securityPolicy;
manager.responseSerializer= [AFHTTPResponseSerializerserializer];
[managerGET:urlStringparameters:nilprogress:^(NSProgress*_NonnulldownloadProgress) {
}success:^(NSURLSessionDataTask*_Nonnulltask,id_NullableresponseObject) {
NSString*html=[[NSStringalloc]initWithData:responseObjectencoding:NSUTF8StringEncoding];
self.label.text=html;
}failure:^(NSURLSessionDataTask*_Nullabletask,NSError*_Nonnullerror) {
self.label.textColor=[UIColorredColor];
self.label.text=error.debugDescription;
}];
}
+(BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData*)inPKCS12Data {
OSStatussecurityError =errSecSuccess;
//client certificate password
NSDictionary*optionsDictionary = [NSDictionarydictionaryWithObject:@"123456"
forKey:(__bridgeid)kSecImportExportPassphrase];
CFArrayRefitems =CFArrayCreate(NULL,0,0,NULL);
securityError =SecPKCS12Import((__bridgeCFDataRef)inPKCS12Data,(__bridgeCFDictionaryRef)optionsDictionary,&items);
if(securityError ==0) {
CFDictionaryRefmyIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
constvoid*tempIdentity =NULL;
tempIdentity=CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
constvoid*tempTrust =NULL;
tempTrust =CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
}else{
NSLog(@"Failedwith error code %d",(int)securityError);
returnNO;
}
returnYES;
}
/**
一.非瀏覽器應(yīng)用(iOS app)與服務(wù)器AFNetworking HTTPS ssl認證
雖然是HTTPS的網(wǎng)站,但是服務(wù)器端也要設(shè)置對客戶端提供認證證書忽略(此處與瀏覽器與服務(wù)器SSl雙向認證不太一樣).
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
//是否允許,NO--不允許無效的證書,設(shè)置允許(YES)(因為我們的證書一般都是自簽名的,并不是像谷歌那種大型網(wǎng)站權(quán)威頒布的官方認證證書)
securityPolicy.allowInvalidCertificates=YES;
//validatesDomainName是否需要驗證域名倡缠,默認為YES;
//假如證書的域名與你請求的域名不一致茎活,需把該項設(shè)置為NO昙沦;如設(shè)成NO的話,即服務(wù)器使用其他可信任機構(gòu)頒發(fā)的證書载荔,也可以建立連接盾饮,這個非常危險,建議打開。
//置為NO丘损,主要用于這種情況:客戶端請求的是子域名普办,而證書上的是另外一個域名。因為SSL證書上的域名是獨立的徘钥,假如證書上注冊的域名是www.google.com衔蹲,那么mail.google.com是無法驗證通過的;當(dāng)然呈础,有錢可以注冊通配符的域名*.google.com舆驶,但這個還是比較貴的。
//如置為NO而钞,建議自己添加對應(yīng)域名的校驗邏輯沙廉。
//securityPolicy.validatesDomainName=NO;
manager.securityPolicy = securityPolicy;
這幾句代碼是必需的,訪問HTTPS(比http多加這些).
AFSSLPinningModeNone,AFSSLPinningModePublicKey,AFSSLPinningModeCertificate,這三個屬性是上邊紅色位置的三種情況.
SSL Pinning可以理解為證書綁定,是指客戶端直接保存服務(wù)端的證書笨忌,建立https連接時直接對比服務(wù)端返回的和客戶端保存的兩個證書是否一樣蓝仲,一樣就表明證書是真的,不再去系統(tǒng)的信任證書機構(gòu)里尋找驗證官疲。這適用于非瀏覽器應(yīng)用袱结,因為瀏覽器跟很多未知服務(wù)端打交道,無法把每個服務(wù)端的證書都保存到本地途凫,但CS架構(gòu)的像手機APP事先已經(jīng)知道要進行通信的服務(wù)端垢夹,可以直接在客戶端保存這個服務(wù)端的證書用于校驗。
為什么直接對比就能保證證書沒問題维费?如果中間人從客戶端取出證書果元,再偽裝成服務(wù)端跟其他客戶端通信,它發(fā)送給客戶端的這個證書不就能通過驗證嗎犀盟?確實可以通過驗證而晒,但后續(xù)的流程走不下去,因為下一步客戶端會用證書里的公鑰加密阅畴,中間人沒有這個證書的私鑰就解不出內(nèi)容倡怎,也就截獲不到數(shù)據(jù),這個證書的私鑰只有真正的服務(wù)端有贱枣,中間人偽造證書主要偽造的是公鑰监署。
為什么要用SSLPinning?正常的驗證方式不夠嗎纽哥?如果服務(wù)端的證書是從受信任的的CA機構(gòu)頒發(fā)的钠乏,驗證是沒問題的,但CA機構(gòu)頒發(fā)證書比較昂貴春塌,小企業(yè)或個人用戶可能會選擇自己頒發(fā)證書晓避,這樣就無法通過系統(tǒng)受信任的CA機構(gòu)列表驗證這個證書的真?zhèn)瘟舜睾矗孕枰猄SL Pinning這樣的方式去驗證。
在iOS開發(fā)中,從Xcode7和iOS9開始,Apple提升了App的網(wǎng)絡(luò)安全性,App默認只能進行對采用權(quán)威機構(gòu)簽名頒發(fā)證書的Web站點進行訪問(信任的HTTPS),而自簽名的證書的HTTPS站點也被列為屬于例外,所以我們需要在App的Info.plist中單獨為我們的域名設(shè)置Exception Domains"白名單”.也可以使用放開全部的設(shè)置NSAllowsArbitraryLoads為true.
1.AFSSLPinningModeNone
這個模式表示不做SSL pinning够滑,只跟瀏覽器一樣在系統(tǒng)的信任機構(gòu)列表里驗證服務(wù)端返回的證書垦写。若證書是信任機構(gòu)簽發(fā)的就會通過,若是自己服務(wù)器生成的證書彰触,這里是不會通過的梯投。
所以要用到上面securityPolicy.allowInvalidCertificates=YES;
必須修改infoplist文件即:像http請求一樣(App TransportSecurity Settings——>Allow Arbitrary LoadsYES.)
這個屬性使數(shù)據(jù)不安全,一些中間人(抓包工具)會截取到數(shù)據(jù)接口.
2.AFSSLPinningModeCertificate:
這個模式表示用證書綁定方式驗證證書,需要客戶端保存有服務(wù)端的證書拷貝况毅,這里驗證分兩步分蓖,第一步驗證證書的域名/有效期等信息,第二步是對比服務(wù)端返回的證書跟客戶端返回的是否一致尔许。
驗證服務(wù)器身份在沒有使用代理的時候可以正常訪問服務(wù)器的資源,但是一旦用戶給手機網(wǎng)絡(luò)設(shè)置使用了如Charle那樣的HTTPS/SSL代理服務(wù),則會出現(xiàn)服務(wù)器證書驗證失敗,SSL網(wǎng)絡(luò)連接會斷開,老板再也不用擔(dān)心數(shù)據(jù)接口被人抓包或者代理給扒出來了.故達到防止中間人攻擊的效果.
無需修改infoplist(自己測試了).
這個證書是服務(wù)器端的證書(.cer文件)(可以是根證書,也可以是證書,總之必須是服務(wù)器的),把這個證書直接copy到Xcode工程,程序會自動讀取.cer文件.
3.AFSSLPinningModePublicKey (和第二個差不多)
這個模式同樣是用證書綁定方式驗證么鹤,客戶端要有服務(wù)端的證書拷貝,只是驗證時只驗證證書里的公鑰味廊,不驗證證書的有效期等信息蒸甜。只要公鑰是正確的,就能保證通信不會被竊聽余佛,因為中間人沒有私鑰柠新,無法解開通過公鑰加密的數(shù)據(jù)。
二.客戶端瀏覽器與服務(wù)器端HTTPS雙向認證
①瀏覽器發(fā)送一個連接請求給安全服務(wù)器辉巡。
②服務(wù)器將自己的證書恨憎,以及同證書相關(guān)的信息發(fā)送給客戶瀏覽器。
③客戶瀏覽器檢查服務(wù)器送過來的證書是否是由自己信賴的CA中心所簽發(fā)的郊楣。如果是憔恳,就繼續(xù)執(zhí)行協(xié)議;如果不是净蚤,客戶瀏覽器就給客戶一個警告消息:警告客戶這個證書不是可以信賴的钥组,詢問客戶是否需要繼續(xù)。
④接著客戶瀏覽器比較證書里的消息今瀑,例如域名和公鑰者铜,與服務(wù)器剛剛發(fā)送的相關(guān)消息是否一致,如果是一致的放椰,客戶瀏覽器認可這個服務(wù)器的合法身份。
⑤服務(wù)器要求客戶發(fā)送客戶自己的證書愉粤。收到后砾医,服務(wù)器驗證客戶的證書,如果沒有通過驗證衣厘,拒絕連接如蚜;如果通過驗證压恒,服務(wù)器獲得用戶的公鑰。
⑥客戶瀏覽器告訴服務(wù)器自己所能夠支持的通訊對稱密碼方案错邦。
⑦服務(wù)器從客戶發(fā)送過來的密碼方案中探赫,選擇一種加密程度最高的密碼方案,用客戶的公鑰加過密后通知瀏覽器撬呢。
⑧瀏覽器針對這個密碼方案伦吠,選擇一個通話密鑰,接著用服務(wù)器的公鑰加過密后發(fā)送給服務(wù)器魂拦。
⑨服務(wù)器接收到瀏覽器送過來的消息毛仪,用自己的私鑰解密,獲得通話密鑰芯勘。
⑩服務(wù)器箱靴、瀏覽器接下來的通訊都是用對稱密碼方案,對稱密鑰是加過密的荷愕。
這兩種形式雖然同樣是ssl認證,本人感覺并不是一個意思.開始的時候是按照瀏覽器與服務(wù)器端的ssl雙向認證來做AFNetworking,服務(wù)器端設(shè)置必須客戶端提供證書才能訪問網(wǎng)站,否則403,但是試了好多次都是請求失敗(萬念俱灰).查看別人的HTTPS接口例子不一樣和之前做的.于是認為這兩種驗證方式是不同的.把服務(wù)器端必需的客戶端證書,改成忽略,經(jīng)過測試,用AFNetworking使用服務(wù)器端證書,也能達到加密的作用,而且AFNetworking,確實是這么應(yīng)用的.代碼很少,但是這里面是非瀏覽器應(yīng)用如何應(yīng)用,以及和瀏覽器的雙向認證區(qū)分開.
我是從下面這些個說法中總結(jié)的:
//瀏覽器與服務(wù)端雙向認證
http://www.reibang.com/p/8b4312c34808
http://www.reibang.com/p/20d5fb4cd76d
//https為什么還要改infoplist如何防止抓包(中間人)
http://www.reibang.com/p/c6a903da8346
非瀏覽器應(yīng)用AFNetworking SSLPINNing (雙向認證)
http://www.cocoachina.com/ios/20140916/9632.html
HTTPS接口例子:
https://tv.diveinedu.com/channel/