Performing SSL request in iOS - ios

I'm trying to use the Google Translate API for a simple iOS app I'm building. I'm essentially stuck in trying to use SSL for this request.
According to Google Translate API, it supports SSL. So now my question is how do I even do this? Here's some code that I'm trying right now. I have an API from the translate API.
func translateRequest(text: String, fromLang: String, toLang: String) {
let httpsURL = NSURL(string: "https://www.googleapis.com/language/translate/v2?key=<MYAPIKEYHERE>&source=\(fromLang)&target=\(toLang)&q=\(text)&format=text")
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
sessionConfiguration.timeoutIntervalForRequest = NSTimeInterval(10)
let session = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
let translateTask = session.dataTaskWithURL(httpsURL!, completionHandler: completionHandlerTranslate)
translateTask.resume()
}
func completionHandlerTranslate(dataOpt: NSData?, responseOpt: NSURLResponse?, errorOpt: NSError?) {
// A helper function to do something with responses
}
Now since I'm using NSURLSession (instead of NSURLConnection), I'm also having my controller by the NSURLSessionDelegate. According to Apple's docs (in the Configuring Authentication section), for NSURLSession I only need to implement didReceiveChallenge function. The signature of the function is:
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
}
But I have no idea what should go in there. I've seen a lot of examples of folks trying to verify the server etc, but I'm still confused.
Here are my questions:
SSL is used to encrypt data, so what do I need to do in didReceiveChallenge for encrypted communication to happen between client and server?
How can I make sure that didReceiveChallenge rejects all other certificates (like placing a proxy like CharlesProxy and trying to sniff the data)?

It usually isn't a good idea to prevent use of Charles Proxy, because it isn't practical for someone to use it against someone else. It only works because the user has explicitly installed certificates to tell the OS to trust the fake certs that Charles Proxy produces. Users generally like the ability to see what data your app is sending on their own devices, and preventing them from doing so will probably create the impression that your app is doing something shady.
With that said, if you really want to prevent HTTPS proxies from working, the way you would do that is with key pinning. Instead of trusting the certificate, you write a didReceiveChallenge handler that requests default handling for everything except for server trust. Then, for server trust:
Extract the proposed credential from the challenge object.
Extract the first (leaf) certificate from the credential's certificate chain; IIRC, this is at index 0, but don't quote me on that.
Extract the public key from the certificate.
Add a copy of your server's public key in your app bundle somewhere.
Compare the two public keys. If they match, return/call the callback in such a way that tells it to use the provided certificate.
If they don't match, cancel the entire connection/task.
That's the basic idea, anyway. With that change, the only way to sniff the traffic is to replace the public key in your app bundle and re-sign the app.

Related

What's the difference between implementing func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge

In a background session, for an app<>server communication using https, what is the difference between a fairly relaxed implementation of the following method, or none at all?
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(.useCredential, challenge.proposedCredential)
}
In both cases the app works, but I've read about each invocation of this method incrementing the Resume Rate Limit of the app (which is supposedly bad)
So yeah, after I realized that the app just works without any implementation of this method, I started asking myself what is the reason for possibly implementing it? Authorization is done via 401 and a subsequent login.
I think some useful information could be found from documentation Performing Manual Server Trust Authentication:
To perform manual server trust authentication, implement the
URLSessionDelegate method urlSession(_:didReceive:completionHandler:).
Following with:
In most cases, you should let the URL Loading System’s default
handling evaluate the server trust. You get this behavior when you
either don’t have a delegate or don’t handle authentication
challenges.
And to explain why you might need to do a manual server trust authentication, thus by implementing the URLSessionDelegate method urlSession(_:didReceive:completionHandler:):
However, performing your own evaluation may be useful for scenarios
like the following:
You want to accept server credentials that would otherwise be rejected
by the system. For example, your app makes a secure connection to a
development server that uses a self-signed certificate, which would
ordinarily not match anything in the system’s trust store.
You want to reject credentials that would otherwise be accepted by the
system. For example, you want to “pin” your app to a set of specific
keys or certificates under your control, rather than accept any valid
credential.

How to handle asynchronous network requests in SiriKit intents

