iOS https雙向認(rèn)證項(xiàng)目適配詳解

Https IOS客戶端適配包括三方面:

1乏盐、接口雙向認(rèn)證

2、webView雙向認(rèn)證

3屯曹、imageView忽略認(rèn)證

直接上核心代碼:

1谆刨、接口雙向認(rèn)證

項(xiàng)目中使用的是AFNetworking, HYBNetworking工具類

+ (AFHTTPSessionManager *)manager {
    @synchronized (self) {
        // 只要不切換baseurl导狡,就一直使用同一個(gè)session manager
        if (sg_sharedManager == nil || sg_isBaseURLChanged) {
        
            // 開啟轉(zhuǎn)圈圈
            [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
            
            AFHTTPSessionManager *manager = nil;;
            if ([self baseUrl] != nil) {
                manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:[self baseUrl]]];
            } else {
                manager = [AFHTTPSessionManager manager];
            }
            
            switch (sg_requestType) {
                case kHYBRequestTypeJSON: {
                    //manager.requestSerializer = [AFJSONRequestSerializer serializer];
                    break;
                }
                case kHYBRequestTypePlainText: {
                    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
                    break;
                }
                default: {
                    break;
                }
            }
            
            switch (sg_responseType) {
                case kHYBResponseTypeJSON: {
                    manager.responseSerializer = [AFJSONResponseSerializer serializer];
                    break;
                }
                case kHYBResponseTypeXML: {
                    manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
                    break;
                }
                case kHYBResponseTypeData: {
                    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
                    break;
                }
                default: {
                    break;
                }
            }
            
            manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
            
            
            for (NSString *key in sg_httpHeaders.allKeys) {
                if (sg_httpHeaders[key] != nil) {
                    [manager.requestSerializer setValue:sg_httpHeaders[key] forHTTPHeaderField:key];
                }
            }
            
            manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
                                                                                      @"text/html",
                                                                                      @"text/json",
                                                                                      @"text/javascript"]];
            
            manager.requestSerializer.timeoutInterval = sg_timeout;
            
            // 設(shè)置允許同時(shí)最大并發(fā)數(shù)量鉴逞,過大容易出問題
            manager.operationQueue.maxConcurrentOperationCount = 3;
            
            //關(guān)閉緩存避免干擾測(cè)試
            manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
            
            //基于公鑰設(shè)置客服端安全策略 ssl
            manager.securityPolicy = [self customSecurityPolicy];
            //客服端利用p12驗(yàn)證服務(wù)器
            [self checkCredential:manager];
            
            sg_sharedManager = manager;
        }
    }
  
  return sg_sharedManager;
}

#pragma mark - ********** SSL校驗(yàn) **********
/** SSL 1.單向驗(yàn)證 */
+ (AFSecurityPolicy*)customSecurityPolicy {
    
    // AFSSLPinningModeCertificate:需要客戶端預(yù)先保存服務(wù)端的證書(自建證書)
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    NSString * cerPath  = [[NSBundle mainBundle] pathForResource:kHttpsServiceCer ofType:@"cer"];
    NSData *certData    = [NSData dataWithContentsOfFile:cerPath];
    NSSet   *dataSet    = [NSSet setWithArray:@[certData]];
    // 自建證書的時(shí)候记某,提供相應(yīng)的證書
    [securityPolicy setPinnedCertificates:dataSet];
    // 是否允許無效證書(自建證書)
    [securityPolicy setAllowInvalidCertificates:YES];
    // 是否需要驗(yàn)證域名
    [securityPolicy setValidatesDomainName:NO];
    
    return securityPolicy;
}

/**
 客戶端驗(yàn)證服務(wù)器信任憑證

 @param manager AFURLSessionManager
 */
+ (void)checkCredential:(AFURLSessionManager *)manager
{
    //為了方便測(cè)試
    [manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
        NSLog(@"%s error:%@",__FUNCTION__,error);
    }];
    
    wSelf(self);
    __weak typeof(manager) weakManager = manager;
    [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
        
        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __autoreleasing NSURLCredential *credential = nil;
        NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);
        //判斷當(dāng)前校驗(yàn)的是客戶端證書還是服務(wù)器證書
        if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            // 客戶端的安全策略來決定是否信任該服務(wù)器;不信任华蜒,則取消請(qǐng)求辙纬。
            //接口提示:已取消;是因?yàn)榭蛻舳嗽O(shè)置了需要驗(yàn)證域名
            if([weakManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                // 創(chuàng)建URL憑證
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;//使用(信任)證書
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;//默認(rèn)豁遭,忽略
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;//取消
            }
        } else {
            
            // client authentication
            SecIdentityRef identity = NULL;
            SecTrustRef trust       = NULL;
            NSString *p12 = [[NSBundle mainBundle] pathForResource:kHttpsClientP12 ofType:@"p12"];
            NSFileManager *fileManager =[NSFileManager defaultManager];
            
            if(![fileManager fileExistsAtPath:p12]){
                NSLog(@"client.p12:not exist");
            }else{
                
                NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
                if ([wSelf extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data]){
                    
                    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;
    }];
}

