本文主要讲解我们最常用的NSURLConnection支持HTTPS的实现(NSURLSession的实现方法类似,只是要求授权证明的回调不一样而已),以及怎么样使用AFNetworking这个非常流行的第三方库来支持HTTPS。本文假设你对HTTP以及NSURLConnection的接口有了足够的了解。
使用NSURLConnection支持HTTPS的实现
// Now start the connection
NSURL*httpsURL=[NSURL URLWithString:@"https://www.google.com"];
self.connection=[NSURLConnection connectionWithRequest:[NSURLRequestrequestWithURL:httpsURL] delegate:self];
//回调
-(void)connection:(NSURLConnection*)connectionwillSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge{
//1)获取trust object
SecTrustRef trust=challenge.protectionSpace.serverTrust;
SecTrustResultType result;
//2)SecTrustEvaluate对trust进行验证
OSStatus status=SecTrustEvaluate(trust,&result);
if(status==errSecSuccess &&
(result==kSecTrustResultProceed ||
result==kSecTrustResultUnspecified)){
//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
NSURLCredential*cred=[NSURL CredentialcredentialForTrust:trust];
[challenge.sender useCredential:cred forAuthenticationChallenge:challenge];
}else{
//5)验证失败,取消这次验证流程
[challenge.sender cancelAuthenticationChallenge:challenge];
}
}
上面是代码是通过系统默认验证流程来验证证书的。但是如何确认服务器返回的证书就是我们想要的特定证书?这就需要先在本地导入证书,设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书),再调用SecTrustEvaluate来验证。
//先导入证书
NSString*cerPath=...;//证书的路径
NSData*cerData=[NSDatadataWithContentsOfFile:cerPath];
SecCertificateRef certificate=SecCertificateCreateWithData(NULL,(__bridgeCFDataRef)(cerData));
self.trustedCertificates=@[CFBridgingRelease(certificate)];
//回调
-(void)connection:(NSURLConnection*)connectionwillSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge{
//1)获取trust object
SecTrustRef trust=challenge.protectionSpace.serverTrust;
SecTrustResultType result;
//注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate
SecTrustSetAnchorCertificates(trust,(__bridgeCFArrayRef)self.trustedCertificates);
//2)SecTrustEvaluate会查找前面SecTrustSetAnchorCertificates设置的证书或者系统默认提供的证书,对trust进行验证
OSStatus status=SecTrustEvaluate(trust,&result);
if(status==errSecSuccess &&
(result==kSecTrustResultProceed ||
result==kSecTrustResultUnspecified)){
//3)验证成功,生成NSURLCredential凭证cred,告知challenge的sender使用这个凭证来继续连接
NSURLCredential*cred=[NSURLCredentialcredentialForTrust:trust];
[challenge.senderuseCredential:cred forAuthenticationChallenge:challenge];
}else{
//5)验证失败,取消这次验证流程
[challenge.sendercancelAuthenticationChallenge:challenge];
}
}
建议采用本地导入证书的方式验证证书,来保证足够的安全性。
NSURLSession和NSURLConnection主要就是代理方法不同:
下面是NSURLSession的代理方法
#pragma mark - NSURLSessionDelegate 代理方法
//主要就是处理HTTPS请求的
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
SecTrustRef serverTrust = protectionSpace.serverTrust;
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:serverTrust]);
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
使用AFNetworking来支持HTTPS
AFNetworking是iOS/OSX开发最流行的第三方开源库之一, AFNetworking 的安全相关设定放在AFSecurityPolicy。
AFNetworking定义了三种SSLpinningmode:
AFSSLPinningModeNone: 代表客户端无条件地信任服务器端返回的证书
AFSSLPinningModePublicKey : 代表客户端会将服务器端返回的证书与本地保存的证书PublicKey的部分进行校验;如果正确,才继续进行。
AFSSLPinningModeCertificate: 代表客户端会将服务器端返回的证书和本地保存的证书中的所有内容,包括PublicKey和证书部分,全部进行校验;如果正确,才继续进行。
(关于 pinning mode:简单的说就是你可以将SSL证书跟你的 APP 一起打包,藉由此机制来避免中间人伪造SSL证书的风险。)
以更加安全的AFSSLPinningModeCertificate验证模式为例。
// 1.获得请求管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.加上这个函数,https ssl 验证。
[manager setSecurityPolicy:[self customSecurityPolicy]];
// https ssl 验证函数
- (AFSecurityPolicy *)customSecurityPolicy {
// 先导入证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];//证书的路径
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用证书验证模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
//validatesDomainName 是否需要验证域名,默认为YES;
//validatesCertificateChain 是否验证整个证书链,默认为YES;