verify a SSL certificate in iOS - wcf

I created a WCF web service hosted using https, I need to access this service using iPhone application.
The service works fine I can retrieve the data using a browser. which in this case, I need to pre-approve the certificate("The certificate for this server is invalid")
But in the iPhone app, I couldn't make it work.
What I have done so far is:
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
[[challenge sender]continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"%#",error.description); }
the connection returns with error(says exactly what a browser says)
The request should return a string or something really simple.
How do I proceed at this point? Btw, I use iOS5.0
Thanks

Found it, only need to add one more line into the connection willSendRequestForAuthenticationChallenge method
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

Related

Two way SSL failed on WatchOS2.1, but same code works on iOS9.1

I was trying to communicate with a service with two way SSL.
I found the connection was cancelled immediately after the client (watch) provide the client certificate, by calling completeHandler(NSURLSessionAuthChallengeUseCredential, credential)
The error got is:
NSURLErrorDomain Code=-999 cancelled
But I've tried run the same piece of code on a phone, it did succeed.
Apart from that, other requests work fine on the watch.
Since, the frameworks on WatchOS and iOS are different, I am wondering if that might be an issue for WatchOS? Or is there anything specifically need to be configured for the watch?
Here is the code
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
NSString *authMethod = [protectionSpace authenticationMethod];
if (authMethod == NSURLAuthenticationMethodServerTrust) {
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:serverTrust]);
} else if (authMethod == NSURLAuthenticationMethodClientCertificate) {
// cancelled immediately after calling the method below.
completionHandler(NSURLSessionAuthChallengeUseCredential, self.credential);
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
Confirmed by Apple engineer.
It is an restriction of Watch OS. Two way SSL is not supported for now.

ASIHTTPRequest setDomain -> NSURLRequest equivalent?

I'm attempting to transition some old code for SOAP requests that relied heavily on the ASIHTTPRequest library, to instead use the standard NSMutableURLRequest instead. However, I'm running an issue: while with ASIHTTPRequest I had access to a setDomain function, I can't seem to find an equivalent with NSMutableURLRequest.
Does anyone know if there is an equivalent function? I've tried setting it as a header named "Domain", but that didn't seem to work.
Ended up following some advice from: iPhone - NTLM, Basic and other authorizations using async NSURLConnection
Turns out, you don't have to set the domain independently; you can set it by passing it as part of the username like so:
domain\\username
within an NSURLCredentials object. The exact code I ended up using was:
//takes care of HTTP Authentication
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSString* authMethod = [[challenge protectionSpace] authenticationMethod];
if ([authMethod isEqualToString:NSURLAuthenticationMethodNTLM]) {
NSURLCredential *credential = [NSURLCredential credentialWithUser:[NSString stringWithFormat:#"domain\\%#", self.userName]
password:self.password
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
}
This all assuming the class is an NSURLConnectionDeligate, of course.

NSURLCredential-credentialForTrust vs username:password

I am new with iOS and https stuffs. I am a beginning software student. I want to write an app that extract the data (such as class number, announcements, etc) from my uni account and show me on my app.
Normally, I go to my uni student page, login with my username and password, and those information showed up on the browser.
I've tried using NSURLConnection and its delegate. please look at my code below. In the beginning, in the method connection:didReceviveAuthenticationChallenge, I created an NSURLCredential object by [NSURLCredential withUser:password:persistence]. And it was not working. Then I tried to do NSLog, then i just found out that the protectionSpace.authenticationMethod is NSURLAuthenticationMethodServerTrust.
Then, I tried to read Apple's document and some googled sources. But, I can't really get it, and then I edited my code as you seen below (it is my final edition, and its still not working).
The point that i don't really get is: I expect that the server should ask me for my username and password. Instead of that, it asked for credentialForTrust, which based on those documents I've read, they suggest me to evaluate the trust of NSURLConnection delegate against the server. However, the server never asked for username and password. so, how can the server know which account I am accessing.
So, i think it should be some relationship between that certificate(trust) with username and password. I don't really understand how these things work.
I think, my question might not be really clear or something, and it might be foolish. I accept this because I've never learned all these things.
So, please someones explain me of how these things work. You can assume that I have some basic understanding of what is https, SSL, NSURLConnection, NSURLCredential, etc. and please guide me to the solution.
I am appreciate for all your efforts.
Below is my code, (it's not working).
The NSLog() in connection:didReceiveAuthenticationChallenge, print out "NSURLAuthenticationMethodServerTrust"
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSLog(#"authenticationMethod is: %#\n",challenge.protectionSpace.authenticationMethod);
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}else{
NSURLCredential *creden = [[NSURLCredential alloc] initWithUser:myusername password:mypassword persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:creden forAuthenticationChallenge:challenge];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#\n %#\n",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.myData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.myData setLength:0];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString *str = [[NSString alloc] initWithData:self.myData encoding:NSASCIIStringEncoding];
NSLog(#"Data in String %#", str);
}
NSURLAuthenticationMethodServerTrust challenges occur when the URL Loading system doesn't trust the server. If you want to avoid those calls, make sure that iOS trusts your server's cert (by installing it manually , or having a trusted root like Verisign sign it).

Is it possible to load an SSL-encrypted website with a self-signed security certificate in an iPhone application?

This is a question that seems to be floating around all over the place, but so far I haven't been able to find a definite answer.
I'm tasked with writing an iPhone application for my company. My company has an SSL-encrypted website with a self-signed certificate. This website is used very frequently, and my employer would like for the site to be easily accessible on an iPhone. My app's function is to provide a method of storing the credentials used to access the site, and when the user opens the app, it automatically sends the stored credentials to the site and skips the login dialog to go straight into the site.
I've set up my app to use a UIWebView and load a request for the site. I've set up the usual authentication methods, such as:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {...}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {...}
When my app reaches the part of the code where I tell the UIWebView to load the NSURLRequest, it doesn't encounter any of my authentication methods. It jumps straight to didFailLoadWithError and the error tells me that I'm connecting to a site that may only be pretending to be my site because it has a self-signed certificate.
How do I get around this? I've found a few answers involving the NSURLConnection methods, but those methods don't appear to be called before I reach the error and am forced to stop loading the page. I've also found some answers involving overriding "undocumented" methods, but when I try implementing that solution my web view never reaches either "didFailLoadWithError" or "didFinishLoad" and never displays any content.
The UIWebKit delegate doesn't forward through any of the NSURLConnection delegate methods to your app. One way to get around this would be to load the page using NSURLConnection and then push it into the UIWebView using -loadData:MIMEType:textEncodingName:baseURL:. Once you've done that you've verified the first page, which (as long as your site doesn't have links off of it), should stay safe. So, how do we verify a self-signed certificate?
I had to solve this with an OSX App a little earlier this year and, once I figured out what I was doing, it was pretty straightforward, assuming you have a similar setup. The solution I propose here actually verifies the server certificate (although in my case I was using a private CA, so I added the CA certificate to the trust root, instead of the server certificate, it should work just as well with that).
You'll need to add tests for NSURLAuthenticationMethodServerTrust to both the -connection:canAuthenticateAgainstProtectionSpace: and -connection:didReceiveAuthenticationChallenge: methods so that you can both request interest in and process the Security challenge.
Hope this helps.
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
// ... implement any other authentication here, such as your client side certificates or user name/password
if ([[protectionSpace authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
return YES;
return NO;
}
-(void)connection:(NSURLConnection *)aConnection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
// implement your client side auth here for NSURLAuthenticationMethodHTTPDigest or basic or your client-side cert
if ([[protectionSpace authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
// install our private certificates or CAs
NSString *myCAString = #"<string containing your CA cert>";
SecCertificateRef certRef = SecCertificateCreateWithData ( NULL, (CFDataRef)[NSData dataFromBase64String: myCAString]);
NSArray *anchorArray = [NSArray arrayWithObject: (NSObject*)certRef];
SecTrustSetAnchorCertificates( challenge.protectionSpace.serverTrust, (CFArrayRef) anchorArray);
SecTrustResultType trustResultType;
OSStatus err=SecTrustEvaluate(challenge.protectionSpace.serverTrust, &trustResultType);
if ((!err) && (trustResultType == kSecTrustResultProceed || trustResultType == kSecTrustResultConfirm || trustResultType == kSecTrustResultUnspecified)) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
} else {
CFArrayRef certChain=NULL;
CSSM_TP_APPLE_EVIDENCE_INFO *statusChain;
SecTrustGetResult( challenge.protectionSpace.serverTrust, &trustResultType, &certChain, &statusChain);
[challenge.sender cancelAuthenticationChallenge:challenge];
}
} else {
// Cancel if we don't know what it is about... this is supposed to be secure
[challenge.sender cancelAuthenticationChallenge:challenge];
}
}
I have managed to do what you desire by using RestKit framework, so yes, I can confirm it can be done. Go see in the RestKit sources how they do it, or just use RestKit. This says how to use SSL with RestKit. In my case, there was one catch - the self signed certificate had to contain the certain extensions, otherwise I always got the kSecTrustResultRecoverableTrustFailure. Go see here, the answer is voted -1 but it actually fixed the problem for me.

Cocoa Touch: How to detect a missing file on the download server

My iOS app downloads document files from a server using the NSURLConnection class. I use it asynchronously.
It can happen that a document is missing from the server (configuration error). I expected NSURLConnection to call my delegate error method:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
But it doesn't. Instead, it happily downloads the HTML error 404 web page returned by the server. The only workaround I found was to check the NSURLResponse MIMEtype property and fail myself when it is text/html. It works fine, but I don't like it because it precludes me from supporting html documents at a later date.
How can I check whether a requested URL is indeed present on the server?
Thanks
In the - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response delegate method you can ask the response for it's error code.
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if (httpResponse.statusCode == 404) {
...
//etc
When you receive the response (in -connection:didReceiveResponse:), you get to query the response. Because you're using HTTP, it will be a kind of NSHTTPURLResponse, though you may want to test for that. Anyway, it will tell you a -statusCode, which might be 200, 404 or something else.