//解讀p12文件信息
+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
    OSStatus securityError = errSecSuccess;
    //client certificate password
    NSDictionary *optionsDic = [NSDictionary dictionaryWithObject:kHttpsP12Password
                                                                  forKey:(__bridge id)kSecImportExportPassphrase];
    
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError    = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDic,&items);
    
    if(securityError == errSecSuccess) {
        CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
        const void*tempIdentity = NULL;
        tempIdentity    = CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
        *outIdentity    = (SecIdentityRef)tempIdentity;
        const void*tempTrust = NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
        *outTrust = (SecTrustRef)tempTrust;
    } else {
        NSLog(@"Failedwith error code %d",(int)securityError);
        return NO;
    }
    return YES;
}

2叭喜、webView雙向認(rèn)證

@interface AgreementController ()<UIWebViewDelegate,NSURLConnectionDataDelegate>

@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) NSURL *baseUrl;
@property (nonatomic, strong) NSMutableData *data;//頁面緩存數(shù)據(jù)

@end

@implementation AgreementController

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.view bringSubviewToFront:self.navBarBackgroud];
    [MobClick beginLogPageView:@"AgreementVC"];
}
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [MobClick endLogPageView:@"AgreementVC"];
}

- (NSMutableData *)data
{
    if (_data == nil){
        _data = [[NSMutableData alloc] init];
    }
    return _data;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"用戶協(xié)議";
    
    _webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, kDeviceWidth, kDeviceHeight)];
    _webView.scalesPageToFit = YES;
    _webView.delegate = self;
    [_webView scalesPageToFit];
    [self.view addSubview:_webView];
    
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    NSString *url = [HYBNetworking absoluteUrlPath:kUrlAgreement params:params];
    self.baseUrl = [NSURL URLWithString:url];
    NSURLRequest *request = [NSURLRequest requestWithURL:self.baseUrl];
    [NSURLConnection connectionWithRequest:request delegate:self];

}

#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [MBProgressHUD hideHUD];
}

- (void)webViewDidStartLoad:(UIWebView *)webView
{
    [MBProgressHUD showMessage:@"正在加載中......."];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [MBProgressHUD hideHUD];
}

#pragma mark - NSURLConnectionDataDelegate
//接收到服務(wù)器返回的數(shù)據(jù)時(shí)調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"接收到的數(shù)據(jù)%zd",data.length);
    [self.data appendData:data];
}

//請(qǐng)求完畢
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"%s 請(qǐng)求完畢",__FUNCTION__);
    NSString *html = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
    [self.webView loadHTMLString:html baseURL:self.baseUrl];
}

//如果返回NO,將由系統(tǒng)自行處理. 返回YES將會(huì)由后續(xù)的didReceiveAuthenticationChallenge處理蓖谢。默認(rèn)為NO
- (BOOL)connection:(NSURLConnection*)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace*)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

-(void)connection:(NSURLConnection*)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
{
    NSLog(@"%s",__FUNCTION__);
    //判斷是否是信任服務(wù)器證書
    if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust){
        //創(chuàng)建一個(gè)憑據(jù)對(duì)象
        NSURLCredential *credential =
        [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        //告訴服務(wù)器客戶端信任證書
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    }else{
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

//允許跳過安全認(rèn)證
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host
{
    NSLog(@"%s",__FUNCTION__);
    return YES;
}

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    
    NSURLCredential * credential;
    
    assert(challenge != nil);
    
    credential = nil;
    
    NSLog(@"----received challenge----");
    
    NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
    
    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        NSLog(@"----server verify client----");
        
        NSString *host = challenge.protectionSpace.host;
        
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        
        BOOL validDomain = false;
        
        NSMutableArray *polices = [NSMutableArray array];
        if (validDomain) {
            [polices addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)host)];
        }else{
            [polices addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
        }
        
        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)polices);
        
        //pin mode for certificate
        
        NSString *path = [[NSBundle mainBundle] pathForResource:kHttpsServiceCer ofType:@"cer"];
        
        NSData *certData = [NSData dataWithContentsOfFile:path];
        
        NSMutableArray *pinnedCerts = [NSMutableArray arrayWithObjects:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData), nil];
        
        SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCerts);
        
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        
    } else {
        
        NSLog(@"----client verify server----");
        
        SecIdentityRef identity = NULL;
        
        SecTrustRef trust = NULL;
        
        NSString *p12 = [[NSBundle mainBundle] pathForResource:kHttpsClientP12 ofType:@"p12"];
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        
        if (![fileManager fileExistsAtPath:p12]) {
            
            NSLog(@"client.p12 file not exist!");
            
        }else{
            
            NSData *pkcs12Data = [NSData dataWithContentsOfFile:p12];
            
            if ([HYBNetworking extractIdentity:&identity andTrust:&trust fromPKCS12Data:pkcs12Data]) {
                
                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];
                
            }
            
        }
        
    }
    
    [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    
}

