验证证书的API(相关API在Security Framework中):
验证流程: 1.获取需要验证信任的对象(Trust Object,证书发布机构),不同的应用场景下获取的方式都不一样,对于NSURLConnection来说,是从: delegate回调方法:-connection:willSendRequestForAuthenticationChallenge: 的参数challenge中获取: [challenge.protectionSpace serverTrust] 2.使用系统默认验证方式验证Trust Object(这里会从操作系统的信任列表中寻找对应证书发布机构的数字证书获取公钥对Trust Object证书发布机构进行验证) 3.验证通过2后,一般的安全要求下: 使用Trust Object生成一份凭证[NSURLCredential credentialForTrust:serverTrust], 传入challenge的sender中[challenge.sender useCredential:cred forAuthenticationChallenge:challenge]处理,建立连接。 4. 假如有更强的安全要求: 可以继续对Trust Object进行更严格的验证。常用的方式是在本地导入证书,验证Trust Object与导入的证书是否匹配。 5. 假如验证失败: 取消此次Challenge-Response Authentication验证流程,拒绝连接请求。 /* 假如是自建证书的,则不使用第二步系统默认的验证方式,因为自建证书的根CA的数字签名未在操作系统的信任列表中。 */示范例子: 使用NSURLConnection支持HTTPS的实现: //start the connection NSURL *httpsURL=[NSURL URLWithString:@"https://www.google.com"]; self.connection=[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:httpsURL]delegate:self]; //delegate functions -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(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 = [NSURLCredential credentialForTrust:trust]; [challenge.sender useCredential:cred forAuthenticationChallenge:challenge]; }else{ //5)验证失败,取消这次验证流程 [challenge.sender cancelAuthenticationChallenge:challenge]; } } /* 上面是代码是通过系统默认验证流程来验证证书的(就是系统去系统本地信任列表中寻找数字证书验证证书发布机构,因此上面收到的验证对象必须是正规的证书发布机构) 假如我们连接地址返回的是自建证书,直接使用SecTrustEvaluate进行验证是不会成功的。 又或者 即使服务器返回的证书是信任CA签发的,又如何确定这证书就是我们想要的特定证书? 这就需要先在本地导入证书,设置成需要参与验证的Anchor Certificate(锚点证书,通过SecTrustSetAnchorCertificates设置了参与校验锚点证书之后,假如验证的数字证书是这个锚点证书的子节点,即验证的数字证书是由锚点证书对应CA或子CA签发的,或是该证书本身,则信任该证书), 再调用SecTrustEvaluate来验证 示范例子(对上面的例子修改): //先导入证书 NSString * cerPath = ...; //证书的路径 NSData * cerData = [NSData dataWithContentsOfFile:cerPath]; SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(cerData)); self.trustedCertificates = @[CFBridgingRelease(certificate)]; //回调 - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ //1)获取trust object SecTrustRef trust = challenge.protectionSpace.serverTrust; SecTrustResultType result; //注意:这里将之前导入的证书设置成下面验证的Trust Object的anchor certificate SecTrustSetAnchorCertificates(trust,(__bridge CFArrayRef)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 = [NSURLCredential credentialForTrust:trust]; [challenge.sender useCredential:cred forAuthenticationChallenge:challenge]; }else{ //5)验证失败,取消这次验证流程 [challenge.sender cancelAuthenticationChallenge:challenge]; } } 建议采用本地导入证书的方式验证证书,来保证足够的安全性。 */