ASIHTTPRequest retrieve server certificate - ssl

For some legacy and internal reasons, I need to retrieve the certificate of the server (and ones from the chain as well), and read its fingerprint.
I understand this is easy to implement using AFNetworking or NSURLConnection since I only need to implement it in - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
But is it possible to do the same thing when using ASIHTTPRequest, which is just just basically a wrapper of CFNetworking?
Once again, I don't have a client certificate and my goal is to get the certificate from the server to read its fingerprint of issuer.
I will need to validate the fingerprint of server certificate in client and continue the request if it matches the one I expected, or cancel the request / throw error otherwise. Is this possible using ASIHTTPRequest?

I verified that there's no way to do this using CFNetworking (what ASIHTTPRequest is using), see https://devforums.apple.com/thread/87346. For those who don't have ios Developers account (I am not sure why you are reading this though), "No. After thinking about this in depth I don't think it's possible. NSURLConnection, which does this, use a private hook into CFHTTPStream, and you won't be able to use that."
Btw, we rewrote our network layer using NSURLConnection and solved this problem

Related

NSURLSessionAuthChallengeRejectProtectionSpace fails - custom certificate validation on OS X iOS

I have application with custom certificate validation. Code is quite simple:
- (void) URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler
{
NSString * authenticationMethod = challenge.protectionSpace.authenticationMethod;
CSHTTPDownoadTaskProxy *proxyTask = [self streamedDataTaskForHttpTask: task]; // get custom information about task
if ([authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust]) {
if (!proxyTask) {
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
LOG_UNEXPECTED_TASK(task);
return;
}
[self validateCertificate: challenge.protectionSpace withCompletionHandler: completionHandler];
} else if ([authenticationMethod isEqualToString: NSURLAuthenticationMethodHTTPBasic] {
… … …
}
}
- (void) validateCertificate: (NSURLProtectionSpace *)protectionSpace
withCompletionHandler: (void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *credential))completionHandler {
// some logic
… … …
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
}
Now when certificate is rejected usually everything works as expected, certificate is rejected and error is reported for HTTP request.
For some servers even though completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil); is invoked request is processed and completed successfully.
NSURLSessionAuthChallengeRejectProtectionSpace documentation says:
Reject this challenge, and call the authentication delegate method
again with the next authentication protection space. The provided
credential parameter is ignored.
This is a bit confusing, what does it mean "next authentication protection space"?
This API covers also password protection space and I understand that that if service has multiple ways of authentication, for example: Negotiate, Diget, Basic, NSURLSession prepossess best one first it it is rejected it notifies about alternative possibility or fails request.
But my delegate is invoked only once for this authentication method NSURLAuthenticationMethodServerTrust!
For most cases when I'm using this value expected error is reported (invalid certificate):
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this
server is invalid. You might be connecting to a server that is
pretending to be “10.133.32.55” which could put your confidential
information at risk."
For other cases (logs show that completion handler was called with proper values) HTTP request is processed and completes successfully (it happens for some servers on different client machines).
Did anyone else experience similar problems?
I could do nasty workaround with NSURLSessionAuthChallengeCancelAuthenticationChallenge. This value reports that request was "Canceled" so I have to generate my own error.
I don't like this approach and prefer find reason why NSURLSessionAuthChallengeRejectProtectionSpace doesn't work sometimes.
Update
Ok I have some extra clue. Problem appears only on servers which are demanding client identity certificate.
When client identity is not needed certificate validation is accepted (certificate is rejected when requested), if client identity is demanded and available, server certificate validation result is ignored.
After finding precise answer it looks like whoever configured test servers did that more properly for servers with client identity leading to this side effects.
The purpose of NSURLSessionAuthChallengeRejectProtectionSpace is to tell the OS that you aren't able to do anything with a particular authentication type. For example, if the server says that it is willing to accept either client certificates, basic auth (username/password) or digest auth (essentially signing the request using the username/password as a key) then NSURLSession will ask you for one of those methods at a time.
If the session asks you for client certificates and all you have are a username and password, you would reject the client certificate authentication method by calling the continuation block with NSURLSessionAuthChallengeRejectProtectionSpace, and the session will then ask you for a username and password.
IMO, it doesn't make much sense to use NSURLSessionAuthChallengeRejectProtectionSpace in combination with server trust authentication, because there is no alternative to TLS for evaluating whether the connection is trusted. In practice, I think that this is equivalent to calling NSURLSessionAuthChallengePerformDefaultHandling, which means that if the cert is trusted, it will be accepted, otherwise it will be rejected. But you should be using NSURLSessionAuthChallengePerformDefaultHandling explicitly if that is the desired behavior (or NSURLSessionAuthChallengeCancelAuthenticationChallenge if you want to cancel the request).
It may be that rejecting the request in the presence of a client certificate causes the server's cert to be ignored, in which case this is probably a bug, and you should file it. But the reason for doing so is presumably because accepting the server's cert is required when sending a client cert, and you told it to skip the server validation and send you the next method (client cert). So I'm pretty sure that's the reason things are behaving strangely.
Also, you need a final "else" case there to handle unknown protection spaces. Chances are, the else case should use NSURLSessionAuthChallengePerformDefaultHandling. If you don't do this, then when you get an unknown protection space, the connection will just stay open forever and will never move forward or get canceled, because nothing ever calls the continuation block.

