HTTPS的基本使用

855 阅读6分钟

在开发阶段后台经常使用自签名的证书来部署。但是在iOS中调用的时候就会因为证书不被信任而调用api不成功。这时候我们就需要通过实现某些网络回调函数来自定义证书的验证逻辑。(在iOS中一般通过UrlSession(OC中是NSUrlSession)来进行网络通信,这里以UrlSession为例)。首先我们需要了解几个概念。

Challenge     

Challenge是计算机安全中的专业术语。字面意思质询。就是为了验证用户身份,向访问者发送一个质询,然后访问者需要提供一个正确的回答以示身份。最简单的就是我们访问一个需要授权的网站,网站后台会通过HTTP协议想浏览器发送一个质询,要求用户输入用户名密码。(浏览器会弹出一个对话框供用户输入)

在iOS的网络相关库中,提供了如下几个类来描述Challenge的过程中的抽象实体。

URLProtectionSpace

这个表示服务器上的一块受保护的区域,访问这一块需要进行质询。他有如下常用属性:

// realm是ProtectionSpace的标示符,服务器上的一组资源通过realm来标示成一组采用相同验证方式的资源(ProtectionSpace)。
@property (nullable, readonly, copy) NSString *realm;
// 资源所在的服务器
@property (readonly, copy) NSString *host;
// 资源所在服务器端口
@property (readonly) NSInteger port;
//获取资源的协议资源
@property (nullable, readonly, copy) NSString *protocol;
// 质询所采用验证方式
@property (readonly, copy) NSString *authenticationMethod;

质询验证方式有如下几种是常用的:

NSURLAuthenticationMethodHTTPBasic //HTTP基本验证,服务器向客户端询问用户名,密码

NSURLAuthenticationMethodClientCertificate//客户端证书验证,服务器向客户端询客户端身份证书

NSURLAuthenticationMethodServerTrust//服务器端证书验证,客户端对服务器端的证书进行验证。HTTPS中的服务器端证书验证属于这一种。

URLAuthenticationChallenge

这就是服务器端对客户端的一次质询的描述了。它有如下常用属性:

//该质询所对应的ProtectionSpace
@property (readonly, copy) NSURLProtectionSpace *protectionSpace;
 
//表示该质询的发送方
@property (nullable, readonly, retain) id<NSURLAuthenticationChallengeSender> sender;

质询验证方式有如下几种是常用的:

NSURLAuthenticationMethodHTTPBasic //HTTP基本验证,服务器向客户端询问用户名,密码

NSURLAuthenticationMethodClientCertificate//客户端证书验证,服务器向客户端询客户端身份证书

NSURLAuthenticationMethodServerTrust//服务器端证书验证,客户端对服务器端的证书进行验证。HTTPS中的服务器端证书验证属于这一种。

URLAuthenticationChallenge

这就是服务器端对客户端的一次质询的描述了。它有如下常用属性:

//该质询所对应的ProtectionSpace
@property (readonly, copy) NSURLProtectionSpace *protectionSpace;
 
//表示该质询的发送方
@property (nullable, readonly, retain) id<NSURLAuthenticationChallengeSender> sender;

UrlCredential 

他是客户端对服务器端质询的响应。根据验证方式不一样,有如下几种UrlCredential:

  • NSURLAuthenticationMethodHTTPBasic  基于用户名密码的UrlCredential
  • NSURLAuthenticationMethodClientCertificate  基于客户端证书的UrlCredential
  • NSURLAuthenticationMethodServerTrust  基于服务器端证书的UrlCredential //就是我们这里验证服务器端的证书要用到的

它们分别对应于UrlCredential的三种构造方式。详情参考Apple开发文档

SecTrust         

是iOS中对证书和Accept Policy的包装。系统对后台证书验证实际上是对该对象的验证。详情建Apple开发文档

好了,到这里所有的概念性的东西都说完了。最后就是要把这些概念全部组合到UrlSession的一个回调方法中来自行验证证书。代码如下:

// HTTPS证书验证,仿写了AFN的验证
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;
    /**
     NSURLAuthenticationMethodHTTPBasic //HTTP基本验证,服务器向客户端询问用户名,密码
     NSURLAuthenticationMethodClientCertificate//客户端证书验证,服务器向客户端询客户端身份证书
     NSURLAuthenticationMethodServerTrust//服务器端证书验证,客户端对服务器端的证书进行验证。HTTPS中的服务器端证书验证属于这一种
     */
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // 认证结果,信任这个challenge.protectionSpace.serverTrust
        credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        if (credential) {
            disposition = NSURLSessionAuthChallengeUseCredential; // 使用证书
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 使用证书但是会进行CA认证
        }
    }
    // 如果disposition是NSURLSessionAuthChallengeUseCredential,credential必须有值
    // NSURLSessionAuthChallengePerformDefaultHandling,credential可以为nil
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

或者对于AFN的请求, 可以简单粗暴,直接信任. 详细可参考这篇文章的最后部分: iOS自建https证书

// self.sessionManager是AFHTTPSessionManager的一个实例
        [self.sessionManager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential) {
            *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            return NSURLSessionAuthChallengeUseCredential;
        }];

1.https简单说明 HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。 即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。 https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。

2.HTTPS和HTTP的区别主要为以下四点: 一、https协议需要到ca申请证书,一般免费证书很少,需要交费。 二、http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。 三、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。 四、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

3.对开发的影响。 3.1 如果是自己使用NSURLSession来封装网络请求,涉及代码如下。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
 
    NSURLSessionDataTask *task =  [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    [task resume];
}
 
/*
 只要请求的地址是HTTPS的, 就会调用这个代理方法
 我们需要在该方法中告诉系统, 是否信任服务器返回的证书
 Challenge: 挑战 质问 (包含了受保护的区域)
 protectionSpace : 受保护区域
 NSURLAuthenticationMethodServerTrust : 证书的类型是 服务器信任
 */
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    //    NSLog(@"didReceiveChallenge %@", challenge.protectionSpace);
    NSLog(@"调用了最外层");
    // 1.判断服务器返回的证书类型, 是否是服务器信任
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSLog(@"调用了里面这一层是服务器信任的证书");
        /*
         NSURLSessionAuthChallengeUseCredential = 0,                     使用证书
         NSURLSessionAuthChallengePerformDefaultHandling = 1,            忽略证书(默认的处理方式)
         NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,     忽略书证, 并取消这次请求
         NSURLSessionAuthChallengeRejectProtectionSpace = 3,            拒绝当前这一次, 下一次再询问
         */
//        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
 
        NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential , card);
    }
}