iOS服务器证书不受信任的解决版本
iOS服务器证书不受信任的解决版本
参考⽂章链接:
⽐较通⽤的解决办法之⼀:
MKNetworkOperation 可以设置
request.shouldContinueWithInvalidCertificate = YES;
//采⽤NSURLConnection进⾏⽹络请求,会调⽤此⽅法
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
//证书处理
if([kBaseURL rangeOfString:@"youhost"].location != NSNotFound)
{
}
// 判断是否是信任服务器证书
if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
// 告诉服务器,客户端信任证书
// 创建凭据对象
NSURLCredential *credntial = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 告诉服务器信任证书
[challenge.sender useCredential:credntial forAuthenticationChallenge:challenge];
}
}
在AFN中如何绕过证书验证呢?设置AFSecurityPolicy参数:
//证书处理
//后台⾃建证书,因为证书⽆效导致AFN请求被取消,此段代码⽤在外⽹测试环境
if([kBaseURL rangeOfString:@"ax"].location != NSNotFound){
AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
}
设置⾃定义证书的⽅法
发送HTTPS请求信任SSL证书和⾃签名证书,分为三种情况
1.如果你的app服务端安装的是SLL颁发的CA,可以使⽤系统⽅法直接实现信任SSL证书,关于Apple对SSL证书的要求请参考:
这种⽅式不需要在Bundle中引⼊CA⽂件,可以交给系统去判断服务器端的证书是不是SSL证书,验证过程也不需要我们去具体实现。
⽰例代码:
NSURL *URL = [NSURL URLWithString:URLString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
//创建同步连接
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
NSString *receivedInfo = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
当然,如果你需要同时信任SSL证书和⾃签名证书的话还是需要在代码中实现CA的验证,这种情况在后⾯会提到。
2.基于AFNetWorking的SSL特定服务器证书信任处理,重写AFNetWorking的customSecurityPolicy⽅法,这⾥我创建了⼀个HttpRequest类,分别对GET和POST⽅法进⾏了封装,以GET⽅法为例:
+ (void)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id))success failure:(void (^)(NSError *))failure
{
// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 2.申明返回的结果是text/html类型
// 3.设置超时时间为10s
// 加上这⾏代码,https ssl 验证。
if(openHttpsSSL) {
[mgr setSecurityPolicy:[self customSecurityPolicy]];
}
// 4.发送GET请求
[mgr GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObj){
if (success) {
success(responseObj);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (error) {
failure(error);
}
}];
}
+ (AFSecurityPolicy*)customSecurityPolicy
{
// /先导⼊证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:certificate ofType:@"cer"];//证书的路径
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使⽤证书验证模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// allowInvalidCertificates 是否允许⽆效证书(也就是⾃建的证书),默认为NO
// 如果是需要验证⾃建证书,需要设置为YES
连接apple id服务器时出错securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不⼀致,需把该项设置为NO;如设成NO的话,即服务器使⽤其他可信任机构颁发的证书,也可以建⽴连接,这个⾮常危险,建议打开。
//置为NO,主要⽤于这种情况:客户端请求的是⼦域名,⽽证书上的是另外⼀个域名。因为SSL证书上的域名是独⽴的,假如证书上注册的域名是le,那么le是⽆法验证通过的;当然,有钱可以注册通配符的域名*.go    //如置为NO,建议⾃⼰添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = NO;
securityPolicy.pinnedCertificates = @[certData];
return securityPolicy;
}
其中的cerPath就是app bundle中证书路径,certificate为证书名称的宏,仅⽀持cer格式,securityPolicy的相关配置尤为重要,请仔细阅读customSecurityPolicy⽅法并根据实际
情况设置其属性。
这样,就能够在AFNetWorking的基础上使⽤HTTPS协议访问特定服务器,但是不能信任根证书的CA⽂件,因此这种⽅式存在风险,读取pinnedCertificates中的证书数组的时候
有可能失败,如果证书不符合,certData就会为nil。
3.更改系统⽅法,发送异步NSURLConnection请求。
- (void)getDataWithURLRequest {
//connection
NSString *urlStr = @"developer.apple/cn/";
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
[connection start];
}
重点在于处理NSURLConnection的didReceiveAuthenticationChallenge代理⽅法,对CA⽂件进⾏验证,并建⽴信任连接。
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
/*
//直接验证服务器是否被认证(serverTrust),这种⽅式直接忽略证书验证,直接建⽴连接,但不能过滤其它URL连接,可以理解为⼀种折衷的处理⽅式,实际上并不安全,因此不推荐。
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]
forAuthenticationChallenge: challenge];
*/
if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
do
{
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
NSCAssert(serverTrust != nil, @"serverTrust is nil");
if(nil == serverTrust)
break; /* failed */
/**
*  导⼊多张CA证书(Certification Authority,⽀持SSL证书以及⾃签名的CA)
*/
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"cloudwin" ofType:@"cer"];//⾃签名证书
NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
NSString *cerPath2 = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"cer"];//SSL证书
NSData * caCert2 = [NSData dataWithContentsOfFile:cerPath2];
NSCAssert(caCert != nil, @"caCert is nil");
if(nil == caCert)
break; /* failed */
NSCAssert(caCert2 != nil, @"caCert2 is nil");
if (nil == caCert2) {
break;
}
SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
NSCAssert(caRef != nil, @"caRef is nil");
if(nil == caRef)
break; /* failed */
SecCertificateRef caRef2 = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert2);
NSCAssert(caRef2 != nil, @"caRef2 is nil");
if(nil == caRef2)
break; /* failed */
NSArray *caArray = @[(__bridge id)(caRef),(__bridge id)(caRef2)];
NSCAssert(caArray != nil, @"caArray is nil");
if(nil == caArray)
break; /* failed */
OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
if(!(errSecSuccess == status))
break; /* failed */
SecTrustResultType result = -1;
status = SecTrustEvaluate(serverTrust, &result);
if(!(errSecSuccess == status))
break; /* failed */
NSLog(@"stutas:%d",(int)status);
NSLog(@"Result: %d", result);
BOOL allowConnect = (result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed);
if (allowConnect) {
NSLog(@"success");
}else {
NSLog(@"error");
}
/* developer.apple/library/ios/technotes/tn2232/_index.html */
/* developer.apple/library/mac/qa/qa1360/_index.html */
/* kSecTrustResultUnspecified and kSecTrustResultProceed are success */
if(! allowConnect)
{
break; /* failed */
}
#if 0
/* Treat kSecTrustResultConfirm and kSecTrustResultRecoverableTrustFailure as success */
/*  since the user will likely tap-through to see the dancing bunnies */
if(result == kSecTrustResultDeny || result == kSecTrustResultFatalTrustFailure || result == kSecTrustResultOtherError)
break; /* failed to trust cert (good in this case) */
#endif
// The only good exit point
return [[challenge sender] useCredential: [NSURLCredential credentialForTrust: serverTrust]
forAuthenticationChallenge: challenge];
} while(0);
}
// Bad dog
return [[challenge sender] cancelAuthenticationChallenge: challenge];
}
这⾥的关键在于result参数的值,根据官⽅⽂档的说明,判断(result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed)的值,若为1,则该⽹站的CA被app信任成功,可以建⽴数据连接,这意味着所有由该CA签发的各个服务器证书都被信任,⽽访问其它没有被信任的任何⽹站都会连接失败。该CA⽂件既可以是SLL也可以是⾃签名。
NSURLConnection的其它代理⽅法实现
#pragma mark -- connect的异步代理⽅法
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@"请求被响应");
_mData = [[NSMutableData alloc]init];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data {
NSLog(@"开始返回数据⽚段");
[_mData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(@"链接完成");
//可以在此解析数据
NSString *receiveInfo = [NSJSONSerialization JSONObjectWithData:self.mData options:NSJSONReadingAllowFragments error:nil];
NSLog(@"received data:\\\\n%@",self.mData);
NSLog(@"received info:\\\\n%@",receiveInfo);
}
//链接出错
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"error - %@",error);
}
⾄此,HTTPS信任证书的问题得以解决,这不仅是为了响应Apple强制性使⽤ATS的要求,也是为了实际⽣产环境安全性的考虑,HTTPS是未来的趋势,建议尽早⽀持。
如需参考Demo请移步

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。