https与AFNetworking

1,328 阅读8分钟

前言

https是可以理解为安全通道的http协议,其在http传输的基础上,进行了验证,且传输过程内容都是以密文的方式进行传输,下面将会介绍https究竟做了什么

https

https请求的过程可以分为两大部分:使用公钥和私钥进行非对称加解密进行身份验证(公对私或者私对公加解密)、对称加密方式进行传输数据

这样既可以较为安全地验证双方身份,还可以最大限度地高速传输内容,而对称加密与解密的过程都需要秘钥,因此秘钥获取的过程在双方身份验证的过程得到

在第一次https请求验证过身份后,会获取到秘钥,那么后面的网络请求则不需要在验证身份,可以直接拿着秘钥直接进行了,应用重新打开则需要重新验证身份,重新更新秘钥

因此https交互的过程经过了下面几个步骤:

1、客户端和服务端经过三次握手后,由客户端发送SSL版本信息,客户端random_c(客户端随机数random_client),加密算法发送给服务器

2、服务器收到后,返回给客户端SSL版本、随机数random_s(服务端随机数random_server)、服务器证书、公钥等

3、客户端校验证书是否合法,合法则继续,否则发出警告(最明显则是浏览器打开位置网页)

4、客户端校验完毕后,则将自己支持的对称加密方案传递给服务端,供其选择

5、服务端收到支持的对称加密方式后,选择加密程度较高的加密方式

6、服务器将选好的对称加密方式以明文方式发送给客户端

7、客户端收到加密方式之后,产生一段随机码pre-master,作为对称加密秘钥,使用公钥进行加密后,发送给服务端

8、服务端收到加密后的信息后,使用私钥进行解密,获得客户端的随机码,组合成加密秘钥(random_c + pre-master + random_s)

9、使用加密秘钥进行对称加密、解密,开始通讯

也就是说 1 ~ 8 步是身份验证的过程,第 9 步是正式传输的过程

也可以看出https传输中重要的一个文件,SSL证书,这个一般去SSL代理机构购买,购买后会获得证书和秘钥

其交互的图,如下图所示

image.png

里面的大部分步骤是系统帮助我们做的,后面我们结合AFNetworking,来说明

AFNetworking的Challenge挑战

在AFNetworking中会发现Challenge这个回调方法,这个方法就是处理https的挑战的地方

如下图所示,AFNetworking最终也是使用的NSURLSession进行请求的,https请求的过程服务器会返回401 Access Denied内容,这是客户端就需要处理Challenge挑战了,也就是后面的回调,处理挑战的过程也就是我们拿到证书公钥的那步了,我们在challenge中需要做的内容就是对证书的校验过程

image.png

AFNetworking中challenge挑战的处理代码如下所示

//挑战处理类型为 默认
/*
 NSURLSessionAuthChallengeUseCredential:使用指定的证书
 NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
 NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
 NSURLSessionAuthChallengeRejectProtectionSpace:拒绝此挑战,并尝试下一个验证保护空间;忽略证书参数
 */
