iOS 对 HTTPS 证书链的验证

HTTPS从最终的数据解析的角度,与HTTP相同。HTTPS将HTTP协议数据包放到SSL/TSL层加密后,在TCP/IP层组成IP数据报去传输,以此保证传输数据的安全;而对于接收端,在SSL/TSL将接收的数据包解密之后,将数据传给HTTP协议层。

 

SSL/TSL包括四次握手,主要交换三个信息:

 

  1. 数字证书;

  2. 三个随机数;

  3. 加密通讯协议。

 

其通讯过程如下示意图:

 

数字证书

 

该证书一般是由服务器发给客户端,接收方通过验证这个证书是不是由信赖的CA签发,或者与本地的证书相对比,来判断证书是否可信;假如需要双向验证,则服务器和客户端都需要发送数字证书给对方验证。

数字证书的生成是分层级的,从叶节点证书往根证书层层验证(有效期、签名等等),遇到根证书时,发现作为可信锚点的它存在与可信证书列表中,那么验证就通过。例如在钥匙串中可以看到某券商公司申请的证书没有在系统可信任的证书列表中。

 

下面以某浪sdk里的证书为代表简单说明一下数字证书的内容:

 

证书字段证书说明
签发者名称 发布并签署该证书的实体的信息
序列号 数字证书机构(Certificate Authority, CA)给证书的唯一整数,一个数字证书一个序列号
主题名称 用于识别该数字证书的信息
公共密钥信息 可公开的密钥信息
签名 通过签名算法计算证书内容后得到的数据,用于验证证书是否被篡改

 

 

三个随机数

 

这三个随机数构成了后续通信过程中用来对数据进行对称加密解密的“对话密钥”。首先客户端先发第一个随机数n1,然后服务器返回第二个随机数n2(这个过程同时把数字证书发给客户端),这两个随机数都是明文的;而第三个随机数n3,客户端用数字证书的公钥进行非对称加密,发给服务器;而服务器用只有自己知道的私钥来解密,获取第三个随机数。服务端和客户端都有了三个随机数n1+n2+n3后,两端就使用这三个随机数来生成“对话密钥”,在此之后的通信就使用这个“对话密钥”来进行对称加解密。

 

加密通信协议

 

双方商量具体使用哪一种加密方式,假如两者支持的加密方式不匹配,则无法进行通信。因为这个过程中,服务端的私钥只用来解密第三个随机数,从来没有在网络中传输过,只要私钥没有被泄露,那么数据就是安全的。

 

下边主要介绍一下NSURLConnection支持HTTPS的实现设置证书作为可信的锚点代码应用。

 

// SDWebImageDownloaderOperation

 

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

//通过 challenge.protectionSpace.authenticationMethod 取得保护空间要求我们认证的方式

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

        if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates) &&

            [challenge.sender respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)]) {

            [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];

        } else {

            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];

        }

    } else {

        if ([challenge previousFailureCount] == 0) {

            if (self.credential) {

                [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];

            } else {

                [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];

            }

        } else {

            [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];

        }

    }

}

 

StockPromotionViewController在webview中使用

 

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

{

 

      NSString* scheme = [[request URL] scheme];

    //判断是不是https

    if ([scheme isEqualToString:@"https"])

    {

        if (self.authed)

        {

            return YES;

        }

        NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:request.URL] delegate:self];

        [conn start];

        [webView stopLoading];

        return NO;

    }

    return YES;

}

 

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

{

    if ([challenge previousFailureCount]== 0)

    {

        self.authed = YES;

        //NSURLCredential 这个类是表示身份验证凭据不可变对象。凭证的实际类型声明的类的构造函数来确定。回调中会收到一个challenge

        NSURLCredential* cre = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

        [challenge.sender useCredential:cre forAuthenticationChallenge:challenge];

    }

}

 

- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response

{

    return request;

}

 

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

{

    self.authed = YES;

    //webview 重新加载请求。

    [self.webView loadRequest:[NSURLRequest requestWithURL:connection.currentRequest.URL]];

   [connection cancel];

}

 

posted @ 2016-10-25 08:34  FMDN  阅读(522)  评论(0编辑  收藏  举报