【问题标题】:SSL certificate validation in NSURLSessionNSURLSession 中的 SSL 证书验证
【发布时间】:2016-12-06 03:10:53
【问题描述】:

我们可以通过哪些不同的方式在我的 ios 应用中实施 SSL 证书验证? 目前我正在做的是将远程 SSL 证书数据与本地证书数据进行比较。但是这种方法的缺点是每次我们更改远程证书时,我们都需要在我们的应用程序中向客户端请求更新,这很烦人。 除了比较数据,还有其他方法可以执行 SSL 证书验证吗? 这是我在 didReceiveChallenge 中的代码。

-(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_pathToCertificate 是我的本地证书的路径。

【问题讨论】:

    标签: ios ssl https nsurlsession


    【解决方案1】:

    不同的方法是使用Public keys(Pinning the key) 进行比较,更多内容请查看wiki

    首先你加载你在Bundle中的所有证书

    + (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];
    

    并从中创建public key

    - (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];
    }
    

    此代码形式为AFNetwking

    如果本地证书在远程服务器中更新,则无需更新本地证书

    【讨论】:

    • 你能说得更具体些吗?你在哪里得到 pinnedCertificates ?您已将其作为参数传递给 setPinnedCertificates 方法。
    • 您捆绑的固定证书(本地证书)请阅读答案的第一句话
    • 你能举个例子吗?你怎么称呼那个方法?我们从哪里获得固定证书?
    • 您说的是我的项目捆绑资源中存在的证书吗?
    • 固定密钥是一个不错的解决方案。当然,理想的方法是创建您自己的具有远期(10 年以上)的根证书(自签名),使用它来签署具有短期(例如 3 年)的中间证书,然后使用它签署实际的服务器密钥。然后,提供中间证书和服务器证书作为链的一部分,并将根证书添加为客户端中的受信任锚。这样,客户端在 10 年内不必更改(根证书到期),但您的服务器证书和密钥可以在需要时更改(风险较小)。
    猜你喜欢
    • 2017-06-13
    • 2019-03-29
    • 2012-06-23
    • 2015-12-15
    • 2014-09-24
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多