NSURLConnection认证集中控制器

时间:2017-06-12 18:34:07

标签: objective-c authentication delegates ssl-certificate nsurlconnection

我有几个控制器管理他们自己的连接和数据,这些都是完美的。我只是想集中canAuthenticateAgainstProtectionSpace并进行ReceiveAuthenticationChallenge以避免重复代码。

我试图覆盖该课程但没有成功。

课程和协议:

#import <Foundation/Foundation.h>

@protocol SSLPiningDelegate;

@interface SSLPiningDelegate : NSURLConnection <NSURLConnectionDelegate>
{
    id<SSLPiningDelegate> delegate;
}

@property (nonatomic,assign) id<SSLPiningDelegate> delegate;

@end



@protocol SSLPiningDelegate <NSURLConnectionDelegate>
@optional
-(id)initWithDelegate:(id)delegate;
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

@end

代码:

#import "SSLPiningDelegate.h"


@interface SSLPiningDelegate ()
{
    CFArrayRef caChainArrayRef;
    BOOL checkHostname;

}


@end

@implementation SSLPiningDelegate

-(id)initWithDelegate:(id)delegate {
    //CGRect rect = [[UIScreen mainScreen] bounds];
    if ((self = [super init])) {
        _delegate = delegate;
    }
    return self;
}

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {

    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    // TODO manage this correctly
    checkHostname = YES;

    NSString * derCaPath;
    NSMutableArray * chain = [NSMutableArray array];

    for(int i=0; i<= 3; i++)
    {
        if (i==0)
            derCaPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];
        else if (i==1)
            derCaPath = [[NSBundle mainBundle] pathForResource:@"comodorsacertificationauthority" ofType:@"cer"];
        else if (i==2)
            derCaPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];
        else
            derCaPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];

        NSData *derCA = [NSData dataWithContentsOfFile:derCaPath];

        SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)derCA);

        [chain addObject:(__bridge id)(caRef)];
    }


    caChainArrayRef = CFBridgingRetain(chain);
    NSLog(@"Loading::didReceiveAuthenticationChallenge --- chain of trust length %lu", (unsigned long)[chain count]);

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        SecTrustRef trust = nil;
        SecTrustResultType result = kSecTrustResultInvalid;
        OSStatus err = errSecSuccess;

        {
            NSLog(@"Chain received from the server (working 'up'):");
            CFIndex certificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);
            for(int i = 0; i < certificateCount; i++) {
                SecCertificateRef certRef = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
                CFStringRef str = SecCertificateCopySubjectSummary(certRef);
                NSLog(@"   %02i: %@", 1+i, str);
                CFRelease(str);
            }

            NSLog(@"Local Roots we trust:");
            for(int i = 0; i < CFArrayGetCount(caChainArrayRef); i++) {
                SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(caChainArrayRef, i);
                CFStringRef str = SecCertificateCopySubjectSummary(certRef);
                NSLog(@"   %02i: %@", 1+i, str);
                CFRelease(str);
            }
        }

        if (checkHostname) {
            // We use the standard Policy of SSL - which also checks hostnames.
            // -- see SecPolicyCreateSSL() for details.
            //
            trust = challenge.protectionSpace.serverTrust;
            //
#if DEBUG
            NSLog(@"The certificate is expected to match '%@' as the hostname",
                  challenge.protectionSpace.host);
#endif
        }
        else {
            // Create a new Policy - which goes easy on the hostname.
            //

            // Extract the chain of certificates provided by the server.
            //
            CFIndex certificateCount = SecTrustGetCertificateCount(challenge.protectionSpace.serverTrust);
            NSMutableArray * chain = [NSMutableArray array];

            for(int i = 0; i < certificateCount; i++) {
                SecCertificateRef certRef = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, i);
                [chain addObject:(__bridge id)(certRef)];
            }

            for(int i = 0; i < CFArrayGetCount(caChainArrayRef); i++) {
                SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(caChainArrayRef, i);
                [chain addObject:(__bridge id)(certRef)];
            }


            // And create a bland policy which only checks signature paths.
            //
            if (err == errSecSuccess)
                err = SecTrustCreateWithCertificates((__bridge CFArrayRef)(chain),
                                                     SecPolicyCreateBasicX509(), &trust);
