How to use NSURLConnection to connect with SSL for an untrusted cert?

后端 未结 13 1576
盖世英雄少女心
盖世英雄少女心 2020-11-22 01:29

I have the following simple code to connect to a SSL webpage

NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection send         


        
相关标签:
13条回答
  • 2020-11-22 01:53

    In iOS 9, SSL connections will fail for all invalid or self-signed certificates. This is the default behavior of the new App Transport Security feature in iOS 9.0 or later, and on OS X 10.11 and later.

    You can override this behavior in the Info.plist, by setting NSAllowsArbitraryLoads to YES in the NSAppTransportSecurity dictionary. However, I recommend overriding this setting for testing purposes only.

    For information see App Transport Technote here.

    0 讨论(0)
  • 2020-11-22 01:55

    To complement the accepted answer, for much better security, you could add your server certificate or your own root CA certificate to keychain( https://stackoverflow.com/a/9941559/1432048), however doing this alone won't make NSURLConnection authenticate your self-signed server automatically. You still need to add the below code to your NSURLConnection delegate, it's copied from Apple sample code AdvancedURLConnections, and you need to add two files(Credentials.h, Credentials.m) from apple sample code to your projects.

    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    //        if ([trustedHosts containsObject:challenge.protectionSpace.host])
    
        OSStatus                err;
        NSURLProtectionSpace *  protectionSpace;
        SecTrustRef             trust;
        SecTrustResultType      trustResult;
        BOOL                    trusted;
    
        protectionSpace = [challenge protectionSpace];
        assert(protectionSpace != nil);
    
        trust = [protectionSpace serverTrust];
        assert(trust != NULL);
        err = SecTrustEvaluate(trust, &trustResult);
        trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
    
        // If that fails, apply our certificates as anchors and see if that helps.
        //
        // It's perfectly acceptable to apply all of our certificates to the SecTrust
        // object, and let the SecTrust object sort out the mess.  Of course, this assumes
        // that the user trusts all certificates equally in all situations, which is implicit
        // in our user interface; you could provide a more sophisticated user interface
        // to allow the user to trust certain certificates for certain sites and so on).
    
        if ( ! trusted ) {
            err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates);
            if (err == noErr) {
                err = SecTrustEvaluate(trust, &trustResult);
            }
            trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
        }
        if(trusted)
            [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    }
    
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
    
    0 讨论(0)
  • 2020-11-22 01:58

    You have to use NSURLConnectionDelegate to allow HTTPS connections and there are new callbacks with iOS8.

    Deprecated:

    connection:canAuthenticateAgainstProtectionSpace:
    connection:didCancelAuthenticationChallenge:
    connection:didReceiveAuthenticationChallenge:
    

    Instead those, you need to declare:

    connectionShouldUseCredentialStorage: - Sent to determine whether the URL loader should use the credential storage for authenticating the connection.

    connection:willSendRequestForAuthenticationChallenge: - Tells the delegate that the connection will send a request for an authentication challenge.

    With willSendRequestForAuthenticationChallenge you can use challenge like you did with the deprecated methods, for example:

    // Trusting and not trusting connection to host: Self-signed certificate
    [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    
    0 讨论(0)
  • 2020-11-22 01:59

    There is a supported API for accomplishing this! Add something like this to your NSURLConnection delegate:

    - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
      return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
      if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
        if ([trustedHosts containsObject:challenge.protectionSpace.host])
          [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    
      [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
    

    Note that connection:didReceiveAuthenticationChallenge: can send its message to challenge.sender (much) later, after presenting a dialog box to the user if necessary, etc.

    0 讨论(0)
  • 2020-11-22 02:01

    You can use this Code

    -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    {
         if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust)
         {
             [[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge];
         }
    }
    

    Use -connection:willSendRequestForAuthenticationChallenge: instead of these Deprecated Methods

    Deprecated:

    -(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace  
    -(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
    -(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    
    0 讨论(0)
  • 2020-11-22 02:02

    NSURLRequest has a private method called setAllowsAnyHTTPSCertificate:forHost:, which will do exactly what you'd like. You could define the allowsAnyHTTPSCertificateForHost: method on NSURLRequest via a category, and set it to return YES for the host that you'd like to override.

    0 讨论(0)
提交回复
热议问题