I have an iOS weather app and am looking to add support for SiriKit Intents.
The basic intent I've set up works correctly with dummy data, but where I'm struggling is with getting my main app / framework to request the forecast from my server and hand it back to the shortcut so I can provide a response in the completion handler:
func handle(intent: GetUVIndexIntent, completion: #escaping (GetUVIndexIntentResponse) -> Void) {
//TODO: Get the main app to request weather data and hand the result back to Siri.
completion(GetUVIndexIntentResponse.success(uvIndex: NSNumber(value: 1), burnTimeEstimate: "I'll fix this later! "))
}
In my main app I handle this by firing off the server request from my model, awaiting a response and using NotificationCenter to communicate the outcome to the View Controller so it can update itself. This sort of pattern doesn't seem like it is suitable for use with Siri Intents though. Are there any alternative patterns/approaches I could use here?
Thank you!

Iphone App Rejected Ipv6 Incompatibility

My application has been rejected twice for Incompatibility over Ipv6 network.
This question has been asked several times. Most of the solutions suggested to avoid using third party api for http request and do not hard code ipaddress. I have not done neither.
However does my server have to be ipv6 compatible or is there something wrong with my http request.
So I tested my server https://services.fingrowthbank.com/ on this website http://ipv6-test.com/validate.php for Ipv6 readiness testing. It shows as incompatible. Could this be a reason for the rejection?
Code for http request
let nsUrlObject: NSURL = NSURL(string: urlAndMethod)!
let nsMutableUrlRequestObj: NSMutableURLRequest = NSMutableURLRequest(URL: nsUrlObject, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: nstime_out_intervalObj)
nsMutableUrlRequestObj.HTTPMethod = “POST"
let bodyData = httpBodyStr
nsMutableUrlRequestObj.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
nsMutableUrlRequestObj.setValue(httpHeaderStr, forHTTPHeaderField: "Authorization")
NSURLConnection.sendAsynchronousRequest(nsMutableUrlRequestObj, queue: NSOperationQueue.mainQueue())
{
(response, data, error) in
if response == nil
{
print(error)
alert.view.removeFromSuperview()
Imps_http_client.showAlertView("Could not connect to the server")
}else{
}
}
There is no problem with your code. The problem is your server side.
As per https://services.fingrowthbank.com/ your server is not compatibility with IPV6. Because I found that you are using old security encryption i.e, Obsolete Cipher suite .
I under lined inside the image.
I tested with sample server. Example: Apple. This apple is using modern cipher suite
The problem with your server there is a chance of security attack number 13. This is the reason the apple is rejected your app. Update your app with late security.
Solution: Just update your server to modern cipher suite.
This url is perfectly fine as far as your are using domain name for sending request to server.
according to apple If you’re writing a client-side app using high-level networking APIs such as NSURLSession and the CFNetwork frameworks and you connect by name, you should not need to change anything for your app to work with IPv6 addresses. If you aren’t connecting by name, you probably should be.
check your code or any third party components you are using and make sure your are not sending any request with IP address instead of domain name. Specifically, Check Reacahbility code. If you are using it, don't use function reachabilityWithAddress , use reachabilityWithHostName instead. reachabilityForInternetConnection is also using reachabilityWithAddress - If you are using this, then edit it.
At last, you can validate your app for ipv6 by creating ipv6 network with mac and testing app with that network.

Can you broadcast data to nearby devices without requesting permissions from the devices and the user broadcasting?

I am trying to make an app as a hobby and I need to know, is it possible to broadcast to all nearby devices without requesting each and every device's permission?
If it is possible to ask the user to accept ALL incoming connections and send connection requests to devices without making the user choose which device it would be great for me.
I found that you can connect up to 7 peers with multipeer but the user have to choose every device he wants to connect with.
Any option of nearby broadcasting would help me here,
Thanks for all the help! :)
Accepting invitation is easy, instead of prompting the advertiser to accept, simply set the invitationHandler to true.
For example, in my implementation, I have an MPManager class:
func advertiser(advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: NSData?, invitationHandler: (Bool, MCSession) -> Void) {
self.invitationHandler = invitationHandler
delegate?.invitationWasReceived(peerID.displayName)
}
And the delegate method:
func invitationWasReceived(fromPeer: String) {
self.appDelegate.cManager!.invitationHandler(true, self.appDelegate.cManager!.session)
self.connectingLabel.text = "Connecting"
}
For the browser side, you can similarly automatically send the invitations to any peer it finds, the question is when to stop searching for peers and continue with what you want to do once they are all connected.
Have you checked PeerKit? I bills itself as exactly that, a zero-config approach. I briefly checked it for what I was doing, but didn't quite match my needs. Maybe it matches yours.

Make http call on iOS while offline

