1

I am trying to use NSUrlsession to connect server with SSL (.p12) client certificate. I have connected to same server using NSURLConnection successfully. But with NSURLsession i get "cancelled" error. Following is my setup:

    -(void) loadDataFromServer{
      NSURL *url=[NSURL URLWithString:@"https://domain:port/serviceName/method/parameter"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionConfiguration *sessionConfig =[NSURLSessionConfiguration defaultSessionConfiguration];
      NSURLSession *session=[NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:Nil];
    NSURLSessionDataTask *downloadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"Data :%@",data);
        NSLog(@"Response :%@",response);
        NSLog(@"Error :%@",error); 
        if (!error) {
            NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
            if (httpResp.statusCode == 200) {     
                NSDictionary* json = [NSJSONSerialization
                                      JSONObjectWithData:data
                                      options:kNilOptions
                                      error:&error];
                NSLog(@"Data :%@",json);        
            }
        }   
    }];
    [downloadTask resume];}



    - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler{
    NSURLCredential *newCredential;
    NSString *authenticationMethod = challenge.protectionSpace.authenticationMethod;
    if ([authenticationMethod isEqualToString:@"NSURLAuthenticationMethodServerTrust"]) {

        // Replace the user login and password as appropriate.
        newCredential = [NSURLCredential credentialWithUser:@"UserName" password:@"Password"  persistence:NSURLCredentialPersistenceNone];

    } else if ([authenticationMethod isEqualToString:@"NSURLAuthenticationMethodClientCertificate"]) {
        // load cert
        NSString *path = [[NSBundle mainBundle] pathForResource:@"certificateName" ofType:@"p12"];
        NSData *p12data = [NSData dataWithContentsOfFile:path];
        CFDataRef inP12data = (__bridge CFDataRef)p12data;    
        SecIdentityRef myIdentity;
        SecTrustRef myTrust;
        OSStatus status = extractIdentityAndTrustCorpInv(inP12data, &myIdentity, &myTrust);

        if (status == 0) {
            SecCertificateRef myCertificate;
            SecIdentityCopyCertificate(myIdentity, &myCertificate);
            const void *certs[] = { myCertificate };
            CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);

            newCredential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistenceForSession];
        }
    }
    [[challenge sender]useCredential:newCredential forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCredential);
}


#pragma mark -
#pragma mark Identity and Trust
- OSStatus extractIdentityAndTrustCorpInv(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
    OSStatus securityError = errSecSuccess;  
    CFStringRef password = CFSTR("CertPassword");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
     CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
     CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import(inP12data, options, &items);

    if (securityError == 0) {
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
        *identity = (SecIdentityRef)tempIdentity;
        const void *tempTrust = NULL;
        tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
        *trust = (SecTrustRef)tempTrust;
    }  
    if (options) {
        CFRelease(options);
    }
    return securityError;
}

And NSLog gives following error:

Error :Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://domain:port/serviceName/method/parameter, NSErrorFailingURLStringKey=https://domain:port/serviceName/method/parameter, NSLocalizedDescription=cancelled}

Any help would be appreciated .

neo
  • 169
  • 2
  • 10
  • This sounds exactly like the same problem [in this related question](http://stackoverflow.com/questions/30739473/nsurlsession-nsurlconnection-http-load-failed-on-ios-9) (which I'm tempted to mark as a duplicate) – Michael Dautermann Oct 19 '15 at 18:59
  • Not a dup. ATS affects NSURLConnection and NSURLSession equally. – dgatwood Dec 03 '15 at 22:39

1 Answers1

2

NSURLAuthenticationMethodServerTrust does not do what you think it does. It is used for validating the TLS certificate that the server provides. You use it if you want to override a self-signed certificate.

With the code you provided, all your HTTPS requests will either be insecure (trusting any cert that the server provides) or will fail. IIRC, for NSURLConnection, it is insecure, and for NSURLSession, it fails.

It looks like that block of code should actually be running if the authentication method is either NSURLAuthenticationMethodHTTPBasic or NSURLAuthenticationMethodHTTPDigest.

If the method is NSURLAuthenticationMethodServerTrust, you should probably tell the URL loading system to use default handling.

If you're trying to use a self-signed cert for some reason, read Overriding SSL Chain Validation Correctly on Apple's website to learn how to do it in a way that is secure.

dgatwood
  • 10,129
  • 1
  • 28
  • 49