NSURLSession prevent authentication challenge for proxy

I am using NSURLSession to implement an osx application that perform REST requests, and has the ability of manually or automatically configure proxy settings for app requests.
I was able to do this using session configuration, and is working for simple contexts.
However when it comes to proxies that require authentication I encounter some issues.
Due to some project requirements, when a request fails due to proxy authentication error(status code 407), I need to display my own custom dialog.
From what I've read so far, I did not find any way of achieving this using NSURLSession. There are some delegate methods related to authentication challenge, but are called after the challenge was already displayed to the user.
Is there a way to skip showing an authentication challenge for a NSURLSession request and fail with 407 status code instead?
As far as I know, neither NSURLConnection nor NSURLSession ask the app to handle 407 responses before displaying that dialog, and there's not a way to change that, short of filing a bug with Apple and convincing them to add that functionality in a future version of iOS or OS X.
For now, I think the best you can probably do is use libcurl to make a "junk" request first, see if it returns a 407, and if so, ask the user to provide new credentials. It isn't pretty, but it will technically meet your requirements. :-)
But honestly, the requirements are dubious. OS X and iOS users don't expect to control proxy settings within an app. They're normally systemwide behaviors. As such, trying to control them in the app is questionable. So if you have any say-so in the matter, you should try to get the requirements changed.

Phone and backend connection, how to minimize hacking

I was reading some topics about security and how hackers can look at the request you send to the backend to figure out how your system works but I did not find any good solution to avoid this.
So I was wondering what would you do in your app (here an iphone app) to make sure that hackers cannot see the content of the request your sending to the backend.
example http://myserver.com/api.php/login&pwd=test&username=pwd,
how to hide this so that no one can see the content.
I was thinking of different solutions:
1) encrypt the pwd and the username (not ideal as hacker can still see the post function of the server you are sending the request to)
2) use SSL request (I think this is only interesting to secure the connection to the server, meaning if the hacker is using his phone to connect to the server he should be able to see the request he is sending and so see the full URL)
3) change my backend so that all the request are sent to the same post function with an encrypted message. Finally the backend would decrypt and dispatch the message to the right function. This could work as the hacker would only see the url I am sending my request but would not have any information on what I am sending.
example: http://myserver.com/api.php/receiver&message=415gre6168sg4rg4e61g6r8g
"415gre6168sg4rg4e61g6r8g" could be decrypted as:
"login#pwd#username" and so I would be able to send this to the right function
But I am sure some of you have encounter similar issues and have better suggestions, would be interested to see what you would do,
Thanks
Any encryption you add is an extra effort for the hackers.
But in terms of what concerns to Apple use SSL connection should be enought based on iOS and the new IT - Security.
You can read more about the security polices used/recommended by Apple on iOS Security Guide

