Problems with SSL Pinning and AFNetworking 2.5.0 (NSURLErrorDomain error -1012.)
Asked Answered
E

9

34

We’ve been having a hard time securing our app’s network connections with SSL using AFNetworking 2.5.0.

We use a self-signed certificate authority and implemented a custom security policy using pinned certificates.

We’ve tested quite a few configurations provided by AFNetworking but have not been lucky so far. The error message we receive is:

2015-01-05 19:03:07.191 AppName[9301:319051] Error updating user journey. Error: Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x7ae056b0 {NSErrorFailingURLKey=https://api.XXX.com/XXX/XXX/, NSErrorFailingURLStringKey=https://api.XXX.com/XXX/XXX/}

Our certificate works fine on other clients such as cURL and Android. When using HTTP, our implementation works perfectly fine too.

Is anyone aware of any issues related to pinned certificates and AFNetworking? If yes, we’d appreciate any pointers you may have.

Here's part of the implementation:

+ (AFSecurityPolicy*)customSecurityPolicy {
   AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
   NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"der"];
   NSData *certData = [NSData dataWithContentsOfFile:cerPath];
   [securityPolicy setAllowInvalidCertificates:NO];
   [securityPolicy setValidatesCertificateChain:NO];
   [securityPolicy setPinnedCertificates:@[certData]];
   return securityPolicy;
}

+ (AFHTTPRequestOperationManager*)customHttpRequestOperationManager {
   AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
   manager.securityPolicy = [self customSecurityPolicy]; // SSL
   return manager;
}

+(void)getRequestWithUrl:(NSString*)url success:(void(^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void(^) (AFHTTPRequestOperation *operation, NSError *error))failure {
   [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
   AFHTTPRequestOperationManager *manager = [HttpClient customHttpRequestOperationManager];
   manager.responseSerializer = [AFHTTPResponseSerializer serializer];
   [manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
       [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
       success(operation, responseObject);
   } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
       [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
       failure(operation, error);
   }];
}

Thank you!

Embolectomy answered 6/1, 2015 at 22:22 Comment(1)
Can you provide your code inline as text? This makes it easier for others to copy/modify/test.Pistoleer
A
48

After reading through the AFNetworking code & and checking the change logs, here's what I had to do to get this working.

Create your AFSecurityPolicy object with AFSSLPinningModeCertificate:

AFSecurityPolicy* policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

By default, AFNetworking will validate the domain name of the certificate. Our certificates are generated on a per-server basis, and not all of them will have a domain name, so we need to disable that:

[policy setValidatesDomainName:NO];

Since the certificates are self-signed, they are technically 'invalid', so we need to allow that as well:

[policy setAllowInvalidCertificates:YES];

Lastly, AFNetworking will attempt to validate the certificate all the way up the certificate chain, which to me seems like it would only go up the chain to OUR CA, but for whatever reason it doesn't, so we have to disable that too:

[policy setValidatesCertificateChain:NO];

And that's it! Set the security policy in your request manager like you're already doing and it should work fine.

So, recap, all you really need to change in the code you posted is this:

A) As David Caunt mentioned, change your pinning mode from AFSSLPinningModeNone to AFSSLPinningModeCertificate

and

B) Add the line to disable validating the domain name: [policy setValidatesDomainName:NO]

Another note, AFNetworking now automatically checks your bundle for .cer files, so if you were to rename your certificate to have a .cer extension, you can eliminate the code to get the certificate data out of the bundle and set the pinned certificates.

Abuzz answered 4/2, 2015 at 20:18 Comment(8)
You should mark this as the accepted answer. worked for me when i had a NSURLErrorDomain code=-999 "cancelled". Saved me a ton of time, thanks!Claire
If you use dynamic frameworks via Cocoapods (use_frameworks!). The automatic detection of .cer files might not work. You can switch to use a bridging header to integrate AFNetworking into a swift project which fixed that issue for me.I suspect the reason for this is the different location of the lib if it is a dynamic framework.Cutup
Where to add this? I used dynamic frameworks via Cocoa pods and I am making use of objective C.Circumpolar
I use AFNetworking via CocoaPods as well. You want to get a new instance AFHTTPRequestOperationManager and call setSecurityPolicy: on that instance, passing the AFSecurityPolicy you create to that.Abuzz
I see this error very randomly.. Like suppose I see this in the first connection I make with the URL, it won't be there in the very next connection that I make. I wouldn't have been worried if it wasn't this random.Pumping
@Pumping Even I'm facing the same issue, it happens for first request and works fine for next request. Did you figure it out ?Nutlet
@Nutlet [securityPolicy setAllowInvalidCertificates:YES]; [securityPolicy setPinnedCertificates:@[certData]]; [securityPolicy setValidatesDomainName:NO]; [securityPolicy setValidatesCertificateChain:NO]; where certData is the the data of the certificate in my bundle. This worked for me. I never see the error after configuring my security policy in this manner. It's funny because other connections in my app work without taking these steps.Pumping
@Nutlet you can check the full answer below.Pumping
S
25

since you have manager initialized, you can do:

