HTTPS post request in IOS - ios

Im trying to make https post request by using following code.
NSURL *url = [NSURL URLWithString:#"https://portkey.formspring.me/login/"];
//initialize a request from url
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[url standardizedURL]];
//set http method
[request setHTTPMethod:#"POST"];
//initialize a post data
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:#"username", #"username",
#"password", #"password", nil];
NSError *error=nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:postDict
options:NSJSONWritingPrettyPrinted error:&error];
[request setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
//set post data of request
[request setHTTPBody:jsonData];
//initialize a connection from request
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//start the connection
[connection start];
But im getting following response.
error Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “portkey.formspring.me” which could put your confidential information at risk." UserInfo=0x7564180 {NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSErrorFailingURLKey=https://portkey.formspring.me/login/, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending >to be “portkey.formspring.me” which could put your confidential information at risk., NSUnderlyingError=0x7168a20 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “portkey.formspring.me” which could put your confidential information at risk.", NSURLErrorFailingURLPeerTrustErrorKey=}
Can anybody tell me how to make post request using NSURLConnection??
Thanks in advance.

The certificate for the site you're trying to connect to is untrusted (try visiting the link you posted in Chrome).
By default, iOS won't let you connect to site that presents an untrusted certificate. You can bypass this check if absolutely necessary - see this question: How to use NSURLConnection to connect with SSL for an untrusted cert?
However, it's going to much, much, much better to actually fix the certificate in question.

These Delegate Methods of NSURLConnection are Deprecated
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
You can use -connection:willSendRequestForAuthenticationChallenge: instead of these 3 Deprecated Methods
-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust)
{
[[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge];
}
}

Related

iOS 9 ATS blocking HTTPS request to server with Self-Signed Certficate

