iOS自签名HTTPS证书单向校验方案

前言

这两天公司选所谓的先进个人,结果也是我最满意的,没选上但能得到自己身边的伙伴对自己的认可,很开心,感冒也好了很多!虽然知道大家选我,只是对我工作层面的认可,与人品无关,但我宁愿伙伴能对我的人品层面也有同等认可。

毕业已近两年,出来工作时间总算起来,也有快三年了。从开始跟着师傅,到慢慢独立,再到带新伙伴一起做项目,这一路走来,有过无助、有过怀疑,有过为伙伴“甘愿独自离去”的冲动,有过低级趣味的诱惑,有过感动,有过坚定,说我傻B也好,说我怎样也罢,我都全盘接受。

说实话,有时也会矛盾。我是一个“虚心听取别人意见”的人,别人的建议我反思后,除了性格使然部分,其他大部分我都会调整,当然你也可以理解为没有主见,设计、测试怎么说就怎么改,领导安排工作也尽量去做。慢慢的对自己的工作方式也有过疑问,这样做是否合适?也有贵人给我提过一些东西,说让我学会表达自己,为此,我也做出些自己的改变,在技术方面,我开始表达自己的想法,不过有时会不注意表达的方法和方式,也会给领导给自己造成些许困惑。

不管怎样,猴年即将过去,这恐怕是年前最后一次项目上线了,给大家分享下前几天HTTPS证书校验的一些东西。
说好的写篇《iOS自签名HTTPS证书单向校验方案》呢,又扯了这么多闲篇,对大家不住。

HTTPS简析

虽说在去年圣诞节前夕苹果发出公告要推迟ATS适配截止时间,但既然它在iOS9.0始推出App Transport Security,适配HTTPS是早晚的事,总要做好技术储备。以鄙人浅显的技术而言,我觉得iOS APP适配HTTPS主要涉及三方面适配:

  • 普通网络请求;
  • H5页面加载;
  • SDWebImage加载HTTPS图片(一般公司测试库会用到自签名证书)。

所谓HTTPS,即HTTP+SSL/TSL,底部也就是在HTTP协议层和TCP/IP协议层之间添加安全传输层协议SSL,从而达到对HTTP数据包加密传输的目的,

sequenceDiagram
    participant Client
    participant Server
    Client->>Server: 以明文传输数据,主要有客户端支持的SSL版本等客户端支持的加密信息
    Server-->>Server: 服务端选择加密方式
    
    Server-->>Client: 服务端给客户端返回SSL版本、随机数等信息
    Client->>Client: 校验服务端证书是否合法、产生随机数
    
    Client->>Server: 使用服务端随机数加密数据发给服务端
    Server-->>Server: 服务端使用私钥解密客户端数据
    
    Server-->>Client: 使用收到的随机数,加密数据,返回给客户端
    Client->>Client: 客户端使用公钥解密数据

简书不支持MarkDown高级语法,上面的MarkDown文本对应下图:

HTTPS sequenceDiagram.png

iOS适配HTTPS注意点

  • 网络请求NSURLConnection/NSURLSession
  • H5页面适配WKWebView/UIWebView
  • SDWebImage图片加载适配

网络请求部分

NSURLConnection适配

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 
    {
        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    } 
    else 
    {
        if ([challenge previousFailureCount] == 0) 
        {
            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
        }
        else 
        {
            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
        }
    }
}

NSURLSession适配

/** disposition:如何处理证书
    NSURLSessionAuthChallengeUseCredential 使用证书
    NSURLSessionAuthChallengePerformDefaultHandling  忽略证书 默认的做法
    NSURLSessionAuthChallengeCancelAuthenticationChallenge 取消请求,忽略证书
    NSURLSessionAuthChallengeRejectProtectionSpace 拒绝,忽略证书
*/
#pragma mark - NSURLSessionDelegate代理方法 HTTPS ---开始---
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    // 判断服务器返回的证书是否是服务器信任的
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        if (credential)
        {
            disposition = NSURLSessionAuthChallengeUseCredential; // 使用证书
        }
        else
        {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 忽略证书 默认的做法
        }
    }
    else
    {
        disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; // 取消请求,忽略证书
    }
    if (completionHandler)// 安装证书
    {
        completionHandler(disposition, credential);
    }
}

H5页面适配

基于WKWebView的H5页面HTTPS适配
#pragma mark: WKWebView的https配置
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];

        completionHandler(NSURLSessionAuthChallengeUseCredential,card);
    }
}
基于UIWebView的H5页面HTTPS适配

详情见:Stretch's stackoverflow答案

#pragma mark - Webview delegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
    NSLog(@"Did start loading: %@ auth:%d", [[request URL] absoluteString], _authenticated);

    if (!_authenticated) 
    {
        _authenticated = NO;
        _urlConnection = [[NSURLConnection alloc] initWithRequest:_request delegate:self];
        [_urlConnection start];
        return NO;
    }
    return YES;
}

#pragma mark - NURLConnection delegate
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
    NSLog(@"WebController Got auth challange via NSURLConnection");
    if ([challenge previousFailureCount] == 0)
    {
        _authenticated = YES;
        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
    } else
    {
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
    NSLog(@"WebController received response via NSURLConnection");

    // remake a webview call now that authentication has passed ok.
    _authenticated = YES;
    [_web loadRequest:_request];

    // Cancel the URL connection otherwise we double up (webview + url connection, same url = no good!)
    [_urlConnection cancel];
}

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

SDWebImage加载HTTPS图片适配

低版本的SDWebImage是对NSURLConnection做的封装,新版本是对NSURLSession做的封装,适配HTTPS图片,记得确定自己使用的SDWebImage版本的SDWebImageDownloaderOperation类是否做过校验。
如果SDWebImage使用的是NSURLConnection看看文件是否有:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

否则查看SDWebImageDownloaderOperation文件中是否有

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler

确定好之后,如果所有图片均是HTTPS的,那么我们可以直接修改UIImageView+WebCache文件

- (void)sd_setImageWithURL:(nullable NSURL *)url 
{
//    [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
    [self sd_setImageWithURL:url placeholderImage:nil options:SDWebImageAllowInvalidSSLCertificates progress:nil completed:nil]; // HTTPS配置问题
}
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder 
{
//    [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
    [self sd_setImageWithURL:url placeholderImage:placeholder options:SDWebImageAllowInvalidSSLCertificates progress:nil completed:nil]; // HTTPS配置问题
}

其实质就是将option设置为SDWebImageAllowInvalidSSLCertificates,看SDWebImageDownloaderOperation文件你会发现,SDWebImage采用直接忽略证书验证的方式加载的,所以设置SDWebImageAllowInvalidSSLCertificates才有效。

SDWebImageDownloaderloaderOperation校验忽略.png

如有个别特殊情况,要支持某些HTTP的图片,按照上面方法适配后,还需在plist的Exception Domains添加响应配置,详情见苹果APP接入HTTPS延迟截止时间是妥协吗

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容