SPDY for iOS with AFNetworking - ios

I'm trying SPDY for iOS app, which currently uses AFNetworking (2.1.0) to handle HTTP requests. Server side, I'm using Google App Engine (Checked with SPDYCheck) it's SPDY-friendly.
Here's how I integrated SPDY in my code.
I'm using AFHTTPRequestOperationManager
#interface MyClient : AFHTTPRequestOperationManager
And I embedded SPDY in initWithBaseURL:
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
// SPDY Config
NSString *spdyURL = [NSString stringWithFormat:#"%#://%#:443",url.scheme, url.host];
[SPDYURLConnectionProtocol registerOrigin:spdyURL];
...
}
Note that my url is with format https://myapp.appspot.com/_ah/ So I reformat it when passing to SPDY registerOrigin: spdyURL will look like https://myapp.appspot.com:443
I suppose that's all I need? But I wasn't able to send request after adding the SPDY code. Debug message showed the following error:
error response from api/users/v1/is_token_valid :
Error Domain=NSURLErrorDomain
Code=-1001
"The request timed out."
UserInfo=0xdc2c250 {NSErrorFailingURLStringKey=https://myapp.appspot.com/_ah/api/users/v1/is_token_valid?
user_token=[a_token],
NSErrorFailingURLKey=https://myapp.appspot.com/_ah/api/users/v1/is_token_valid?
user_token=[a_token],
NSLocalizedDescription=The request timed out., NSUnderlyingError=0xf270e60 "The request timed out."}
I have no clue at all. Hope someone with experience with SPDY on iOS could help!!

It is true that Apple does not support NPN in default TLS implementation. However there are some SPDY libs using OpenSSL (which supports NPN).
Here is one on them:
https://github.com/locationlabs/SPDY-for-iPhone

Unfortunately Apple's TLS implementation doesn't support NPN which is used by Google App Engine. You can read more about it in the CocoaSPDY GitHub README.
You can't use CocoaSPDY with Google App Engine at this time.

Is it ok for you?
I see that SPDY protocol support is now available in NSURLSession on OS X Yosemite and iOS 8 , and is Supported transparently by NSURLSession.
see details: https://developer.apple.com/videos/wwdc/2014/ [What's New in Foundation Networking]
But why it not work?
When i use CocoaSPDY, set manager.session.configuration.protocolClasses=#[[SPDYURLSessionProtocol class]]; it's ok;

Related

How to create an HTTPS server on IOS using cocoaHTTPServer?

I made a javascript cloud app that runs on a webpage in a webview on my iPad app that communicates via WebSocket connection but it only works when im on my http site and not https or else I get an CFNetwork SSLHandshake failed (-9806) error in Xcode and on the website it says time out during handshake.
Is this because the webserver on the iPad is running on HTTP instead of HTTPS?
JAVASCRIPT CLOUD APP
This part in the cloud is working for HTTP when connecting to the web server on the iPad.
var protocol = "ws";
if (this.useSecureConnection)
protocol = "wss";
var url = protocol+'://localhost:'+this.port+'/service';
this.connection = new WebSocket(url);
Xcode iOS iPad App (Objective-C)
I thought that was the issue so I tried to enable HTTPS but I am not sure what to create for the "sslIdentityAndCertificates" method.
- (BOOL)isSecureServer
{
HTTPLogTrace();
// Override me to create an https server...
return YES;
}
/**
* This method is expected to returns an array appropriate for use in kCFStreamSSLCertificates SSL Settings.
* It should be an array of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef.
**/
- (NSArray *)sslIdentityAndCertificates
{
HTTPLogTrace();
return nil;
}
Some of the other posts I have seen use APIs that are only available on Mac and not iOS.
I tried several combinations of ATS permissions as well. All resulted in HTTPS not allowing for WebSocket connection.
Any help is greatly appreciated! :)

NSURLRequest on iOS vs OS X

Is there any difference between NSURLRequest class on Mac OS and iOS?
Apparently there is one.
Here how the class description looks on Mac OS:
<NSURLRequest: 0x60000000f290> { URL: http://www.google.com/ }
And here how it looks on iOS:
<NSURLRequest http://www.google.com/>
As it appears there is a big difference for my project as it work perfectly on iOS and connection fails for Mac OS version.
Does anyone know how to make Mac OS app init the NSURLRequest class object exactly the way it does for iOS?
UPD 1
Same code for initialisation of the object for both platforms
NSURLRequest* aRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com/"]];
I tried same to use NSURLRequest in both AFNetworking and NSURLConnection getting same result - successful connection on iOS and 403 error for Mac OS
successful connection on iOS and 403 error for Mac OS
403 is HTTP's Forbidden response. The fact that you're getting a 403 means that a) your request is making a proper HTTP connection, but b) the server doesn't want to talk to you for some reason. A good strategy for diagnosing the issue is to use a proxy like Charles to look at the successful and unsuccessful requests and note the differences. The user agent parameter is one thing that's likely to differ between the two platforms, but you'll probably find other differences as well. Once you know what's different between the two requests, you'll have a better idea of what you need to change to make the failing requests work.

Supporting HTTPS in iOS XMPPFramework

I am developing an Chatting app for iOS platform. I am using XMPP framework downloaded from https://github.com/robbiehanson/XMPPFramework.
Initially server was not having certificates installed, it was a plain HTTP service. But now certificate authentication is required because now certificate is installed and need to support HTTPS protocol. I Tried just implementing following method,
- (void)xmppStream:(XMPPStream *)sender didReceiveTrust:(SecTrustRef)trust
completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler;
But now it is not connecting to that server or certificate validation is not happening.
Any idea apart from above delegate method do we need to set up any other thing ?
To connect to server i am using following method,
- (BOOL)connectWithTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
Regards,
Bhat
I resolved the above issue by setting the isSecure values as YES.
The following method was not publicly available for other classes in XMPPStream class.
-(void)setIsSecure:(BOOL)flag
By setting the above value am able to connect the port 5223 and SSL handshake works perfectly..
Regards,
Chandrika

Socket.io chat example handshake failed from iOS

I'm trying to create a native iOS client for the chat example in Socket.io.
I've set up everything, but at this point...
[socketIO connectToHost:#"localhost" onPort:3000];
I get this error
ERROR: handshake failed ... Server returned status code 400
I guess I have to send some params, maybe like this...
[socketIO connectToHost:#"localhost" onPort:3000 withParams:...];
...but I have no idea what I have to send.
Thanks!

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.

Resources