Decent routine to check server certificate

I am writing a client app that need talk to Active Directory server and one of requirements is to support LDAPS/StartTLS.
I already figure out there is one option need to set:
if (ldap_set_option(pLdap, LDAP_OPT_SERVER_CERTIFICATE, &my_cert_check_func) != LDAP_SUCCESS) {
std::cerr << "ldap set cert check callback failed" <<std::endl;
return NULL;
}
and my_cert_check_func is over-naive and not safe at all:
static BOOLEAN my_cert_check_func(PLDAP connection, PCCERT_CONTEXT server_cert)
{
return TRUE;
}
And I also did a lot of googling and read quite a lot msdn, but still no clue. I have never handle such security-related coding before so any thing related to cert check are welcome.
And because I write this app using Winldap API, so the code should use Windows specific APIs.
And I am also thinking do such check using openssl api (this api is a dependency of my app, so it is fine to use that).
Could you show me some sample code for doing real checking of server certs against client security store or what ever client has?
Thank you very much!
You don't need to verify the entire certificate chain etc. for validity. LDAPS should already have done that. You only need to check the subjectDN of the certificate against what you think it should be when talking to that server.

Objective-C/Cocoa: How do I accept a bad server certificate?

Using NSURLRequest, I am trying to access a web site that has an expired certificate. When I send the request, my connection:didFailWithError delegate method is invoked with the following info:
-1203, NSURLErrorDomain, bad server certificate
My searches have only turned up one solution: a hidden class method in NSURLRequest:
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:myHost];
However, I don't want to use private APIs in a production app for obvious reasons.
Any suggestions on what to do? Do I need to use CFNetwork APIs, and if so, two questions:
Any sample code I can use to get started? I haven't found any online.
If I use CFNetwork for this, do I have to ditch NSURL entirely?
EDIT:
iPhone OS 3.0 introduced a supported method for doing this. More details here: How to use NSURLConnection to connect with SSL for an untrusted cert?
The supported way of doing this requires using CFNetwork. You have to do is attach a kCFStreamPropertySSLSettings to the stream that specifies kCFStreamSSLValidatesCertificateChain == kCFBooleanFalse. Below is some quick code that does it, minus checking for valid results add cleaning up. Once you have done this You can use CFReadStreamRead() to get the data.
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://www.apple.com"), NULL);
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), myURL, kCFHTTPVersion1_1);
CFReadStreamRef myStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(myDict, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
CFReadStreamSetProperty(myStream, kCFStreamPropertySSLSettings, myDict);
CFReadStreamOpen(myStream);
If it's for an internal server for testing purposes, why not just import the test server's certificate into the KeyChain and set custom trust settings?
iPhone OS 3.0 introduced a supported way of doing this that doesn't require the lower-level CFNetwork APIs. More details here:
How to use NSURLConnection to connect with SSL for an untrusted cert?
I've hit the same issue - I was developing a SOAP client, and the dev server has a "homegrown" certificate. I wasn't able to solve the issue even using that method, since I wasn't using NSURL, but the (poorly documented and apparently abandoned) WS methods, and decided for the time being to (internally) just use a non-SSL connection.
Having said that, however, the question that springs to mind is, if you aren't willing to use a private API in a production app, should you be allowing access to a site with a dodgy certificate?
I'll quote Jens Alfke:
That's not just a theoretical security problem. Something
like 25% of public DNS servers have been compromised, according to
recent reports, and can direct users to phishing/malware/ad sites even
if they enter the domain name properly. The only thing protecting you
from that is SSL certificate checking.
Can you create a self signed certificate and add your custom certificate authority to the trusted CAs? I'm not quite sure how this would work on the iPhone, but I'd assume on Mac OS X you would add these to the Keychain.
You may also be interested in this post Re: How to handle bad certificate error in NSURLDownload
Another option would be to use an alternate connection library.
I am a huge fan of AsyncSocket and it has support for self signed certs
http://code.google.com/p/cocoaasyncsocket/
Take a look, I think it is way more robust then the standard NSURLRequests.