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).
Related
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.
For my Mac OS X Cocoa app, I am trying to
connect to a SFTP server that only accepts username/password credentials
get the contents of a remote directory
upload files
and find it surprisingly complicated.
After trying ConnectionKit (nearly no documentation), NMSSH (crashed once too often with simultaneous uploads), rsync (not supported by the server), sftp (needs key authentication if scripted, doesn't work with username/password), I am now back to ConnectionKit: https://github.com/karelia/ConnectionKit
However, I am struggling with the authentication challenge, as I don’t know what to do with my credential in the delegate method.
I downloaded and compiled ConnectionKit (apparently version 2).
I am trying to use CK2FileManager as the Readme indicates (is this the right approach at all? Or should I use the libssh2_sftp-Cocoa-wrapper instead?… however I had troubles with libssh2 blocking methods in NMSSH before)
I am successfully setting up my connection URL and
my delegates' -didReceiveAuthenticationChallenge is called
But this is where I struggle: I know how to create a NSURLCredential, however, I can’t figure out what to do with it =>
- (void)fileManager:(CK2FileManager *)manager
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLCredential *credentials = [NSURLCredential
credentialWithUser:self.username
password:[self getPassword]
persistence:NSURLCredentialPersistenceForSession];
// what to do now?
// [manager useCredential:] doesn’t exist, nor is there a manager.connection?
// ...
}
I already read the header, I searched the archives of this list, but all answers seem to be outdated.
I also searched Google, Bing and StackOverflow and found one promising example from 2011 using CKFTPConnection, which doesn’t seem to be included in the current framework anymore.
Thanks so much for any pointer to the right direction.
tl;dr
I don't know how to respond to ConnectionKit's CK2FileManager authenticationChallenge:
see the comment in the code example
For CK2:
- (void)listDirectoryAtPath:(NSString *)path
{
// path is here #"download"
NSURL *ftpServer = [NSURL URLWithString:#"sftp://companyname.topLevelDomain"];
NSURL *directory = [CK2FileManager URLWithPath:path isDirectory:YES hostURL:ftpServer];
CK2FileManager *fileManager = [[CK2FileManager alloc] init];
fileManager.delegate = self;
[fileManager contentsOfDirectoryAtURL:directory
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
completionHandler:^(NSArray *contents, NSError *error) {
if (!error) {
NSLog(#"%#", contents);
} else {
NSLog(#"ERROR: %#", error.localizedDescription);
}
}];
}
and than you have to implement the following protocol
- (void)fileManager:(CK2FileManager *)manager operation:(CK2FileOperation *)operation
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(CK2AuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
if (![challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodDefault]) {
completionHandler(CK2AuthChallengePerformDefaultHandling, nil);
return;
}
NSString * username = #"<username>";
NSString * pathToPrivateSSHKey = #"</Users/myNameOnLocalMaschine/.ssh/id_rsa>"
NSURLCredential *cred = [NSURLCredential ck2_credentialWithUser:username
publicKeyURL:nil
privateKeyURL:[NSURL fileURLWithPath:pathToPrivateSSHKey]
password:nil
persistence:NSURLCredentialPersistenceForSession];
completionHandler(CK2AuthChallengeUseCredential, cred);
}
That's it.
Call -listDirectoryAtPath: and then you will get in the Completion Handler Block in contents array all the files located on the given path :)
Okay, that was easy and I could have found out that on my own; just for the reference: [[challenge sender] useCredential:credentials forAuthenticationChallenge:challenge];
Sorry to reward myself for my own question, but maybe this code snippet helps filling the missing docs, this is how I connect to my SFTP server with ConnectionKit:
- (void)connectWithCompletionBlock:(void (^)(void))completionBlock {
if(!self.cFileManager) {
self.cFileManager = [[CK2FileManager alloc] init];
self.cFileManager.delegate = self;
}
NSURL *sftpServer = [NSURL URLWithString:[#"sftp://" stringByAppendingString:self.server]];
self.remoteFolder = [CK2FileManager URLWithPath:self.remotePath relativeToURL:sftpServer];
// try to get the contents of the current directory
[self.cFileManager contentsOfDirectoryAtURL:self.remoteFolder
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
completionHandler:^(NSArray *contents, NSError *error)
{
NSLog(#"remote folder contents: \n%#", contents);
// invoke completion block
completionBlock();
}];
}
- (void)fileManager:(CK2FileManager *)manager
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLCredential *credentials = [NSURLCredential
credentialWithUser:self.username
password:[self getPassword]
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credentials forAuthenticationChallenge:challenge]
}
I need to communicate with server with SOAP request and get response. I have quite good method which creates XML request with parameters, transform it to NSMutableURLRequest and send with NSURLConnection. All of it works fine so I'll skip this part of code. My server is kind of Magento shop so I receive different amount of data depends what request I use. When I get short responses like session ID, everything works perfect, but when response is longer (list of countries for example) my data is lost somehow. I checked it by comparison of data length in didReceiveResponse and didReceiveData
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.expectedLength = response.expectedContentLength;
self.downloadedLength = 0;
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Data recived");
self.downloadedLength = self.downloadedLength + [data length];
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.downloadedLength == self.expectedLength) {
NSLog(#"correctly downloaded");
}
else{
NSLog(#"sizes don't match");
}
str = [[NSString alloc] initWithBytes:[receivedData bytes] length:[receivedData length] encoding:NSISOLatin1StringEncoding];
NSData *tmp_Data = [[NSData alloc] initWithData:[str dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]];
parser = [[SYXmlParser alloc]initWithData:tmp_Data];
[parser startParser];
if([parser theDataArray] != nil && [[parser theDataArray]count] != 0)
{
resultArray = [[NSMutableArray alloc]initWithArray:[parser theDataArray]];
[self performSelectorOnMainThread:#selector(loadFinished) withObject:nil waitUntilDone:YES];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
When response is long I got cut XML and my comparison returned "sizes don't match". XML is not cut definitely by half, but the data is missing in the middle of text, like some of characters is missing only. The issue repeats every time in same places in same way so its not random. I tried use AFNetworking to solve this problem, but I think I cant use it properly with this SOAP request. I will be grateful for every proposition which would fix this problem.
EDIT:
I used this Charles but there is the same problem as in xcode. When I open response tab and SOAP tab the response is null, on XML tab there is an error that Charles could't parse this received data, but in overview tab there is size of response 4666 bytes. Is that mean that server gives bad response? I cant believe it because its commercial server Magento which is used with many others languages and it works.
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];
I'm currently using NSURLConnection to test if I can successfully connect to a server. Up until now, everything has worked flawlessly. The delegate methods get called, I can do what i want. But when I want to set a BOOL called connected in.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.connected = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.connected = NO;
}
I always get NO.
I'm using my connection class in another class. I access my variable by doing this.
Connection *connection = [[Connection alloc] init];
[connection connectTo:#"localhost"];
connection.connected;<------ this is always NO.
Thanks
Your connectTo method is being called asynchronously. Meaning, after calling connectTo, the execution will proceed immediately to the next line where you check for your connected property. It won't wait for the connection to receive a response.
Also, you may want to take a look at Apple's Reachability class, which is provided for exactly this sort of connection testing:
http://developer.apple.com/library/ios/#samplecode/Reachability