//证书的安全验证策略
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
     //不使用固定证书(本地)验证服务器。直接从客户端系统中的受信任颁发机构 CA 列表中去验证
    AFSSLPinningModeNone,
    // 代表会对服务器返回的证书中的PublicKey进行验证,通过则通过,否则不通过
    AFSSLPinningModePublicKey,
    // 代表会对服务器返回的证书同本地证书全部进行校验,通过则通过,否则不通过
    AFSSLPinningModeCertificate,
};

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
     NSURLCredential *credential))completionHandler
{
    //挑战处理类型为默认
    NSURLSessionAuthChallengeDisposition disposition = 
        NSURLSessionAuthChallengePerformDefaultHandling;

    //证书凭证
    __block NSURLCredential *credential = nil;//证书

    // 自定义方法,用来如何应对服务器端的认证挑战
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge
            (session, challenge, &credential);
    } else {
        // 1.判断接收服务器挑战的方法是否是信任证书(证书验证)
        if ([challenge.protectionSpace.authenticationMethod 
            isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            //SecTrustRef(serverTrust):需要验证的对象,包含了待验证的证书(服务端给过来的),支持的验证方法
            //securityPolicy默认是AFSSLPinningModeNone即不使用本地证书进行校验,即单向认证
            //一般会将SSL证书放到本地,设置AFSSLPinningModePublicKey、AFSSLPinningModeCertificate
            //使用本地证书进行校验,即双向认证
            if ([self.securityPolicy 
                evaluateServerTrust:challenge.protectionSpace.serverTrust 
                forDomain:challenge.protectionSpace.host]) {
                 // 2.证书校验通过,就从受保护空间里面拿出信任证书,回调给服务器
                credential = [NSURLCredential 
                    credentialForTrust:challenge.protectionSpace.serverTrust];
               // 确定挑战的方式
                if (credential) {
                    //证书挑战
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    //默认挑战
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                 //取消挑战
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            //默认挑战方式
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }
 //完成挑战
    // 3.将信任凭证发送给服务端
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

通过上面可以看到,验证方式需要信任证书是,则开始验证,负责使用默认的挑战处理方式,否则就要通过用户端的校验

1、如果用户端验证验证策略为默认(AFSSLPinningModeNone),则从受信任颁发机构且信任的 CA 列表中去验证,可以理解服务端的单向认证过程,客户端并没有精准的认证手段(没有公钥证书原件)

也有一些中间者使用的是自制证书,即非法证书,他们通过让用户下载证书并信任,因此从服务器下载的证书如果与其一致则会校验通过(这也是中间人攻击的手段,例如charles抓包https也会让你下载一个证书并信任)

2、可以设置为使用publicKey去验证(AFSSLPinningModePublicKey),公钥方式则使用本地证书中的公钥与服务器返回的证书中的公钥进行对比,进行校验,即使服务器更新证书,只要公钥不变,依然能完成校验,否则失败,为客户端和服务端的双向认证过程

3、加入本地证书文件(.cer文件),使用本地证书校验策略(AFSSLPinningModeCertificate),使用证书与服务端的SSL证书进行对比校验,证书完全一致则校验成功,可以继续访问,否则取消本次访问,为客户端和服务端的双向认证过程

一般不需要自定义校验过程,只需要在使用的时候设置好安全策略,以及对应的参数即可(例如:证书验证需要提供本地证书文件)

//获取本地证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];
NSData   *data    = [NSData dataWithContentsOfFile:cerPath];
//加入证书集合
NSSet    *cerSet  = [NSSet setWithObject:data];
//设置证书校验的安全策略为本地证书校验
AFSecurityPolicy *security = [AFSecurityPolicy 
    policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:cerSet];
//[AFSecurityPolicy defaultPolicy]; //不设置的话默认是这个了
//开启校验证书校验
security.allowInvalidCertificates = YES;
security.validatesDomainName      = NO; //是否验证域名

//开始https网络请求
NSString *urlstr = @"XXX";
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy        = [self securityPolicy];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:urlstr parameters:nil progress:nil 
success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

}];

总结

https设置前需要购买SSL证书,或者使用往上免费的SSL证书,一般由服务器发送证书,客户端负责接收和验证(为单向验证,也相对比较危险),而客户端一般使用本地SSL证书进行双向验证,相对比较安全

也可以通过上面步骤猜出,使用浏览器访问某个网页时是怎么样一个场景,校验过程全部由浏览器完成,且基本上只能从的市面上的权威的证书信任列表来校验

通过上面https的交互过程可以看到,https交互过程用户可以操作的范围其实很窄,主要是证书的验证策略,而网上所说的什么md5、SHA256、RSA为非对称加密,这个应用于https无关,一般用于用户身份校验、密码防泄漏等

https功能可以简易理解为,使用非对称的公钥私钥协商出 传输过程加密 所需的秘钥,使用此秘钥对传输过程中的信息(request和reponse)进行对称加密,网络截取的信息都是密文,可以理解为https做的事情是在传输层

而实际开发中,我们需要对用户的操作进行进一步确认,放置用户恶意篡改数据,一般在请求参数中,使用用户的token作为秘钥,根据请求参数信息按照一定规则组成字符串,然后使用非对称加密方式进行加密,然后作为参数传递个服务器(传递过程由https加密成密文传输),服务器接收到数据后(使用秘钥将传输内容从密文翻译成明文),然后根据token等信息,以及协商好的加密规则加密,然后对比一致方真正执行请求

其中token为系统给一个用户保存的临时令牌,一段时候后会过期,快过期时需要更新,或者重新登录后更新

最后

http被代理抓包后,明文形式的返回值会被完好无损的抓回来,而https接收的都是密文,没有秘钥怎么抓包呢,你能想象到从哪里下手的么?

注意: charles抓包https过程就属于中间人攻击,可以参考此文理解https与中间人攻击

每次收货知识也是一种喜悦,与大家分享更是进步的开始!