关于ios:SecTrustEvaluate始终返回kSecTrustResultRecoverableTrustFailure

SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
- (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;

}];

} ??


如果调用方法setSessionDidReceiveAuthenticationChallengeBlock,则AFSecurityPolicy将不起作用。这是AFNetworking

中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (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的实例。
您可以如下更改服务器信任代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
- (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;

}];

} ??