Although certificates are pinned, NSURLErrorDomain error -1012 - ios

[manager setSecurityPolicy:[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]];
NSLog(#"Using %lu pinned certificates", (unsigned long)[requestManager.securityPolicy.pinnedCertificates count]);
I am using AFNetworking 2.0, and I have enabled certificate authentication on the server. I have placed two .crt files in the xcode project. and whenever I try to establish a https connection to the server I get the NSURLErrorDomain error -1012. Interesting thing to note here is that in my logs I can see the count of loaded certificates as 2. So, I guess pinning is successful, can someone help me with whats is wrong here?
Also, instead of pinning if I write - [sec setAllowInvalidCertificates:YES] then everything works fine.

Related

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) error not on all iPhones?

one of my apps cannot connect to it's API server. The logged reason is NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813). So far no luck on finding why or what, but the most frustrating thing is, that this problem appears on some of my test devices. On other everything is fine. I am using NSURLConnection:sendSynchronousRequest:returningResponse:error:, but after some refactoring and trying with NSURLSessionDataTask:dataTaskWithRequest:completionHandler:, the problem still occurs.
Also added
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
and still nothing.....
p.s. even though my first problem is duplicate of many others, my main concern is why this error appears only on ONE of my testing devices (after Settings -> General -> Reset it works fine too, but how to tell Apple "please, reset your testing device"?). The other work perfectly fine.
Error -9813 is errSSLNoRootCert. This means that the certificate chain provided by the server does not reach a trusted root (anchor) cert, and is usually caused by missing certificates in the chain.
The reason for the difference from one device to another could be because some devices were able to successfully fetch the missing certificates (OCSP?), because some devices have additional trusted anchor certs that are not present on the other devices for whatever reason, or because some of the certificates in the chain are signed using a key signing algorithm that is no longer accepted on iOS 9 and thus are being ignored.
I would suggest that you contact your TLS certificate vendor and get a new copy of the TLS chain file, update the chain file on the server, restart the server, and see if the problem goes away.
If that doesn't help, please edit your question with a URL that we can test, along with the results of running openssl s_client -connect yourhostname:443 against the host in question.
More tips here:
iOS HTTPS requests 101
https://developer.apple.com/library/ios/technotes/tn2232/_index.html

Error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."

