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.
Related
I have an AFHTTPSessionManager created NSURLSessionDownloadTask being used to download a video within an app that sometimes when initialized with an http:80 url will convert it to https:443. This is happening before any connection attempt is being made (I added a custom HTTP protocol class via NSURLSessionConfiguration to the session in order to log when the connection is being made).
By the time the request makes it to the
-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
delegate method of my CustomHTTPProtocolDelegate class, the request has been changed to https.
App Transport Security is disabled (NSAllowsArbitraryLoads=true) and this behavior seems to be associated with a particular http-only server (other http-only server have no issue, and the connection is made as http on port 80).
Any idea of what could be going on? Anything else I could do to debug?
Here is how the download task is being created (including the debug custom protocol class):
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
config.protocolClasses = #[[CustomHTTPProtocol class]];
AFHTTPSessionManager *session = [[AFHTTPSessionManager manager] initWithSessionConfiguration:config];
self.downloadTask = [session downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response){
...
[UPDATE]
This issue is causing us a number of headaches, so to help facilitate troubleshooting, I created a small test project to help narrow in on the problem. My simple project does two things, loads a url into a UIWebView and downloads a file using NSURLSessionDownloadTask. The urls for these actions follow this pattern:
WebView URL: https://console.company.com/home.html
Download URL: http://data.company.com/file.txt
And those hostnames resolve to different IPs (different servers).
If I download the file before navigating the webview, then everything is fine, but if the webview loads its URL first, then the download URL will be switched to HTTPS automatically and the initial request for data will fail. One thought we had was that once iOS opens a TLS tunnel for the HTTPS connection that the webview is creating, that it tries to use that same tunnel for all subsequent *.company.com connections. Or at the very least, it assumes all *.company.com connections must also be TLS.
Figured it out. Both servers were sending a HSTS header for all subdomains. Because the networking layer under NSURLSession observes this header, the calls to the HTTP server were being upgraded to HTTPS prior to leaving the client.
I have been struggling with an issue where NSURLConnection calls instantly fail. The device needs to be rebooted entirely or Flight Mode needs to be toggled on/off to resolve the problem. Restarting the app (swipe up) alone does not help.
Some facts:
-All URLs are HTTPS, TLS 1.2 compatible with Forward Secrecy. There are no issues with ATS and iOS 9. The error has been present since iOS 7 and remains on 9.2.
-No third party frameworks are used by the app. I use only native NSURLConnection calls that always work, except for when this odd situation occurs.
-No infrastructure/network issues - other devices on same networks (same WiFi for instance) work in the same app at the same time. Going to/from 3G/Wifi makes no difference.
-I always implement willCacheResponse to return nil.
-The service is hosted on AWS Elastic Beanstalk, so some suggested that it might be a DNS caching issue in case of IP address changes - this seems unlikely to me and should trigger multiple errors at once on different devices, which I have never seen.
-The method called is didFailWithError, instantaneously, as if there were no Internet connection on the device at all - all other apps work, however.
-The website that hosts the API used by the app can be browsed with no problems at all times. The website actually makes the same requests to fetch data.
The error code returned is -1003, kCFURLErrorCannotFindHost. I've been following a thread on Git dealing with the same issue to no avail. https://github.com/AFNetworking/AFNetworking/issues/967
I tried using NSURLRequestReloadIgnoringCacheData for all my requests, but that did not help.
With this information, will anyone care to venture a guess what I might be doing wrong? I added the bounty because I have no idea how to approach this problem - especially because it's so inconsistent. And it is definitely not a legitimate error (that is, that the domain could not be found), as the service is operating fine while this happens on random clients.
I create my request with a static method that looks like this. It's been stripped of some non-public info, but basically it just performs a POST request with JSON data. [Controller getSQLHost] just returns a URL - the base domain.
+(NSURLConnection*)initiatePOSTwithJSONDictionary:(NSDictionary*)dictionary toURL:(NSString*)urllocation withDelegate:delegate {
NSMutableDictionary *connectionDictionary = [[NSMutableDictionary alloc] init];
if (dictionary) {
[connectionDictionary setObject:dictionary forKey:#"input"];
}
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:connectionDictionary options:kNilOptions error:nil];
NSURL *url = [NSURL URLWithString:[[Controller getSQLHost] stringByAppendingString:urllocation]];
NSString *postLength = [NSString stringWithFormat:#"%i", (int)[jsonData length]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:jsonData];
return [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
}
Does you delegate implement connectionShouldUseCredentialStorage ? (or respond with YES)
I think the device's keychain is used when this method returns yes which may explain the persisting failure beyond the life time of the running application and why rebooting or otherwise resetting network connectivity "fixes" it. If an authentication failure has been recognized once, it may linger in the key chain for a little while which would then respond immediately without actually going to the server.
What would cause the authentication to register as a failure in the keychain in the first place may depend on a variety of factors. It could be as simple as a typo in the saved password or more convoluted such as some certificate expiration preventing the SSL layer from establishing a secure link.
You're creating NSURLConnections on the current runloop. How many are active at any one time? Have you considered using an NSOperationQueue so that you don't get bitten by load bugs?
Is your delegate thread-safe? If not, that could explain the sporadic-ness of the bug.
You say you don't see the problem often, but others do. Can you borrow their devices and maybe even them and their usage patterns and thus get to see the problem more often?
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.
[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.
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.