Perhaps I have been reading the wrong stuff, but one thing that all of the literatures that I have been reading seem to agree on is that: iOS does not allow background threads to run for longer than ten minutes. That seems to violate one of the greatest principles of app development: the internet should be invisible to your users. So here is a scenario.
A user is going through a tunnel or flying on an airplane, which causes no or unreliable network. At that instant, the user pulls out my email app, composes an email, and hits the send button.
Question: How do I the developer make sure that the email is sent when network becomes available? Of course I am using email as a general example, but in reality I am dealing with a very much simple http situation where my app needs to send a POST to my server.
Side Note: on android, I use Path’s priority job queue, which allows me to set it and forget it (i.e. as soon as there is network it sends my email).
another Side Note: I have been trying to use NSOperationQueue with AFNetworking, but does not do it.
What you want to achieve can be done using a background NSURLSession. While AFNetworking is based on NSURLSession I’m not quite sure if it can be used with a background session that runs while your app doesn’t. But you don’t really need this, NSURLSession is quite easy to use as is.
As a first step you need to create a session configuration for the background session:
let config = URLSessionConfiguration.background(withIdentifier: "de.5sw.test")
config.isDiscretionary = true
config.waitsForConnectivity = true
The isDiscretionary property allows the system to decide when to perform the data transfer. waitsForConnectivity (available since iOS 11) makes the system wait if there is no internet connection instead of failing immediately.
With that configuration object you can create your URL session. The important part is to specify a delegate as the closure-based callbacks get lost when the app is terminated.
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
To perform your upload you ask the session to create an upload task and then resume it. For the upload task you first create your URLRequest that specifies the URL and all needed headers. The actual data you want to upload needs to be written to a file. If you provide it as a Data or stream object it cannot be uploaded after your app terminates.
let task = session.uploadTask(with: request, fromFile: fileUrl)
task.resume()
To get notified of success or failure of your upload you need to implement the URLSessionDataDelegate method urlSession(_:task:didCompleteWithError:). If error is nil the transfer was successful.
The final piece that is missing is to handle the events that happened while your app was not running. To do this you implement the method application(_:handleEventsForBackgroundURLSession:completionHandler:) in your app delegate. When the system decides that you need to handles some events for background transfers it launches your app in the background and calls this method.
In there you need first store the completion handler and then recreate your URLSession with the same configuration you used before. This then calls it’s delegate for the events you need to handle as usual. Once it is done with the events it calls the delegate method urlSessionDidFinishEvents(forBackgroundURLSession:). From there you need to call the completion handler that was passed to your app delegate.
The session configuration provides some more options:
timeoutIntervalForResource: How long the system should try to perform your upload. Default is 7 days.
sessionSendsLaunchEvents: If false the app will not be launched to handle events. They will be handled when the user opens the app manually. Defaults is true.
Here is a small sample project that shows how everything fits together: https://github.com/5sw/BackgroundUploadDemo
Your app needs to store the data internally and then you either need something which will cause the app to run in the background (but you shouldn't necessarily add something specially if you don't already have a reason to be doing it) or to wait until the user next brings the app to the foreground - then you can check for a network connection and make the call.
Note that e-mail is very different to a POST, because you can pass an e-mail off to the system mail app to send for you but you can't do exactly the same thing with a POST.
Consider looking also at NSURLSessionUploadTask if you can use it.
In three words: you don't.
And that's actually a good thing. I certainly do not want to have to think and speculate about my last 20 apps, if they are still running in the background, using memory and battery and bandwidth. Furthermore, they would be killed if more memory is needed. How would the user be able to predict if it completed its task successfully? He can't, and need to open the app anyhow to check.
As for the email example, I'd go with showing the email as "pending" (i.e. not sent), until it transferred correctly. Make it obvious to the user that he has to come back later to fulfill the job.
While every developer thinks that his app has an extremely good reason for backgrounding, reality is, for the user in 99% it's just a pain. Can you say "task manager"? ;-)
I wrote a pod that does pretty much this - https://cocoapods.org/pods/OfflineRequestManager. You'd have to do some work listening to delegate callbacks if you want to monitor whether the request is in a pending or completed/failed state, but we've been using it to ensure that requests go out in poor or no connectivity scenarios.
The simplest use case would look something like the following, though most actual cases (saving to disk, specific request data, etc.) will have a few more hoops to jump through:
import OfflineRequestManager
class SimpleRequest: OfflineRequest {
func perform(completion: #escaping (Error?) -> Void) {
doMyNetworkRequest(withCompletion: { response, error in
handleResponse(response)
completion(error)
})
}
}
///////
OfflineRequestManager.defaultManager(queueRequest: SimpleRequest())

Resources