I am working on an application in Xcode 6.1, iOS 8.1; the application was working completely fine till 2 days before, but today as I executed it I got an error in the web service & the error is printed below.
Error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed
out." UserInfo=0x7c6899b0 {NSErrorFailingURLStringKey=,
NSErrorFailingURLKey=, NSLocalizedDescription=The request timed
out., NSUnderlyingError=0x7c688f60 "The request timed out."}
I had used AFNetworking 2.x and following code snippet to make network call:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes=[manager.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
[manager POST:<URL>
parameters:<parameters>
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
NSError *error = nil;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error];
if (error) {
NSLog(#"Error serializing %#", error);
}
NSLog(#"Dictionary %#", JSON);
NSLog(#"Success");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
UPDATE: I had done quit/relaunched the iOS Simulator.app, reseted content & settings, but nothing worked.
There was no issue in the code. I guess the simulator internally was not able to connect to the internet, so that is why it was not able to connect to the server after providing various timeout intervals. But it worked completely fine when I executed another day. Thanks to #Andrew Jaffee for helping me to find this issue.
iOS Simulator -> Reset Content and Settings; worked for me
Had this problem and had a different resolution so I thought I'd add it here:
Basically, I was sending some parameters when it should have been a clean GET request. Deleted the parameters, GET request worked just fine.
I was calling local server connection and was getting this error. I was using different network in my device and phone. When I connected both to same wifi, it worked.
This can happen if your network configuration changes while the simulator is running. Please reboot the simulator runtime (eg: quit/relaunch the iOS Simulator.app) and that will likely fix your problem.
If the problem persists, please update your question to indicate that the issue you are experiencing is not this known problem. Thanks.
Ok, I lost a lot of time investigeting similar issue.
In my case the problem was strange (bad?) firewall on the server. It was banning device when there was many (not so many) requests in short period of time.
I believe you can do easy test if you are facing similar issue.
Send a lot of (depends of firewall settings) requests in loop (let's say 50 in 1 second).
Close/Kill app (this will close connections to server)
(OPTIONAL) Wait a while (lets say 60 seconds)
Start app again and try send request
If you now got timeout for all next requests, you probably have same issue and you should talk
with sever guys.
PS: if you don't have access to server you can give user info that he should restart wifi on device to quit that timeout loop. It could be last resort in some cases.
I am working for an enterprise company so we use VPN for connecting our web services.
My computer connected to VPN so my simulators (xcode simulator) could VPN but my own iPhone (real phone) was not connected to VPN so the issue occur for this reason your simulator should be connected to URLs
Kidly check your URL in the simulator.
You must close the firewall and it will work. I tried this solution and it worked for me.

I cannot get push notifications to work with ARCGIS after deleting and recreating certificates many times

I have spent hours deleting certificates and remaking them. I've followed this tutorial https://developers.arcgis.com/en/geotrigger-service/guide/ios-push-notifications/ many times and I still cannot get push notifications to work. I was using the geoloqi.com API but they were bought by esri and just released a new api. I had push working with geoloqi.
The only thing questionable about that tutorial is where it says "Paste the pem file in the box" I just copied from finder and pasted it like it said but it's weird because usually there's a browse for file button and then you upload. I don't know, thats probably not the problem but I thought I'd bring it up.
With this test code:
[[AGSGTApiClient sharedClient] postPath:#"device/notify"
parameters:#{#"text": #"Push notifications are working!", #"url": #"http://developers.esri.com"}
success:^(id res) {
NSLog(#"device/notify success: %#", res);
}
failure:^(NSError *err) {
NSLog(#"device/notify failure: %#", err.userInfo);
}];
I get:
device/notify success: {
devices = {
TKhqTzrTSQI0DGBa = queued;
};
But I never receive the push. Does anybody have a suggestion I can try next because I am lost?
The first thing you'll want to check is whether you have the proper certificates configured for your application. The best way to do this is completely outside the Geotrigger API so that it's not adding more steps to the mix. There are any number of ways you can do this, but we have also written a guide you can follow here: https://github.com/Esri/pushlet/tree/master/client
If you are familiar with Node.js, you can run the Pushlet server yourself and send test notifications to your device. If you are not familiar with Node.js, you can follow the first two steps, Set up the Certificates and Test the Connection.
Once you have verified you are not getting any SSL errors communicating with APNS, and you have verified you can use the certificates to send a push notification to your device directly, if you're still having trouble with sending a push notification through the Geotrigger API, you can contact our support at geotrigger-support#esri.com.

SSL - behaves differently in iOS7?

I'm working on an iOS Enterprise POS app that communicates with a server using https. I've looked at Receiving SSL error in iOS7 GM - "AddTrust External CA Root" is not trusted? and Difference between self-signed CA and self-signed certificate and generally scoured the web but I'm not getting anywhere.
The app works fine on iOS6.1 using either http or https protocols. It also works fine on iOS 7GM over http, but not over https - it fails on the first message it sends to the server. On the app side I handle the authentication challenge in:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
forAuthenticationChallenge:challenge];
}
after which I get a callback to:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
NOT:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
I believe that means the client and server successfully negotiated a connection, agreed upon an encryption protocol, etc. Unfortunately although the return appears successful (as far as the network stack is concerned), I get back 0 bytes of data in the AMF payload.
Here's the interesting part - on the server side (JBoss4.2.3) I can breakpoint and examine the httpRequest body containing the AMFRequest. Over http I always get 384 bytes in the body. Over https, I get 384 bytes if the client is iOS 6.1, but 0 bytes if the client is iOS 7. I interpret that as the https request was accepted "normally" by the server with no errors, security violations, etc.
One more data point. If I run Charles on the client side everything works correctly over https using the iOS 7 simulator. I can see my 384 byte AMFRequest just fine in both Charles and on the server. Charles works as an http proxy - but the app doesn't know anything about that, so why does inserting Charles as an intermediary make it work? I've installed Charles' certificate so I think it is communicating to the server over SSL (don't know for sure).
Thanks for any help or suggestions.
Update: I implemented Apple's recommended approach:
- (void)connection: (NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
traceMethod();
SecTrustRef trust = challenge.protectionSpace.serverTrust;
NSURLCredential *credential = [NSURLCredential credentialForTrust: trust];
[challenge.sender useCredential: credential
forAuthenticationChallenge: challenge];
}
but it gets exactly the same result as the old (pre iOS 5) way.
After considerable research I opened a incident with Apple Developer Tech Support and eventually have an explanation.
I've been able to confirm that Apple made a change in iOS 7 as a "recommended countermeasure to the BEAST attack", which is a "man in the middle" attack against the SSL TLS protocol - see this article on CERT.
The ideal solution would be to change the JBoss (Tomcat) ssl connector to use:
sslProtocol="TLSv1.2"
Unfortunately the stock JBoss4.2.3GA implementation is unable to handle TLSv1.1 or TLSv1.2, or process messages that use this countermeasure. It appears the only solution is to upgrade the JBoss configuration. That requires significant JBoss changes - see this JBoss article.
An alternative is to re-write the networking methods to use a lower level framework (CFSocketStream instead of NSURLConnection) and disable the BEAST countermeasure. That has two downsides - it re-exposes the security vulnerability and it is a non-trivial implementation that would need thorough testing (especially simulating network edge cases).
In my case time does not permit changes of such magnitude prior the Holiday season. My client is a major retailer and their IT department locks down the environment in mid-October.
Perhaps this information will help someone else.
There have definitely been changes to SSL in the API
I don't know much about how SSL works, but I noticed that MKNetworkKit is using a now deprecated in iOS 7 constant called kSecTrustResultConfirm (found in SecTrust.h in the Security-Framework):
#constant kSecTrustResultConfirm Indicates confirmation with the user
is required before proceeding. Important: this value is no longer returned
or supported by SecTrustEvaluate or the SecTrustSettings API starting in
OS X 10.5; its use is deprecated in OS X 10.9 and later, as well as in iOS.
Maybe this is a point in the right direction? Anyway, good luck solving your issue!
This is the commit diff in which someone "fixed" this in MKNetworkKit:
https://github.com/MugunthKumar/MKNetworkKit/commit/c28959805991bb8f0e99ede9c822e985b41f6fc9
(scroll down to L:1142)
Had this same issue getting iOS 7 to talk to Jboss eap 6.0.1 resteasy methods.
The answer is to set the ssl tags protocol to TLSv1.2.
e.g.
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true">
<ssl name="ssl" key-alias="awssink" password="keystore-password" certificate-key-file="${jboss.server.config.dir}/attimoto.jks" verify-client="false" protocol="TLSv1.2"/>
</connector>
Now we are using Bearer tokens to access the restful methods so using AFNetworking 2+ we do something like this:
NSString *secureURLString = [secureBaseURLString stringByAppendingString:BURP];
NSLog(#"the secure url is %#\n\n", secureURLString);
// set up the request manager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
// allow invalid certificates
manager.securityPolicy.allowInvalidCertificates = YES;
//Note we dont deal with pinned certificates since we are doing bearer token
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
[manager.requestSerializer setValue:BEARER_TOKEN forHTTPHeaderField:#"Authorization"];
//Note we do NOT use setAuthorizationHeaderFieldWithToken:BEARER_TOKEN];
//Ultimately you want just a header like this
// "Authorization: Bearer textofthebearertoken"
//So BEARER_TOKEN is literally #"Bearer thetextofthebearertoken"
[manager GET:secureURLString
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#" RESPONSE RETURNED %#\n\n", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error = %#", error);
}];
Had an issue with NSURLConnection and Amazons ELB & EC2 instance, where ELB didn't have TLS v1.2 enabled, causing 15-50% of the requests not being processed properly (HTTPS PUT with JSON in the body). Turning TLS v1.2 solved this issue... Scratching my head trying to figure out the logic behind this fact.

Switching from http to https. Invalid certificate

I have an app that connects to my home routers web interface. I want to convert this to use https instead of just http.
I was originally using ASIHttpRequest, but as it's no longer supported i'm switching over to AFNetworking.
The problem is, whenever I try to connect, I get this error message:
_block_invoke_0220 [Line 243] ERROR: Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.1.1” which could put your confidential information at risk." UserInfo=0x9792ad0 {NSErrorFailingURLStringKey=https://192.168.1.1/Info.live.htm, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSErrorFailingURLKey=https://192.168.1.1/Info.live.htm, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.1.1” which could put your confidential information at risk., NSUnderlyingError=0xa6a3560 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.1.1” which could put your confidential information at risk.", NSURLErrorFailingURLPeerTrustErrorKey=< SecTrustRef:
If I navigate to the url i safari, I get a message that Safari can't verify the identity.... and I have to click continue to carry on.
How can I achieve this? I don't really know anything about ssl or https unfortunately.
Here is the code i'm currently using:
NSString *urlString = #"https://192.168.1.1/";
NSURL *url = [NSURL URLWithString:urlString];
// Set authorization
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setAuthorizationHeaderWithUsername:user password:pass];
NSURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"Info.live.htm" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *responceString = [operation responseString];
// NSLog(#"%#",responceString);
if ([self parseInfoLive:responceString])
[[NSNotificationCenter defaultCenter] postNotificationName:#"downloadsComplete" object:nil];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"ERROR: %#",error.description);
}];
[operation start];
For getting around the validity check of the host certificate, add the following code.
First add an interface for the setter method that is already within the SDK but not exposed into public:
#interface NSURLRequest(Private)
+(void)setAllowsAnyHTTPSCertificate:(BOOL)inAllow forHost:(NSString *)inHost;
#end
Now, whenever you are rendering a new request, invoke that setter:
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[inURL host]];
Warning
Do not use this code for production but only while developing your app in cases where the certificate is not yet approved/submitted/installed. Typical would be the use of a development server that does not have a trusted certificate installed.
The use of this code will get your App rejected from distribution via iTunes as it uses a private API method.
For making sure that things work smoothly in a production environment, you will have to get a trusted SSL certificate for your host. There are various authoritative companies providing such thing. To mention at least one (there are MANY more), you could use GoDaddy.
Update (31st May 2013)
AFNetworking got updated to support invalid certificates out of the box, without using any private API's. Kudos to Peter Steinberger!
For enabling that feature, the most convenient solution is to add the following to your prefix header (.pch):
#ifdef DEBUG
#define _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_
#endif
Once again, I can not emphasize enough that you should refrain from enabling that feature in production code - you would pretty much invalidate the entire point of SSL connections and render them vulnerable.
This URL from Apple documentation might help Check this link
In the above document read Introduction section.
I am not familiar with AFNetworking, but there is a solution here that works around the error you are seeing. Till's answer is reputed to keep you from being able to submit your app to the app store.

Resources