HTTPS NSURLConnection not working - ios

I have a website that has traffic on both HTTP and HTTPS.
I develop an app (iOS 9) that call some of these URLs, but when I changed from HTTP to HTTPS I got the following error:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
I have already disabled the ATS, because I have a facebook integration and it was complaining.
Here is my piece of code that makes the call, and handles the result:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://myRestWebService?test=Y"]];
NSMutableURLRequest *request = [ NSMutableURLRequest requestWithURL:url];
NSError *error;
NSURLResponse *response;
NSData *myUrlData = [ NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSString *result = [[NSString alloc] initWithData:myUrlData encoding:NSUTF8StringEncoding];
As you can see, it is a synchronous call.
The certificate is signed by Go Daddy, so it is trusted, and works well on a browser.
Any advice would be appreciated.

I talked to Apple and turns out my apache had a problem.
Their answer:
Configure your server to return its intermediate certificate in the
TLS handshake. If you’re using Apache, you should look at the
SSLCertificateChainFile directive.
So I set the intermediate certificate and everything start working.

Related

NSURLSessionDownloadTask switching from http to https

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.

ios FTP upload using NSURLSession

I am trying to upload files via FTP to server. According to
Apple Documentation NSURLSession class supports FTP operations.
There is a famous Apple Developer blog which also supports that. But still its not clear whether NSURLSession API's supports ftp upload or not? (I tried with the way suggested and getting error).
With the conventional way of CFFTPStreamRef, ftp upload works fine but it's deprecated in 9.0. The header says: CF_DEPRECATED(10_3, 10_11, 2_0, 9_0 , "Use NSURLSessionAPI for ftp requests")
Any idea, example or link to get help with. I am trying something like this for now:
NSURL *url_upload = [NSURL URLWithString:#"ftp://username:password#thelink/myfolder/filename.zip"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url_upload];
[request setHTTPMethod:#"PUT"];
NSURL *docsDirURL = [NSURL fileURLWithPath:filePath];
NSURLProtectionSpace * protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:url_upload.host port:[url_upload.port integerValue] protocol:url_upload.scheme realm:nil authenticationMethod:nil];
NSURLCredential *cred = [NSURLCredential
credentialWithUser:userId
password:password
persistence:NSURLCredentialPersistenceForSession];
NSURLCredentialStorage * cred_storage ;
[cred_storage setCredential:cred forProtectionSpace:protectionSpace];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.URLCredentialStorage = cred_storage;
sessionConfig.timeoutIntervalForRequest = 30.0;
sessionConfig.timeoutIntervalForResource = 60.0;
sessionConfig.allowsCellularAccess = YES;
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [upLoadSession uploadTaskWithRequest:request fromFile:docsDirURL];
[uploadTask resume];
To the best of my recollection, NSURLSession (and NSURLConnection) support retrieving files via FTP, but do not support any other FTP commands, such as STOR (note that PUT is an HTTP method, not an FTP method).
For your purposes, your options are to either use the CFFTPStream API (which only barely works) or stop using FTP.
My strong recommendation would be to stop using FTP. The FTP protocol is hopelessly insecure, sending the username and password in cleartext form over the wire, which makes it really easy for people to sniff the credentials and masquerade as the user. So the only situation where an FTP upload would be even remotely acceptable these days is an anonymous FTP upload to a shared dropbox, and even then, it is somewhat dubious. That's why the functionality was never added to the NSURLConnection API, much less NSURLSession.
There are much better alternatives that are much more secure, such as WebDAV over HTTPS, POST request uploads over HTTPS, WebDAV or POST requests with digest authentication, etc. And those alternatives are actually supportable with NSURLSession and provide other advantages like the ability to resume downloads. Unless you have absolutely no way to change the server side, please use one of those alternatives instead.

Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found

I get this error when I try running the app on a device. I do not get this error when I run it on the simulator and the post request works fine. This is code snippet I have:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:300.f];
[request setHTTPMethod:#"POST"];
NSData* data = [paramString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:data];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
Any help is appreciated. Thanks
With Xcode 11.2.1, working on a macOS project, I had the same issue.
The fix here was:
Click on project in project explorer
Select the Signing & Capabilities tab
Check "Network: Outgoing connections (client)" checkbox in the AppSandbox section
This error would typically suggest a DNS related issue. Check the DNS settings on your device and confirm you can browse the internet using Safari or another browser (double check by visiting a site you have not visited before to ensure it is not cached).
If there is a url you can GET (instead of POST) on the same server, try visiting it directly in Safari on the device.
The simulator uses your computers network connection, so I recommend checking the System Preferences > Network > Advanced > Proxies > Automatic Proxy Configuration : You must disable Automatic conf.

iOS: SocketRocket - How to implement SSL Handshake

just switched to Websockets in combination with Protobufs. Works like a charm on IOS but I am not sure how to implement SSL Handshake (like with NSURLConnection) via SocketRocket Lib. Has someone experience with that or is it just not yet supported.
TSL connection is already working and SSL pinning would also work - but how to implement the correct SSL handshake by validating the SSL chain correctly with web sockets via SocketRocket?!
BR
EDIT: Correcting error in my previous answer.
CFStream which is what Socket Rocket uses in the background will handle the handshake automatically assuming the certificate has been added to the keychain. If you need to add a certificate, see answer to this question: iOS: Pre install SSL certificate in keychain - programmatically
If however, Pinning is what you are looking for, this is straightforward to do with Socket Rocket. Use the initWithURLRequest initializer and everything else is handled automatically. For pinned certificates, SocketRocket does not validate the certificate chain which is the behavior you want, because with pinning you are specifically saying trust this certificate or certificate signed by this certificate only. i.e. it does not rely on validating a chain.
NSURL *url = [NSURL URLWithString: ServerSocketURLString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSString *cerPath = [[NSBundle mainBundle] pathForResource:#"certificatefilename" ofType:#"cer"];
NSData *certData = [[NSData alloc] initWithContentsOfFile:cerPath];
CFDataRef certDataRef = (__bridge CFDataRef)certData;
SecCertificateRef certRef = SecCertificateCreateWithData(NULL, certDataRef);
id certificate = (__bridge id)certRef;
[request setSR_SSLPinnedCertificates:#[certificate]];
self.clientWebSocket = [[SRWebSocket alloc] initWithURLRequest:request];
self.clientWebSocket.delegate = self;

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