manager.securityPolicy.allowInvalidCertificates = YES;
manager.securityPolicy.validatesDomainName = NO;

and it will work for a self-signed certificate

Scarcely answered 26/10, 2015 at 4:52 Comment(0)
M
5

I was getting this error, Error Domain=NSURLErrorDomain Code=-1012 NSErrorFailingURLStringKey

The below change alone got it working for me.

self.validatesDomainName = NO;
Madame answered 22/10, 2015 at 6:14 Comment(1)
This was also necessary for me after applying the above-mentioned fixesTwelve
P
3

You're creating an AFSecurityPolicy with SSLPinningMode mode AFSSLPinningModeNone.

For AFNetworking to trust the server, with pinning mode set to AFSSLPinningModeNone, you must set allowInvalidCertificates to YES, but this is the opposite of what you are trying to achieve.

Instead, you should create your security policy with pinning mode AFSSLPinningModeCertificate or AFSSLPinningModePublicKey:

AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
Pistoleer answered 7/1, 2015 at 13:41 Comment(7)
Thanks for your answer @DavidCaunt. In fact, we've tried AFSSLPinningModeCertificate, AFSSLPinningModePublicKey and AFSSLPinningModeNone and none of them seem to work. Any other pointers? Thanks again.Embolectomy
Did you convert your cert to DER format using the following or similar? openssl x509 -in domain.crt -out domain.cer -outform derPistoleer
Thanks @DavidCaunt. Yes, the cert passed to AFNetworking has been converted to DER format.Embolectomy
If you set validatesDomainName to NO does it work? Assuming your certificate is set up correctly (correct host etc.) I would try these steps to grab the cert from the server and get it in the correct format. Otherwise, without your cert and API endpoint I am probably out of ideas.Pistoleer
Hi @DavidCaunt. Unfortunately not. I've followed the steps you've provided without luck. I am now investigating what is happening inside AFNetworking. After AF_Require_noErr(SecTrustEvaluate(serverTrust, &result), _out) the value in result is 5 (kSecTrustResultRecoverableTrustFailure) so AFServerTrustIsValid returns NO and the provided cert is considered to be invalid.Embolectomy
Did you ever make progress with this? I'm experiencing the same thing and am stuck at the same place (AFServerTrustIsValid == NO). This was previously working before updating from v. 2.3.1 to 2.5.0.Abuzz
I also have a similar problem, but on version 1 of AFNetworking.Comose
P
1
- (AFSecurityPolicy *)securityPolicy {
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"*.something.co.in" ofType:@"cer"];
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    [securityPolicy setAllowInvalidCertificates:YES];
    [securityPolicy setPinnedCertificates:@[certData]];
    [securityPolicy setValidatesDomainName:NO];
    [securityPolicy setValidatesCertificateChain:NO];
    return securityPolicy;
}

This worked for me for some reason. Still not sure how this changes things because other connections in my app work without taking all these steps.

This is what the error generating security policy looks like -

- (AFSecurityPolicy *)securityPolicy {
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"*.something.co.in" ofType:@"cer"];
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    [securityPolicy setAllowInvalidCertificates:NO];
    [securityPolicy setPinnedCertificates:@[certData]];
    [securityPolicy setValidatesDomainName:YES];
    return securityPolicy;
}

Now sticking to the "Don't fix if it ain't broken" rule

Pumping answered 2/3, 2016 at 7:7 Comment(0)
C
0

Having dealt with a similar issue, it is common for things seem normal via browser HTTPS connections, as many browsers cache certificate files from third parties so that they don't always need to be loaded. Therefore, it could very well be that your certificate chain is not fully trusted as you may have been led to believe.

In other words, it may seem fine to connect securely with a browser, but depending on your AFNetworking settings, your app won't actually accept your certificate chain. After you are sure your settings are appropriate, the next step is to make sure your certificate chain is actually as good as you think it is. Download an app called SSL Detective and query your server. You can also use www.ssldecoder.org. Make sure there are no red (untrusted) items in your chain. If there are, change your cert setup on the server.

Given that your AFNetworking settings are as follows:

 [securityPolicy setAllowInvalidCertificates:NO];
 [securityPolicy setValidatesCertificateChain:NO];

It may not like your certificate chain because it is self signed. You might also have to switch those to YES.

Comose answered 10/2, 2015 at 18:11 Comment(0)
C
0

For me, I had use_frameworks! set in my Podfile - the project doesn't make user of Swift and I was using Pods for AFNetworking. Commenting this out, fixed the issue for me.

Conover answered 15/3, 2017 at 7:37 Comment(0)
B
0

If you just want to silence the warning without having a client certificate, the policy should look like this:

AFSecurityPolicy* policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
[policy setValidatesDomainName:YES];
[policy setAllowInvalidCertificates:NO];
Beefwitted answered 26/12, 2018 at 15:13 Comment(0)
C
-2

I tried all of these but nothing helped then I searched for this line

'NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");'

and below this line I changed

'return NO;'
to

'return YES;'

and it did the magic.

Thanks.

Curbstone answered 21/11, 2015 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.