SecTrustEvaluate始终返回kSecTrustResultRecoverableTrustFailure

时间:2016-10-19 01:31:45

标签: ios objective-c ssl tls1.2 afnetworking-3

我想用afnetworking实现自签名连接。 我有一个p12和der文件尝试评估自签名证书的服务器信任证书,使用相同的证书文件android客户端连接成功,经过大量的研究我仍然无法解决。任何人都请帮助我。 那是我的完整代码

- (void)sendRequest{
// Do any additional setup after loading the view, typically from a nib.
NSURL * actionURL=[[NSURL alloc] initWithString:@"https://38.121.62.19"];
NSURL * relativeURL=[[NSURL alloc] initWithString:@"/"];
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"der"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];

AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
[securityPolicy setPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];
[securityPolicy setAllowInvalidCertificates:kAllowsInvalidSSLCertificate];
[securityPolicy setValidatesDomainName:NO];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:actionURL];
[manager setSecurityPolicy:securityPolicy];
manager.responseSerializer=[AFHTTPResponseSerializer serializer];
[manager GET:relativeURL.absoluteString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
    NSLog(@"Success");

} failure:^(NSURLSessionTask *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
//    [manager setTaskDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing  _Nullable * _Nullable credential) {

[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing  _Nullable * _Nullable credential) {
    if ([challenge previousFailureCount] > 0) {
        //this will cause an authentication failure
        [[challenge sender] cancelAuthenticationChallenge:challenge];
        NSLog(@"Bad Username Or Password");
        return NSURLSessionAuthChallengeUseCredential;
    }
    //this is checking the server certificate
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        SecTrustResultType result;
        //This takes the serverTrust object and checkes it against your keychain
        SecTrustEvaluate(challenge.protectionSpace.serverTrust, &result);


        //if we want to ignore invalid server for certificates, we just accept the server
        if (kAllowsInvalidSSLCertificate) {
            NSData *rootCertData = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"server" ofType:@"der"]];
            CFDataRef myCertData = (__bridge CFDataRef)rootCertData;
            SecCertificateRef rootCertRef = SecCertificateCreateWithData(NULL, (CFDataRef) myCertData);
            SecTrustRef trust = [[challenge protectionSpace] serverTrust];         // Create trust object
            NSArray *trustArray = [NSArray arrayWithObjects:(__bridge id _Nonnull)(rootCertRef), nil];   // Add as many certificates as needed

            SecTrustSetAnchorCertificates(trust, (CFArrayRef) trustArray );
            SecTrustSetAnchorCertificatesOnly(trust, YES);
            SecTrustResultType trustResult;                                        // Store trust result in this
            SecTrustEvaluate(trust, &trustResult);
            SecTrustResultType result1;

            [challenge.sender useCredential:[NSURLCredential credentialForTrust: trust] forAuthenticationChallenge: challenge];
            return NSURLSessionAuthChallengeUseCredential;

        } else if(result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
            //When testing this against a trusted server I got kSecTrustResultUnspecified every time. But the other two match the description of a trusted server
            [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
            return NSURLSessionAuthChallengeUseCredential;

        }
    } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
        //this handles authenticating the client certificate

        /*
         What we need to do here is get the certificate and an an identity so we can do this:
         NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:myCerts persistence:NSURLCredentialPersistencePermanent];
         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];

         It's easy to load the certificate using the code in -installCertificate
         It's more difficult to get the identity.
         We can get it from a .p12 file, but you need a passphrase:
         */

        NSData *p12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];

        CFStringRef password = CFSTR("123456");
        const void *keys[] = { kSecImportExportPassphrase };
        const void *values[] = { password };
        CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        CFArrayRef p12Items;

        OSStatus result = SecPKCS12Import((__bridge CFDataRef)p12Data, optionsDictionary, &p12Items);

        if(result == noErr) {
            CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
            SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);

            SecCertificateRef certRef;
            SecIdentityCopyCertificate(identityApp, &certRef);

            SecCertificateRef certArray[1] = { certRef };
            CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
            CFRelease(certRef);

            NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:(__bridge NSArray *)myCerts persistence:NSURLCredentialPersistencePermanent];
            CFRelease(myCerts);

            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        } else {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
    } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodDefault || [[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodNTLM) {

        // For normal authentication based on username and password. This could be NTLM or Default.
        /*
         DAVCredentials *cred = _parentSession.credentials;
         NSURLCredential *credential = [NSURLCredential credentialWithUser:cred.username password:cred.password persistence:NSURLCredentialPersistenceForSession];
         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
         */

        NSLog(@"BASIC AUTHENTICATION");

    } else {
        //If everything fails, we cancel the challenge.
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
    return NSURLSessionAuthChallengePerformDefaultHandling;

}];

}

2 个答案:

答案 0 :(得分:1)

如果您致电方法setSessionDidReceiveAuthenticationChallengeBlockAFSecurityPolicy将无效。以下是AFNetworking

