Ios NSURLSession中的SSL证书验证
在ios应用程序中实现SSL证书验证的不同方式有哪些? 目前我正在做的是比较远程SSL证书数据和本地证书数据。但这种方法的缺点是,每次我们更改远程证书时,我们都需要从应用程序中的客户端请求更新,这非常烦人。 有没有其他方法来执行SSL证书验证而不是比较数据? 这是我在didReceiveChallenge中的代码Ios NSURLSession中的SSL证书验证,ios,ssl,https,nsurlsession,Ios,Ssl,Https,Nsurlsession,在ios应用程序中实现SSL证书验证的不同方式有哪些? 目前我正在做的是比较远程SSL证书数据和本地证书数据。但这种方法的缺点是,每次我们更改远程证书时,我们都需要从应用程序中的客户端请求更新,这非常烦人。 有没有其他方法来执行SSL证书验证而不是比较数据? 这是我在didReceiveChallenge中的代码 -(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0); //index 0 indicates leaf certificate .
NSMutableArray *policies = [NSMutableArray array];
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
BOOL certificateIsValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); //Unspecified-4 ,Proceed-1
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
NSData *localCertificateData = [NSData dataWithContentsOfFile: self.nsurl_pathToCertificate ];
if ([remoteCertificateData isEqualToData:localCertificateData ]&& certificateIsValid) {
NSLog(@"Certificate data are same ");
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
else{
NSLog(@"Certificate data are different or invalid certificate");
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL);
}
}
其中self.nsurl\u pathToCertificate是指向本地证书的路径。不同的方法是使用
公钥(固定密钥)进行比较,请查看更多信息
首先,加载捆绑包中的所有证书
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
+ (NSSet *)defaultPinnedCertificates {
static NSSet *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
});
return _defaultPinnedCertificates;
}
_pinnedCertificates = [YOUR_CLASS defaultPinnedCertificates];
并从中创建公钥
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
[mutablePinnedPublicKeys addObject:publicKey];
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecCertificateRef allowedCertificates[1];
CFArrayRef tempCertificates = nil;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
allowedCertificates[0] = allowedCertificate;
tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
policy = SecPolicyCreateBasicX509();
SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust);
SecTrustEvaluate(allowedTrust, &result);
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
if (allowedTrust) {
CFRelease(allowedTrust);
}
if (policy) {
CFRelease(policy);
}
if (tempCertificates) {
CFRelease(tempCertificates);
}
if (allowedCertificate) {
CFRelease(allowedCertificate);
}
return allowedPublicKey;
}
而且
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
方法检查它
NSMutableArray *policies = [NSMutableArray array];
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust;
SecTrustCreateWithCertificates(certificates, policy, &trust);
SecTrustResultType result;
SecTrustEvaluate(trust, &result);
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
if (trust) {
CFRelease(trust);
}
if (certificates) {
CFRelease(certificates);
}
continue;
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}
static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
return [(__bridge id)key1 isEqual:(__bridge id)key2];
}
NSMutableArray*策略=[NSMutableArray];
[policies addObject:(uuu bridge_transfer id)SecPolicyCreateSSL(true,(uu bridge CFStringRef)domain)];
SectrustSetPolicys(服务器信任,(\uu桥CFArrayRef)策略);
NSUInteger trustedPublicKeyCount=0;
NSArray*publicKeys=AFPublicKeyTrustChainForServerTrust(serverTrust);
for(公钥中的id trustChainPublicKey){
for(id pinnedPublicKey在self.pinnedPublicKey中){
if(AFSecKeyIsEqualToKey((_桥SecKeyRef)信任链公钥,(_桥SecKeyRef)pinnedPublicKey)){
trustedPublicKeyCount+=1;
}
}
}
返回trustedPublicKeyCount>0;
静态NSArray*AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust){
SecPolicyRef policy=SecPolicyCreateBasicX509();
CFIndex certificateCount=SecTrustGetCertificateCount(serverTrust);
NSMutableArray*信任链=[NSMutableArray阵列容量:(NSInteger)certificateCount];
对于(CFIndex i=0;i
这是一个代码表单
对于这种方式,如果在远程服务器中更新本地证书,则无需更新本地证书您能更具体一点吗?你在哪里得到固定的证书?您已将其作为参数传递给setPinnedCertificates方法。来自捆绑包的固定证书(本地证书)请阅读答案的最上面一句,您能给我一个示例吗?你怎么称呼这个方法?我们从哪里获得固定的证书?您是在谈论项目的捆绑资源中存在的证书吗?固定密钥是一个不错的解决方案。当然,理想的方法是创建您自己的根证书(自签名),该证书的过期日期较远(10年以上),使用该证书来签署具有较短过期日期(例如3年)的中间证书,并使用该证书来签署实际的服务器密钥。然后,提供中间证书和服务器证书作为链的一部分,并在客户端中添加根证书作为受信任的锚。这样,客户端在10年内不必更改(根证书到期),但您的服务器证书和密钥可以在需要时更改(风险较小)。