自iOS 9以后,苹果发出强制使用HTTPS协议的规定,虽然到现在也还没有强制实行,不过大多数用户早已使用HTTPS协议替换原有的HTTP协议。相对HTTP,HTTPS更安全,它是在tcp/ip层加入了SSL/TSL加密层。HTTPS的作用是防止中间人攻击的,下面我们来看下有关HTTPS的一些tip
首先我们先来了解下在HTTPS下客户端与服务端之间的一个交互流程:
- 客户端发送SSL版本信息、random-c、加密算法至服务端
- 服务端发送SSL版本、随机数random-s等信息及服务器公钥至客户端- 非对称加密,耗时,性能低
- 客户端校验证书是否合法,合法继续,否则警告
- 服务端选择加密程度高的加密方式
- 将选择好的加密方式明文发送给客户端
- 客户端收到加密方式后,产生随机码(Pre-master),作为加密对称密钥,使用公钥加密后,发给服务端
- 服务端使用私钥解密,获得对称加密的密钥
- 使用随机码对称加密,进行通讯
上述交互流程中,与客户端密切相关的就是证书校验部分,下面我们来具体看下这个部分的代码实现
首先看下几个重要的类及属性
- SecTrustRef - 需要验证的信任对象,包含待验证的证书和支持的验证方法等
- SecTrustResultType - 验证结果,kSecTrustResultProceed表示server trust验证成功,且该验证得到了用户认可,用户点击了always trust;kSecTrustResultUnspecified表示server trust验证成功,此证书被暗中信任,但用户并没有显式的决定信任该证书
- SecTrustEvaluate-证书校验函数,在函数的内部递归的从叶节点到根证书验证。需要验证证书本身的合法性(验证签名完整性,验证证书有效期等);验证证书颁发者的合法性。
- NSURLAuthenticationChallenge
protectionSpace:NSURLProtectionSpace - 保存服务器中希望的认证方式以及协议,主机端口号等信息
这个类中的属性
host - 主机地址
port - 端口号
authenticationMethod - 指定授权方式,例如NSURLAuthenticationMethodServerTrust为ServerTrust认证、 - proposedCredential:NSURLCredential - 建议使用的证书
有了上面的了解,我们就更容易看明白下面代码实现
自签名证书单向校验
针对NSURLConnection的适配
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
[[challenge sender] useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge: challenge];
}
}
针对NSURLSession适配
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// 判断返回的证书是否是服务器所信任的
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential)
{
//使用返回的证书
disposition = NSURLSessionAuthChallengeUseCredential;
}
else
{
//忽略证书
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
else
{
//取消请求
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
//安装证书
if (completionHandler) {
completionHandler(disposition, credential);
}
}
证书预置本地的单向校验
针对URLSession进行说明
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
SecTrustRef trustRef = challenge.protectionSpace.serverTrust;
SecCertificateRef certificateRef = SecTrustGetCertificateAtIndex(servertrust, 0);
NSData *certidata = CFBridgingRelease(CFBridgingRetain(CFBridgingRelease(SecCertificateCopyData(certificateRef))));
NSString *path = [[NSBundle mainBundle] pathForResource:@"证书名称" ofType:@"cer"];
// NSLog(@"证书 : %@",path);
NSData *localCertiData = [NSData dataWithContentsOfFile:path];
if ([certidata isEqualToData:localCertiData]) {
NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:servertrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
// NSLog(@"服务端证书认证通过");
}else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
NSLog(@"服务端认证失败");
}
}
至此,HTTPS的记录就到此结束了。
生活如此美好,今天就点到为止。。。
网友评论