中的代码
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
   NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
   __block NSURLCredential *credential = nil;

   if (self.sessionDidReceiveAuthenticationChallenge) {
       disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
   } else {
       if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
           if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
               credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
               if (credential) {
                   disposition = NSURLSessionAuthChallengeUseCredential;
               } else {
                   disposition = NSURLSessionAuthChallengePerformDefaultHandling;
               }
           } else {
               disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
           }
       } else {
           disposition = NSURLSessionAuthChallengePerformDefaultHandling;
       }
   }

   if (completionHandler) {
       completionHandler(disposition, credential);
   }   
}

AFSecurityPolicysessionDidReceiveAuthenticationChallenge无法合作。您不需要配置AFSecurityPolicy的实例 您可以更改服务器信任代码,如下所示:

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if (kAllowsInvalidSSLCertificate) {
            NSData *rootCertData = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"server" ofType:@"der"]];
            CFDataRef myCertData = (__bridge CFDataRef)rootCertData;
            SecCertificateRef rootCertRef = SecCertificateCreateWithData(NULL, (CFDataRef) myCertData);
            SecTrustRef trust = [[challenge protectionSpace] serverTrust];         // Create trust object
            NSArray *trustArray = [NSArray arrayWithObjects:(__bridge id _Nonnull)(rootCertRef), nil];   // Add as many certificates as needed

            SecTrustSetAnchorCertificates(trust, (CFArrayRef) trustArray );
            SecTrustSetAnchorCertificatesOnly(trust, YES);
            SecTrustResultType trustResult;                                        // Store trust result in this
            SecTrustEvaluate(trust, &trustResult);

            BOOL certificateIsValid = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
            if (certificateIsValid) {
                *credential = [NSURLCredential credentialForTrust:trust];
                return NSURLSessionAuthChallengeUseCredential;
            } else {
                return NSURLSessionAuthChallengePerformDefaultHandling;
            }
        }
    }

答案 1 :(得分:0)

效果很好,下面的代码正确

- (void)sendRequest{
// Do any additional setup after loading the view, typically from a nib.
NSURL * actionURL=[[NSURL alloc] initWithString:@"https://38.121.62.19"];
NSURL * relativeURL=[[NSURL alloc] initWithString:@"/"];
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"der"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];

AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
[securityPolicy setPinnedCertificates:[[NSSet alloc] initWithObjects:certData, nil]];
[securityPolicy setAllowInvalidCertificates:kAllowsInvalidSSLCertificate];
[securityPolicy setValidatesDomainName:NO];
_manager = [[AFHTTPSessionManager alloc] initWithBaseURL:actionURL];
[_manager setSecurityPolicy:securityPolicy];
_manager.responseSerializer=[AFHTTPResponseSerializer serializer];
[_manager GET:relativeURL.absoluteString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
    NSLog(@"Success");

} failure:^(NSURLSessionTask *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
//    [manager setTaskDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing  _Nullable * _Nullable credential) {
__weak typeof(self)weakSelf = self;
[_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing  _Nullable * _Nullable _credential) {

    if ([challenge previousFailureCount] > 0) {
        //this will cause an authentication failure
        [[challenge sender] cancelAuthenticationChallenge:challenge];
        NSLog(@"Bad Username Or Password");
        return NSURLSessionAuthChallengeUseCredential;
    }
    //this is checking the server certificate
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
         NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __autoreleasing NSURLCredential *credential =nil;
        if([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            if(credential) {
                disposition =NSURLSessionAuthChallengeUseCredential;
            } else {
                disposition =NSURLSessionAuthChallengePerformDefaultHandling;
            }
        } else {
            disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
        }
    } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
        //this handles authenticating the client certificate

        /*
         What we need to do here is get the certificate and an an identity so we can do this:
         NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:myCerts persistence:NSURLCredentialPersistencePermanent];
         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];

         It's easy to load the certificate using the code in -installCertificate
         It's more difficult to get the identity.
         We can get it from a .p12 file, but you need a passphrase:
         */

        NSData *p12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];

        CFStringRef password = CFSTR("123456");
        const void *keys[] = { kSecImportExportPassphrase };
        const void *values[] = { password };
        CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        CFArrayRef p12Items;

        OSStatus result = SecPKCS12Import((__bridge CFDataRef)p12Data, optionsDictionary, &p12Items);

        if(result == noErr) {
            CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
            SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);

            SecCertificateRef certRef;
            SecIdentityCopyCertificate(identityApp, &certRef);

            SecCertificateRef certArray[1] = { certRef };
            CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
            CFRelease(certRef);

            NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:(__bridge NSArray *)myCerts persistence:NSURLCredentialPersistencePermanent];
            CFRelease(myCerts);
            *_credential = credential;
//                [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
        } else {
            [[challenge sender] cancelAuthenticationChallenge:challenge];
        }
    } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodDefault || [[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodNTLM) {

        // For normal authentication based on username and password. This could be NTLM or Default.
        /*
         DAVCredentials *cred = _parentSession.credentials;
         NSURLCredential *credential = [NSURLCredential credentialWithUser:cred.username password:cred.password persistence:NSURLCredentialPersistenceForSession];
         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
         */

        NSLog(@"BASIC AUTHENTICATION");

    } else {
        //If everything fails, we cancel the challenge.
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
    return NSURLSessionAuthChallengeUseCredential;

}];

}