# AFNetworking框架下的SSL服务器证书的自定义验证服务器
## 如何使用本地证书进行SSL验证session
#### 开启SSL验证app
须要设置 `AFHTTPSessionManager` 的 `setSecurityPolicy` ,使用 `AFSSLPinningModeCertificate` 证书验证模式框架
```网站
[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]lua
```code
而后进行 `AFSecurityPolicy` 的一系列设置,以下orm
```server
+ (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];
});
```