3捂蕴、imageView忽略認(rèn)證

NSURL *url =  [NSURL URLWithString:model.resume.head_img_small];

UIImage *placeholder = [UIImage imageNamed:@"photo.png"];

[_headView.avator  yy_setImageWithURL:url

placeholder:placeholder

options:YYWebImageOptionAllowInvalidSSLCertificates

completion:NULL];

特別說明

#define kHttpsServiceCer    @"server"          //服務(wù)器公鑰server.cer

#define kHttpsClientP12    @"client"          //服務(wù)器授權(quán)的p12:包含服務(wù)器信息+私鑰

#define kHttpsP12Password  @"123"      //訪問p12文件的密碼

2、webView使用

//請(qǐng)求并緩存頁面數(shù)據(jù)闪幽,避免重復(fù)請(qǐng)求接口
[NSURLConnection connectionWithRequest:request delegate:self];

//請(qǐng)求完畢
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"%s 請(qǐng)求完畢",__FUNCTION__);
    NSString *html = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
    [self.webView loadHTMLString:html baseURL:self.baseUrl];
}

3啥辨、“已取消”問題。部署好雙向認(rèn)證代碼后盯腌,請(qǐng)求接口出現(xiàn):提示 “已取消”溉知。是因?yàn)榭蛻舳嗽O(shè)置了需要驗(yàn)證域名。

// 是否需要驗(yàn)證域名
[securityPolicy setValidatesDomainName:NO];

附加:來源http://blog.csdn.net/duanbokan/article/details/50847612

20160310160503593.jpeg

20160310160519781-2.jpeg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腕够,一起剝皮案震驚了整個(gè)濱河市级乍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帚湘,老刑警劉巖玫荣,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腰湾,死亡現(xiàn)場(chǎng)離奇詭異翠桦,居然都是意外死亡渊啰,警方通過查閱死者的電腦和手機(jī)爷耀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焙贷,“玉大人撵割,你說我怎么就攤上這事∮澹” “怎么了睁枕?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沸手。 經(jīng)常有香客問我外遇,道長,這世上最難降的妖魔是什么契吉? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任跳仿,我火速辦了婚禮,結(jié)果婚禮上捐晶,老公的妹妹穿的比我還像新娘菲语。我一直安慰自己,他們只是感情好惑灵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布山上。 她就那樣靜靜地躺著,像睡著了一般英支。 火紅的嫁衣襯著肌膚如雪佩憾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天干花,我揣著相機(jī)與錄音妄帘,去河邊找鬼。 笑死池凄,一個(gè)胖子當(dāng)著我的面吹牛抡驼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肿仑,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼致盟,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了尤慰?” 一聲冷哼從身側(cè)響起馏锡,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎割择,沒想到半個(gè)月后眷篇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荔泳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蕉饼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虐杯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昧港,死狀恐怖擎椰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情创肥,我是刑警寧澤达舒,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叹侄,受9級(jí)特大地震影響巩搏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜趾代,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一贯底、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撒强,春花似錦禽捆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芽隆,卻和暖如春浊服,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摆马。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國打工臼闻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸿吆,地道東北人囤采。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像惩淳,于是被迫代替她去往敵國和親蕉毯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協(xié)議思犁。它實(shí)...
    香橙柚子閱讀 23,864評(píng)論 8 183
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫代虾、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,103評(píng)論 4 62
  • 爸爸復(fù)員的時(shí)候激蹲,正好趕上支援西部建設(shè)棉磨,爸爸帶著媽媽和姐姐來到了西藏,隨后我出生了学辱,我記憶中的童年都是甜甜的五...
    飴逸閱讀 154評(píng)論 4 0
  • GooglePlay 首頁效果----tab的揭示效果(Reveal Effect) (1) 前言:無意打開Goo...
    didikee閱讀 1,328評(píng)論 4 20
  • 微信開發(fā) 服務(wù)號(hào)功能 1乘瓤、1個(gè)月(自然月)內(nèi)僅可以發(fā)送4條群發(fā)消息环形。2、發(fā)給訂閱用戶(粉絲)的消息衙傀,會(huì)顯示在對(duì)方的...
    wswenyue閱讀 3,356評(píng)論 2 24