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; }]; |
} ??
如果调用方法
中的代码
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); } } |
您可以如下更改服务器信任代码:
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; }]; |
} ??