使用客户端SSL证书的NSURLSession提供"错误域= NSURLErrorDomain代码= -999"取消""

时间:2015-10-19 18:53:27

标签: objective-c authentication ssl-certificate ios9 nsurlsession

我正在尝试使用NSUrlsession将服务器与SSL(.p12)客户端证书连接起来。我已成功使用NSURLConnection连接到同一台服务器。   但是凭借NSURLsession,我得到了#34;取消了#34;错误。   以下是我的设置:

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

NSLog给出以下错误:

错误:错误域= NSURLErrorDomain代码= -999"已取消" UserInfo = {NSErrorFailingURLKey = https://domain:port/serviceName/method/parameter,NSErrorFailingURLStringKey = https://domain:port/serviceName/method/parameter,NSLocalizedDescription = cancel}

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:1)

NSURLAuthenticationMethodServerTrust 不会按照您的想法执行操作。它用于验证服务器提供的TLS证书。如果要覆盖自签名证书,请使用它。

使用您提供的代码,您的所有HTTPS请求都将不安全(信任服务器提供的任何证书)或将失败。 IIRC,对于NSURLConnection,它是不安全的,对于NSURLSession,它失败了。

如果身份验证方法为 NSURLAuthenticationMethodHTTPBasic NSURLAuthenticationMethodHTTPDigest ,则看起来该代码块实际上应该正在运行。

如果方法是 NSURLAuthenticationMethodServerTrust ,您应该告诉URL加载系统使用默认处理。

如果您出于某种原因尝试使用自签名证书,请阅读Apple网站上的Overriding SSL Chain Validation Correctly,了解如何以安全的方式进行操作。