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];
});
网友评论