#if DEBUG
            NSLog(@"The certificate is NOT expected to match the hostname '%@' ",
                  challenge.protectionSpace.host);
#endif
        }

        // Explicity specify the list of certificates we actually trust (i.e. those I have hardcoded
        // in the app - rather than those provided by some randon server on the internet).
        //
        if (err == errSecSuccess)
            err = SecTrustSetAnchorCertificates(trust,caChainArrayRef);

        // And only use above - i.e. do not check the system its global keychain or something
        // else the user may have fiddled with.
        //
        if (err == errSecSuccess)
            err = SecTrustSetAnchorCertificatesOnly(trust, YES);

        if (err == errSecSuccess)
            err = SecTrustEvaluate(trust, &result);

        if (err == errSecSuccess) {
            switch (result) {
                case kSecTrustResultProceed:
                    // User gave explicit permission to trust this specific
                    // root at some point (in the past).
                    //
                    NSLog(@"GOOD. kSecTrustResultProceed - the user explicitly trusts this CA");
                    [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust]
                         forAuthenticationChallenge:challenge];
                    goto done;
                    break;
                case kSecTrustResultUnspecified:
                    // The chain is technically valid and matches up to the root
                    // we provided. The user has not had any say in this though,
                    // hence it is not a kSecTrustResultProceed.
                    //
                    NSLog(@"GOOD. kSecTrustResultUnspecified - So things are technically trusted. But the user was not involved.");
                    [challenge.sender useCredential:[NSURLCredential credentialForTrust:trust]
                         forAuthenticationChallenge:challenge];
                    goto done;
                    break;
                case kSecTrustResultInvalid:
                    NSLog(@"FAIL. kSecTrustResultInvalid");
                    break;
                case kSecTrustResultDeny:
                    NSLog(@"FAIL. kSecTrustResultDeny (i.e. user said no explicitly)");
                    break;
                case kSecTrustResultFatalTrustFailure:
                    NSLog(@"FAIL. kSecTrustResultFatalTrustFailure");
                    break;
                case kSecTrustResultOtherError:
                    NSLog(@"FAIL. kSecTrustResultOtherError");
                    break;
                case kSecTrustResultRecoverableTrustFailure:
                    NSLog(@"FAIL. kSecTrustResultRecoverableTrustFailure (i.e. user could say OK, but has not been asked this)");
                    break;
                default:
                    NSAssert(NO,@"Unexpected result: %d", result);
                    break;
            }
            // Reject.
            [challenge.sender cancelAuthenticationChallenge:challenge];
            goto done;
        };

        //CFStringRef str =SecCopyErrorMessageString(err,NULL);
        //NSLog(@"Internal failure to validate: result %@", str);
        //CFRelease(str);

        [[challenge sender] cancelAuthenticationChallenge:challenge];

    done:
        if (!checkHostname)
            CFRelease(trust);
        return;
    }
    // In this example we can cancel at this point - as we only do
    // canAuthenticateAgainstProtectionSpace against ServerTrust.
    //
    // But in other situations a more gentle continue may be appropriate.
    //
    // [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];

    NSLog(@"Not something we can handle - so we're canceling it.");
    [challenge.sender cancelAuthenticationChallenge:challenge];
}


@end

然后我宣布控制器

#import <UIKit/UIKit.h>
#import "TagUIViewControllerLoading.h"
#import <Foundation/Foundation.h>
#import "SSLPiningDelegate.h"

@interface Loading : TagUIViewControllerLoading <SSLPiningDelegate>

但代表从未被召集过。

欢迎任何帮助。

1 个答案:

答案 0 :(得分:0)

事实上,我试图在第三级(控制器使用委托调用控制器构建)进行委托。第二个控制器级别附带的代码工作正常。