AFNetworking框架下的SSL服务器证书的自定义验证

# AFNetworking框架下的SSL服务器证书的自定义验证

 

## 如何使用本地证书进行SSL验证

 

#### 开启SSL验证

需要设置 `AFHTTPSessionManager` 的 `setSecurityPolicy` ,使用 `AFSSLPinningModeCertificate` 证书验证模式

 

```

[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]

```

 

然后进行 `AFSecurityPolicy` 的一系列设置,如下

```

+ (AFSecurityPolicy *)customSecurityPolicy {

    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; // 设置证书验证模式

    [securityPolicy setAllowInvalidCertificates:NO]; //  是否允许无效证书(也就是自建的证书),默认为NO.如果是需要验证自建证书,需要设置为YES

    [securityPolicy setValidatesDomainName:YES]; // 验证域名

    

    // 设置证书

    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"itouchtv_app" ofType:@"cer"];

    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

    [securityPolicy setPinnedCertificates:[NSSet setWithObject:certData]];

    

    return securityPolicy;

}

 

```

 

#### 关闭SSL验证

只需要使用 `AFSSLPinningModeNone` 模式,即可关闭SSL验证

 

```

[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]

```

 

 

 

## 自定义实现SSL证书的验证逻辑

 

#### 使用 NSURLSession 走 HTTPS 通道访问网站或接口

NSURLSession的 ` -URLSession:didReceiveChallenge:completionHandler: ` 回调中会收到一个类型为 `NSURLAuthenticationChallenge` 的质询 challenge,在此回调中,进行证书信息的认证,以决定是否继续连接服务器,或者断开连接。

 

通过 challenge.protectionSpace.authenticationMethod 获取保护空间要求认证的方式,如果值是 `NSURLAuthenticationMethodServerTrust` ,就可以走数字证书的自定义验证逻辑了。

 

#### AFNetworking 下的具体实现方式

跟 `NSURLSession` 是相同的原理,质询的回调是 `AFHTTPSessionManager` 的 `setSessionDidReceiveAuthenticationChallengeBlock` 。

 

 

#### SSL证书的层级和自定义验证逻辑

SSL证书,一般有三层,根证书和二级证书是申请证书时的权威可信的颁发机构的证书,在换证书时,是保持不变的,所以可以使用字符串常量来直接验证是否相等,即可完成根证书和二级证书的验证。

 

而最后一层,才是每个证书申请者自己的独特的信息,也是需要自定义验证的部分。

 

##### 具体代码

关键在于 `isServerTrust` 变量,表示自定义验证通过。

 

```

        // 设置验证模式

        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

        securityPolicy.allowInvalidCertificates = NO;//是否允许使用自签名证书

        securityPolicy.validatesDomainName = YES;//是否需要验证域名,默认YES

        [manager setSecurityPolicy:securityPolicy];

 

        // 自定义验证证书

        [manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential)

        {

            BOOL isServerTrust = NO;//所有证书验证通过

            

            BOOL isCertRootTrust = NO;//根证书验证通过

            BOOL isCertSecondTrust = NO;//二级证书验证通过

            BOOL isCertClientTrust = NO;//最后一级,本地证书验证通过

            

            //取得服务器返回的三级证书

            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];

            

            //遍历服务器返回的证书

            CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);

            for (CFIndex i = certificateCount - 1; i >= 0; i--) {

                SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);

                NSString *subjectSummary = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificate);

                

                //证书NSData转为NSString

                CFDataRef certData = SecCertificateCopyData(certificate);

                NSString *certificateBase64String = [(__bridge_transfer NSData *)certData base64EncodedStringWithOptions:0];

                

                //比较本地保存的一级和二级证书NSString

                if (i == certificateCount - 1) {//一级根证书

                    if ([certificateBase64String isEqualToString:certStringForRoot]) {

                        isCertRootTrust = YES;

                    }

                } else if (i == certificateCount - 2) {//第二级证书

                    if ([certificateBase64String isEqualToString:certStringForSecondLevel]) {

                        isCertSecondTrust = YES;

                    }

                } else if (i == 0) {//第三级:要部分验证的本地证书

  

                    //获取本地证书

                    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"cer"];

                    NSData *certData = [NSData dataWithContentsOfFile:cerPath];

                    SecCertificateRef certificateLocal = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);

                    

                    //域名

                    NSString *subjectSummaryLocal = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificateLocal);

                    

                    //验证

                    if ([subjectSummary isEqualToString:subjectSummaryLocal])

                    {

                        isCertClientTrust = YES;

                    }

                }

            }

            

            NSLog(@"一级根证书是否验证通过:%@,第二级证书是否验证通过:%@,第三级本地证书是否验证通过:%@", @(isCertRootTrust), @(isCertSecondTrust), @(isCertClientTrust));

            

            //判断所有证书是否验证通过

            if (isCertRootTrust && isCertSecondTrust && isCertClientTrust) {

                isServerTrust = YES;

            }

 

            NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

            __autoreleasing NSURLCredential *credential = nil;

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

//                if ([manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { // 此处注释的,为默认的处理方式

                if (isServerTrust) { // 要自定义验证,需要使用自己的判断逻辑

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

                    if (credential) {

                        disposition = NSURLSessionAuthChallengeUseCredential;

                    } else {

                        disposition = NSURLSessionAuthChallengePerformDefaultHandling;

                    }

                } else {

                    disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;

                }

            } else {

                disposition = NSURLSessionAuthChallengePerformDefaultHandling;

            }

            return disposition;

        }];

        

        [TTVPlayerMoniter sharedMointerManager];

 

    });

    

```

转载于:https://my.oschina.net/u/3729372/blog/1596609

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值