I have a HTTP REST server with a self-signed certificate. I want to talk to this server from an iOS Swift app, using alamofire. The current code that I have is:
```
let Almgr : Alamofire.SessionManager = {
// Create the server trust policies
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"localhost": .disableEvaluation
]
// Create custom manager
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let man = Alamofire.SessionManager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
return man
}()
Almgr.request(url, method: .post, parameters: params).responseJSON {
response in
if response.result.isSuccess {
print("Success")
} else {
print("Failure")
}
}
With the above code snippet, I am always getting an error when I try to make the http call Almgr.request. The error message is:
2017-12-30 18:24:20.114486+0530 myApp[58036:2721102] ATS failed system trust
2017-12-30 18:24:20.114625+0530 myApp[58036:2721102] System Trust failed for [1:0x600000178a80]
2017-12-30 18:24:20.114814+0530 myApp[58036:2721102] TIC SSL Trust Error [1:0x600000178a80]: 3:0
2017-12-30 18:24:20.115142+0530 myApp[58036:2721102] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
2017-12-30 18:24:20.115274+0530 myApp[58036:2721102] Task <4E3D9E88-B9CE-48C4-850C-5A3E7C9A6A72>.<1> HTTP load failed (error code: -1200 [3:-9802])
2017-12-30 18:24:20.115469+0530 myApp[58036:2721231] Task <4E3D9E88-B9CE-48C4-850C-5A3E7C9A6A72>.<1> finished with error - code: -1200
Any idea how to get this fixed ? I do not want any checks to be done if the url is localhost on port 8000. I have even tried with adding port to the serverTrustPolicies definition but that does not make any difference and I still get error. Any help ?
Update: My problem I believe is related to https://developer.apple.com/library/content/qa/qa1948/_index.html but not found the way to fix yet.
1.
Your approach of modifying server trust policies should work when providing the port. Also see this post. Maybe you are testing your app with the simulator and trying to connect to a web server on the same machine? This can cause all kinds of connection problems (or why are you trying to connect to localhost anyway?).
2.
You should never set NSAllowsLocalNetworking or similar parameters. It breaks SSL and you never know what may happen, even in the local network. If absolutely necessary, you should just make exceptions for single hosts and ports as stated above.
3.
You should never use self signed certificates because this also breaks SSL. It is very easy to obtain a valid certificate using Let's Encrypt. Though, in some cases it is just not possible to obtain a valid certificate. You should then create your own certificate authority and export the CA root certificate to your device. This way, you also make an exception for only one specific host.
Please note that security is crucial in all applications. Please only make exceptions if you exactly know what you are doing.
I have figured out the solution. We need to edit the Info.plist file and add the following section:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
to let iOS allow local networking without https errors.
I was facing same problems. What I figured out and what works:
let almgr:Alamofire.SessionManager = {
//getcertificates is my own method where i create certificate with data from my .cer file
let certificates = getCertificates()
let trustPolicy = ServerTrustPolicy.pinCertificates(certificates: certificates, validateCertificateChain: true, validateHost: true)
// Here was the problem. I had to modify that dict: (localhost with port and with .disableEvaluation)
let serverTrustPolicies = ["liper:8000":trustPolicy, "liper":.disableEvaluation]
let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let man = Alamofire.SessionManager(configuration: URLSessionConfiguration.default, serverTrustPolicyManager: serverTrustPolicyManager)
return man
}()
Related
I have a static function which I use to retrieve a configuration file through AFNetworking library, as follows:
static func getConfiguration(success: NetworkServiceSuccessBlock, failure: NetworkServiceFailureBlock) -> AFHTTPSessionManager? {
let sessionManager = AFHTTPSessionManager(sessionConfiguration: NSURLSessionConfiguration.defaultSessionConfiguration())
sessionManager.requestSerializer.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
sessionManager.GET(getConfigurationUrl()!, parameters: nil, progress: nil, success: success, failure: failure)
return sessionManager
}
I need to check the server SSL certificate domain to be the proper one, something like challenge.protectionSpace.host for NSURLSession, and I need the check to determine whether the GET request will fail or not.
EDIT: I don't want to perform SSL Pinning with certificates stored in the app bundle, it is enough for me to verify the server certificate domain is correct.
Can someone point me in the right direction to perform this?
I apologize in advance if i made a duplicate of this question. I'm working on a mobile app that communicates with an API on a .local domain. I'm stuck at trying to establish a secure connection (using Alamofire). I Tried to write a custom ServerTrustPolicy just as Alamofire wiki suggests, and include my certificate chain, but it always fails. The only way to get the app to connect is by configuring App Transport Security in .plist file and adding the domain as exception. Does anyone have experiance with using certificates trough alamofire with .local domains? Would really appreciate any advice. Thank you in advance.
This is my Alamofire.Manger:
static let sharedInstance: Manager = {
var apiObject = ApiBaseService()
//let domainName = apiObject.getDomain()
let pathToCert = NSBundle.mainBundle().pathForResource("certca", ofType: "der")
let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)!
let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
certificates: ServerTrustPolicy.certificatesInBundle(),
validateCertificateChain: true,
validateHost: true
)
let value = GlobalConstants.APICallErrorLocations.actionsGetActions
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"test.ex01.local": serverTrustPolicy
]
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
configuration.timeoutIntervalForRequest = NSTimeInterval(12.0)
return Manager(configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}()
Am I doing something wrong?
Kristian
EDIT:
The thing was TLS version was v1.0 which i discovered with this command:
openssl s_client -connect test.ex01.local:5000
After updating TLS version and including certificate chain in ServerTrustPolicy object, everything works.
Hope this will help someone in the future.
If your certificate chain is not valid and is self-signed (assuming this is the case), then you MUST disable ATS for that host, or you'll never be able to use the Alamofire cert pinning logic. ATS will never even give Alamofire a chance to evaluate the cert chain.
The reason for this behavior is that ATS first evaluates the connection challenge before giving the NSURLSessionDelegate a chance to evaluate the challenge. If ATS evaluates the properties of the challenge and determines it should not trust the connection, it stops there and the request will not succeed. It doesn't call the NSURLSessionDelegate to give you a second chance to override it.
However, when you disable ATS, the first ATS checks no longer happen, and the challenge is sent to the NSURLSessionDelegate for evaluation which is where Alamofire kicks in.
AppTransportSecurity is used to list connections that you want that are not HTTPS. iOS blocks non HTTPS connections that are not in this list!
I am developing an iOS app which required to connect with web socket server.
I can successfully connect to server but when I send request on it, it drop off the connection.
I am using Starscream library for web socket.
As per server support team:
it does not support protocol compression, but in the headers below they're requesting "permessage-deflate" from us. We'll accept uncompressed messages just fine (it's just a flag on the packet) but due to the extension they asked for, messages we send out will be compressed with that flag set.
I send request as following using Swift
let dict = ["Parameter1":"value1","Parameter2":"value2"]
do {
let data = try NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(rawValue: 0))
var jsonString: String = String(data: data, encoding: UInt())!
self.socket.writeString(jsonString);
} catch {
print(error)
}
It disconnect with server and print following message.
"websocket is disconnected: Optional("masked and rsv data is not currently supported")"
What the server support team meant is that the request from your WebSocket client application contained an HTTP header like below.
Sec-WebSocket-Extensions: permessage-deflate
So, if your application has a line like below, remove it.
socket.headers["Sec-WebSocket-Extensions"] = "permessage-deflate"
This error might also be thrown if the server doesn't accept the incoming connection (regardless of the reasons), or if the server crashed.
Basically, when this message shows up, the best action would be to check what is going on the server as you might be wasting time trying improve client code (it happened to me :)
For those facing this issue when trying to connect to the backend WebSocket, make sure the front end and the backend version of the socket.io are compatible. Running the following command fixed the issue for me.
pod update
Updated the both to the latest and solved the issue.
this will fix your issue I believe. just add "wamp" in the header like this.
*
var request = URLRequest(url: URL(string: URL)!)
request.setValue(["wamp"].joined(separator: ","), forHTTPHeaderField: "Sec-WebSocket-Protocol")
socket = WebSocket(request: request)
socket.delegate = self
socket.connect()
I am trying to get my app to connect to an http server at 152.111.198.244 through the Apple Transport Security. And nothing I'm trying works.
Adding the NSAllowsArbitraryLoads key to the info.plist file of my project still did not allow my app to connect to this specific ip address 152.111.198.244
I have gone through the technote on Apple Transport Security. I installed OSX 10.11 to try and find what settings might work for the URL using
nscurl --ats-diagnostics http://152.111.198.244
and
nscurl --ats-diagnostics http://152.111.198.244/publications/
in the terminal. All settings that nscurl tries fail. I have looked at similar questions NSAllowsArbitraryLoads not working and NSExceptionAllowsInsecureHTTPLoads not working for ip addresses and have not found the solution. I also looked here and the NSExceptionMinimumTLSVersion key proposed there doesn't work.
I am starting to think that there may be a bug somewhere, or something that I missed. Are IP addresses an issue with Apple Transport Security? Why? Is there way to make an IP address work through ATS?
UPDATE: I added in the specific ip address that is giving me trouble. Hopefully someone will be able to replicate what I'm talking about.
UPDATE: I logged this as a bug in radar and I got a message that says it's a duplicate. In the meantime, the IP address mentioned in this question has a domain name now http://3d.media24.com/ but unfortunately it has not solved the problem.
UPDATE: I marked an answer as correct. It seems that apple fixed this with XCode7.1 and that the keys for Apple Transport Security have changed a bit. The site in question has also been updated for https connections.
I was able to access that URL, http://152.111.198.244, using ‘Allow Arbitrary Loads’ in my Info.plist under Xcode 7.1 and Simulator 9.1:
App Transport Security Settings: Dictionary
Allow Arbitrary Loads: Boolean = YES
Screenshot:
I used the following code:
let url = NSURL(string: "http://152.111.198.244")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) -> Void in
print("response \(response!)")
}
task.resume()
Here is the response that I received:
response <NSHTTPURLResponse: 0x7fe1a2421f80> { URL: http://152.111.198.244/auth/login } { status code: 200, headers {
"Cache-Control" = "no-cache";
Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 1138;
"Content-Type" = "text/html; charset=UTF-8";
Date = "Fri, 23 Oct 2015 09:33:59 GMT";
"Keep-Alive" = "timeout=5, max=98";
Server = "Apache/2.4.7 (Ubuntu)";
"Set-Cookie" = "XSRF-TOKEN=eyJpdiI6IldBOWYxcDk3SEtMekJ3YTNSUm9mYUE9PSIsInZhbHVlIjoiTFBcL3RGWW10cjlONFFkeXY1ZDA4SWRkSURIYlFsOGE3QkFEV3hRNTVwRFJuWSt5SXN3OU55Sng4elduMHd1T1duV0VFQ1o4dDVjeDJTZGRFeXJxMjN3PT0iLCJtYWMiOiJiZjNmOTg0NTZmY2RkMGQzNmE2YWEyNjJiNzA1MDlmZjIwM2M3NWYyNjYwZjM5N2Q3ZTgxNjRjNzAzMGYzYmMzIn0%3D; expires=Fri, 23-Oct-2015 11:33:59 GMT; Max-Age=7200; path=/, laravel_session=eyJpdiI6InR5OSs3cmpObVRBbFhORnVJQjRvWFE9PSIsInZhbHVlIjoiSTJ2bk41RVVLZUR1a0xKbFwvalZXQWpsNEtWeHppUVpYVUlRM1ZjQXc5aDJxT1wvXC9uYkViaTQ0SCtGNTMrdmtiQXFOd0VJTFwvM0ZCbmFHZk5MWlwvZ3BBUT09IiwibWFjIjoiYjRmNzcxY2Q5NDFlZjYzZTI1YzU2YzI0YTkxM2M0NDg0MGY2YThiODIxOGZjOTgxYjNmM2FlZTkzZGMyZTdjOCJ9; expires=Fri, 23-Oct-2015 11:33:59 GMT; Max-Age=7200; path=/; httponly";
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.5.9-1ubuntu4.11";
} }
I was also able to connect using the named domain 3d.media24.com but not the numeric address of 152.111.198.244 using exception domains.
Screenshot:
My results agree with the definition for exception domains in the Apple Technote on App Transport Security:
A dictionary of exceptions for the named domain. The name of the key is the name of the domain–for example, www.apple.com.
Some helpful fields can be added below App Transport Security Settings:
NSIncludesSubdomains
NSExceptionAllowInsecureHTTPLoads
NSExceptionRequiresForwardSecrecy
NSExceptionMinimumTLSVersion
NSThirdPartyExceptionAllowsInsecureHTTPLoads
NSThirdPartyExceptionMinimumTLSVersion
NSThirdPartyExceptionRequiresForwardSecrecy
Visit https://github.com/ChenYilong/iOS9AdaptationTips#1-demo1_ios9%E7%BD%91%E7%BB%9C%E9%80%82%E9%85%8D_ats%E6%94%B9%E7%94%A8%E6%9B%B4%E5%AE%89%E5%85%A8%E7%9A%84https for more usage.
Hope helpful for you!
Unfortunately this morning my XCode updated to version 7 and the iOS app I was developing with http now wants https. So, following many tutorials, I configured my MAMP server in order to use https/ssl creating a dummy certificate. Now in my iOS app URLs are like the following:
static var webServerLoginURL = "https://localhost:443/excogitoweb/mobile/loginM.php"
static var webServerGetUserTasks = "https://localhost:443/excogitoweb/mobile/handleTasks.php"
static var webServerGetUsers = "https://localhost:443/excogitoweb/mobile/handleUsers.php"
static var webServerGetProjects = "https://localhost:443/excogitoweb/mobile/handleProjects.php"
and they work fine if I try to access them in my browser.
I was used to access the database and php files with NSURLSession.sharedSession().dataTaskWithRequest() which now raises the error in title. For example, here's the line where error is raised:
if let responseJSON: [[String: String]] = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())) as? [[String: String]] {
...
}
and this is the complete error message:
2015-09-21 16:41:48.354 ExcogitoWeb[75200:476213] CFNetwork SSLHandshake failed (-9824)
2015-09-21 16:41:48.355 ExcogitoWeb[75200:476213] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
fatal error: unexpectedly found nil while unwrapping an Optional value
I would like to know how to fix this. I've read some useful answers here but there are many things I still don't understand and if anyone would help/explain me I'd be very grateful.
Add this to your app's Info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
fatal error: unexpectedly found nil while unwrapping an Optional value
usually means that you're doing something not so good, and by looking at your if
if let responseJSON: [[String: String]] = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())) as? [[String: String]] {
I can see that there's a data!, but that data object is nil. You really should unwrap the optionals before using them, especially when working with remote data.
Then you have a network error, that's probably related to the ATS Apple added in iOS 9.
See another answer on how to temporarily disable ATS.
https://stackoverflow.com/a/30748166/421755
edit: I see now that you added ssl to your localhost, that's good. However it's not enough for ATS to work, since it needs TLS 1.2 and not self-signed certificates.
Really old question, but I thought I would respond. This usually happens when you try to unwrap an http object and it comes back nil.
Make sure that you unwrap your url and give it a default value.
example :
let jsonURL = "your https link to your json"
guard let url = URL(string: jsonURL) else { return } // dont force unwrap
hope this helps!