This issue is all over Stack Overflow and I have spent the past 2 days trying countless combinations of ATP configurations and getting my app to work. I'm going to be thorough with my problem as it appears the tiniest thing can affect how to resolve this.
I have just recently set up an Ubuntu 14 server with SSL and TLS 1.2 enabled. On my server are my server-side scripts which my app depends on. In my app I use a NSMutableURLRequest to request my API from the server like so:
NSString * noteDataString = [NSString stringWithFormat:#"email=%#&password=%#&type=%#", companyEmail, companyPassword, #"login"];
NSData * postData = [noteDataString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString * postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"https://mydomain.me:443/path/to/file.php"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSHTTPURLResponse * urlResponse = nil;
NSError * error = nil;
NSData * responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
NSString * result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"Response Code: %ld", (long)[urlResponse statusCode]);
When I copy the url into Chrome the correct PHP return is shown. When requesting from Xcode 7 on iOS 9 I get this error:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
I have used countless info.plist settings as seen across similar issues. I have tried disabling the need for forward secrecy, I have tried enabling arbitrary loads, I have tried using the exception domain mydomain.me and its subdomains. The only progress I achieve is an error code -9813 switching to -9802.
I am aware of adding delegate methods for NSURLRequest but these are never called and assumed redundant for solving this problem.
I built a lot of the app off a MAMP server using localhost and http and the requests worked when I enabled arbitrary loads then, so nothing wrong with my plist.
It's mind-boggling onto why I have a special case, and I knew Stack Overflow was the place for such situations!
Thanks, and I hope that this helps many more developers beyond me when solved.
Solution. Thank you everybody in the comments for pointing me in the right direction. The solution was to create an NSObject that handled NSURLRequests for this special case.
Credit to: https://www.cocoanetics.com/2010/12/nsurlconnection-with-self-signed-certificates/
The following is almost a direct copy from the tutorial in the link, but I figured it'd be easier to stay here.
So,
I had to create a new NSObject class with the following code:
BWWebService.h
#import <Foundation/Foundation.h>
#interface BWWebService : NSObject{
NSMutableData *receivedData;
NSURLConnection *connection;
NSStringEncoding encoding;
}
- (id)initWithURL:(NSURL *)url;
#end
BWWebService.m
#import "BWWebService.h"
#implementation BWWebService
- (id)initWithURL:(NSURL *)url{
if (self = [super init]){
NSURLRequest * request = [NSURLRequest requestWithURL:url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
// Every response could mean a redirect
receivedData = nil;
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)
[response textEncodingName]);
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if (!receivedData){
// No store yet, make one
receivedData = [[NSMutableData alloc] initWithData:data];
}else{
// Append to previous chunks
[receivedData appendData:data];
}
}
// All worked
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSString * xml = [[NSString alloc] initWithData:receivedData encoding:encoding];
NSLog(#"%#", xml);
}
// And error occurred
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Error retrieving data, %#", [error localizedDescription]);
}
// To deal with self-signed certificates
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace{
return [protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
// we only trust our own domain
if ([challenge.protectionSpace.host isEqualToString:#"myuntrusteddomain.me"]){
NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
#end
And if that wasn't enough, to make the request I used the following in replacement of my original request:
NSURL * url = [NSURL URLWithString:#"https://myuntrusteddomain.me:443/path/to/script.php"];
BWWebService * webService;
webService = [[BWWebService alloc] initWithURL:url];
I know that this does not POST data like the original, but that comes later. I am sure it will be a matter of handling the POST in initWithURL.
Thanks everyone.
Edit: It appears this application of the solution only works with Allows Arbitrary Loads set to YES.
Open Xcode, Command-Shift-2, enter 9813, and you immediately find -9813 = errSSLNoRootCert, which was to be expected since your self signed certificate has no root certificate, while -9802 = errSSLFatalAlert (you really buggered it up).
The problem seems to be that for security reasons, some software doesn't like self signed certificates. This can often be fixed by creating and installing your own root certificate, and having a certificate signed by your own root certificate.

Prestashop Search API Method in IOS Application

I'm building my first IOS Application and I'm currently integrating my application with my Prestashop website. I already have grabbed a ton of data from the databases, but I'm having less luck using the Search Method from their API.
I have successfully called the REST API and authenticated with the server. I'm getting back a response status code of 200 so everything looks ok with the authentication, however the data I'm receiving from the server is the homepage of my site in HTML form rather than an XML file with the search results.
Below I'll list some of the parts of code in question that I'm using to access the Prestashop server
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://www.******.com:443/api/search/?query=%#&language=1",searchTerm]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
This is the method I use to set up the connection to the Prestashop Server
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
NSLog(#"Test 1.5");
[self clearCookiesForURL];
if([challenge previousFailureCount]==0)
{
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"(I'm omitting the credential key but it does work)" password:#"" persistence:NSURLCredentialPersistenceForSession];
NSLog(#"Credential : %#",credential);
//NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
} else{
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
And this is the method for my authentication. I have all of the other connection methods instantiated (canAuthenticateAgainstProtectionSpace, didReceiveResponse, didReceiveData, etc.) and they are all being called and working fine.
NSLog(#"Data After : %#",responseData);
NSString *tempString;
tempString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
NSLog(#"String : %#",tempString);
This is the section of code I use to translate the data I get from the server into a string (which i thought was going to be XML but is instead HTML)
Here is a screenshot of the debug log showing that I'm getting an HTML File
Can anybody see what I'm doing wrong here? Any help would be greatly appreciated!

HTTP(S) POST : connectionDidFinishLoading gets called with empty data, possible trust issue

I am trying to communicate with TSYS payment gateway which has an https server from my iOS application. My original code is given below.
-(void)postRequest:(NSString *)jsonBody{
NSData *requestData = [NSData dataWithBytes:[jsonBody UTF8String]
length:[jsonBody length]];
NSURL *url = [NSURL URLWithString:TSYS_URL];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d",[requestData length]]
forHTTPHeaderField:#"Content-Length"];
[request setValue:#"*/*" forHTTPHeaderField:#"Accept"];
[request setHTTPBody:requestData];
[[NSURLConnection alloc]initCustomURLWithRequest:request delegate:self ];
}
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
NSLog(#"http error code : %d", code);
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"receivedData.length : %d", [self.receivedData length]);
connection = nil;
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
connection = nil;
}
The payment gateway url is
#define TSYS_URL #"https://stagegw.transnox.com/servlets/TransNox_API_Server"
And I get this in console
http error code : 404
receivedData.length : 0
When I saw the log, the first port of call was about HTTPS SSL authentication, so I wrote a android project that do POST by trusting all certificate (not ideal, I know), and hurray I was getting correct response. But when I removed the part that enables HttpClient to trust all certificates, I got empty response(in Android).
To make iOS trust all certificate, I added below code
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:
(NSURLProtectionSpace *)protectionSpace {
//This always returns true
return [protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:
(NSURLAuthenticationChallenge *)challenge {
if([challenge.protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust]){
NSURLCredential *credential = [NSURLCredential
credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential
forAuthenticationChallenge:challenge];
}
[challenge.sender
continueWithoutCredentialForAuthenticationChallenge:challenge];
}
Still I get the same error.
Ok my questions are
First and foremost, why is this not working? I need to get this
working , so that I can move forward with project. Since my project
is in beginning state, I don't mind if the solution is to trust all
certificates. I somehow need to get server talking.
The type of authentication challenge I am getting is
NSURLAuthenticationMethodServerTrust. Ideally I should get the
server issued certificate and make my NSURLConnection object trust
it. How could I get the certificate file from server?
I got it working at last. In the end, it was not an issue with authentication. Along with the all the code above, I had to pass a header field "User-Agent" with the request. When I passed it I started to get response from the server.

detect if website login failed objective-c?

Just a quick one how do i detect if a login failed here is my code:
- (IBAction)btnTimetable:(id)sender {
NSString *user = _txtUsername.text;
NSString *pass = _txtPassword.text;
NSString *content = [NSString stringWithFormat:#"username=%#&password=%#", user, pass];
NSData *data =[content dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postlenght=[NSString stringWithFormat:#"%lu",(unsigned long)[data length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://moodle.thomroth.ac.uk/login/index.php?mode=login"]];
[request setHTTPMethod:#"POST"];
[request setValue:postlenght forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:data];
//NSError *error=nil;
//NSURLResponse *response=nil;
//NSData *result=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[_webView loadRequest:request];
[self performSelector:#selector(parseTimetable) withObject:nil afterDelay:3.0];
}
i dont even know where to start on this one is there a delegate method to detect such actions ?
As stated in the official Developer forums, UIWebView does not support authentication challenges in iOS. Please read here (requires developer account): UIWebView does not directly support authentication challenges
sendSynchronousRequest:returningResponse:error: should return nil and the status code of the returned response should equal 401 (Unauthorized):
[(NSHTTPURLRequest*)response statusCode] == 401
I would guess, the error parameter should be set to a corresponding error (please check this through printing it to the console).
If you use the delegate approach of NSURLConnection the situation is different:
When NSURLConnection receives a 401 (for example, the connection requires credentials for authentication, or a previous authentication attempt failed), it does invoke the delegate
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
if implemented, otherwise it invokes these (now considered obsolete methods):
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
if implemented.
The official documentation provided more information: NSURLConnectionDelegate Protocol Reference.
You can cancel the authentication request, if you decide to do so. As a result, the connection can fail.
If the connection fails, connection:didFailWithError will be invoked.
If you really want to do it using UIWebView you could use it's delegate method and parse code answer of your website.
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *webSource = [_web stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];
}

Basic auth iOS 6 - Not working

I am trying create a login screen which sends login info sharepoint server and i expect to be able to successfully login.
There are plenty of old examples and libraries which I am not able to use. But after spending hours I found this link to have the crux of all
http://transoceanic.blogspot.com/2011/10/objective-c-basic-http-authorization.html
My code looks like this now:
- (void) startLogin {
NSURL *url = [NSURL URLWithString:#"http://site-url.com"];
NSString *loginString =(NSMutableString*)[NSString stringWithFormat:#"%#:%#",usernameTextField.text,passwordTextField.text];
NSData *encodedLoginData=[loginString dataUsingEncoding:NSASCIIStringEncoding];
NSString *authHeader=[NSString stringWithFormat:#"Basic %#", [encodedLoginData base64Encoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:3.0];
// [request setValue:authHeader forKey:#"Authorization"];
[request setValue:authHeader forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"POST"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
There are three issues:
the commented out line-code doesn't give any error but it crashes on that line(while debugging)
on [request setValue:authHeader forHTTPHeaderField:#"Authorization"]; i am getting error "No visible interface for NSURLRequest declares selector setHTTPHeaderField"
Also, I am getting warning - unused variable "connection" in last line. I am not sure how this whole thing works and any simple example or correction is appreciated.
I would also like to know if there are any other simple methods for basic auth.
UPDATE: Delegate methods
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// Access has failed two times...
if ([challenge previousFailureCount] > 1)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Authentication Error"
message:#"Too many unsuccessul login attempts."
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
else
{
// Answer the challenge
NSURLCredential *cred = [[NSURLCredential alloc] initWithUser:#"admin" password:#"password"
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Connection success.");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection failure.");
}
Change NSURLRequest to NSMutableURLRequest to access it's setValue:forHTTPHeaderField method, and add a HOST header as well if it's a shared web host.
At the end, you have to start the connection:
[connection start];
Also, make sure you've set up your NSURLConnectionDelegate delegate methods for the call backs.

Resources