Error in GET on https ?? Can't set NSLocalizedRecoverySuggestion - objective-c

NSURL *url = [NSURL URLWithString:#"https://myUrlString.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *responseurl;
NSError *err;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&responseurl error:&err];
NSLog(#"data length:%u", data.length);
NSLog(#"response:%# , error:%#", responseurl, err);
And the response i got is :-
data length:0
response:(null) ,
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 “myUrlString.com” which could put your confidential information at risk." UserInfo=0x8742d70 {NSErrorFailingURLStringKey=https:https://myUrlString.com,
NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?,
NSErrorFailingURLKey=https://myUrlString.com,
NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “myUrlString.com” which could put your confidential information at risk., NSUnderlyingError=0x8745bf0 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “myUrlString.com” which could put your confidential information at risk.", NSURLErrorFailingURLPeerTrustErrorKey=}

You are using Https request so you should use ASIHTTPRequest or you may try this code
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:#"https:yoururl"]];
[request setHTTPMethod:#"GET"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:getData];

See Technical Note TN2232 for a discussion of what to do to properly resolve HTTPS Server Trust issues, and more importantly, what not to do. The bottom line is that they encourage you to fix the server to resolve the trust issue, rather than working around it.
If you want to temporarily work around it (for example you're just doing some testing with some tool like Charles for which you're temporarily intercepting requests in order to perform diagnostics), you can use the private method to turn off this validation (note, if this code is in app submitted to App Store, they may reject it for using a private API) or you can respond to the NSURLConnection delegate methods.
Again, be very careful about shipping code that bypasses this important warning, but if you have to during the testing of your app, then one of the above methods can be useful.

Related

Request NSURLSession with SSL .cert and .key files

I have to login via https in an Obj-C project. Everything is fine with all the url, the user, pass and the needed stuff. For identify, the server checks by ssl .cert and .key files. So far, so good, the files were uploaded to the server, and the connection made well by curl from terminal.
Here comes my problem.
Spend some days, read the available stuff here and there, but simply can't find any solution to send the ssl files with the request in Obj-C. (The server cannot accept p12)
Here's the curl:
curl -q -k --cert cert2048.crt --key key2048.key https://somesite.com/ -d "username=usrnm&password=psswrd"
Here's my Obj-C code so far:
-(void)connect {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://somesite.com/"]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSDictionary* bodyParameters = #{
#"username": #"usrnm",
#"password": #"psswrd"
};
[request setHTTPBody:[self httpBodyForParameters:bodyParameters]];
[request setHTTPMethod:#"POST"];
[request setValue:#"gzip, deflate" forHTTPHeaderField:#"Accept-Encoding"];
[request setValue:#"keep-alive" forHTTPHeaderField:#"Connection"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSError *jsonError;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
NSLog(#"%#", jsonResponse);
}];
[postDataTask resume];
}
(httpBodyForParameters simply sets up the request body)
The connection establish, everything passes, the jsonResponse holds response as expected (well, the error message about authentication failure, in regular format). But I simply can't find a way to send the ssl files as in the curl line. Sadly, https authentication is far beyond my knowledge. I'm stucked. Every help appreciated.
Thank you,
Sz
That curl command is using the cert and key as a client certificate for authentication, not sending them as files. It's actually part of the authentication handshake.
The code for importing PKCS data and using it in response to a client certificate challenge is fairly involved, so rather than try to explain it all off the top of my head, I'm going to point you to another Stack Overflow question and answer that contain pretty extensive code snippets.
Creating a SecCertificateRef for NSURLConnection Authentication Challenge
However, be aware that some of the code in that link is not quite correct. For the protection spaces that are not handled, you should use default handling, not cancel the challenge.

NSUrlSession proxy request fails with error code 310 (kCFErrorHTTPSProxyConnectionFailure)

I'm trying to send a POST request to a specific URL through a proxy server. To test that the code I'm writing is working, I installed squidman on my machine and started a proxy server on port 33074. I tested the proxy server by changing the network settings to use the proxy when making HTTP/HTTPS requests and it's working ok.
Now I wrote the following code:
NSDictionary* proxyConfig = #{
(NSString*) kCFNetworkProxiesHTTPSEnable: #(1),
(NSString*) kCFNetworkProxiesHTTPSProxy: #"127.0.0.1",
(NSString*) kCFNetworkProxiesHTTPSPort: #"33074",
};
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
[sessionConfig setTimeoutIntervalForRequest:60];
[sessionConfig setConnectionProxyDictionary:proxyConfig];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"https://the-host-where-im-posting"]];
[request setHTTPMethod:#"POST"];
[request setValue:[[NSString alloc] initWithFormat:#"%lu",length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:data];
NSURLSession *session = [self createHttpSession];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 200){
//...
} else {
LOGERROR(#"Error: %#", error);
}
}
The problem is that the request is not successfull and the following error is logged:
Error: Error Domain=kCFErrorDomainCFNetwork Code=310 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2096, _kCFStreamErrorDomainKey=4}
Additional Info:
1. It should be noted that the requests submits perfectly without setting the proxyConfig dictionary.
2. A similar error gets reported if I'm trying to submit the request using HTTP through the proxy (by correspondingly changing the keys in the proxyConfig dictionary) with a small diff in the Err Code and _kCFStreamErrorCodeKey: 306 instead of 310 and -2094 instead of -2096.
3. The process making the request runs as a daemon.
What exactly am I doing wrong ? What am I missing ?
I was setting the kCFNetworkProxiesHTTPSPort field from the proxyConfig dictionary to a value of type NSString. After carefully reading the documentation for it I observed the following:
Key for the port number associated with the HTTPS proxy; value is a CFNumber which is the port number.
The library was encountering an object that was not a CFNumber and was using the default port for HTTPS communication to connect to the proxy instead (443). That's bad error handling imho. It was essentially silently trying to make a request using a port that I was not aware of. Changing the proxyConfig dictionary to the following fixed the problem:
NSDictionary* proxyConfig = #{
(NSString*) kCFNetworkProxiesHTTPSEnable: #(1),
(NSString*) kCFNetworkProxiesHTTPSProxy: #"127.0.0.1",
(NSString*) kCFNetworkProxiesHTTPSPort: #(33074),
};

JSON truncated when sent via NSMutableURLRequest to APS.NET MVC controller

I am building a JSON post in objective-c and sending it to an ASP.NET MVC controller.
I am building the NSMutableURLRequest as follows:
request = [[NSMutableURLRequest alloc] initWithURL:url];
NSString* jsonRequest = [NSString stringWithFormat: #"{\"collection\":\"images\",\"id\":\"%#\",\"objectjson\":%#}",response.id,response.json];
NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
I then send the request as follows:
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:backgroundQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{ ... completion code goes here
This works well most of the time. However, for very large JSON strings I occasionally get a web service error where the web service reports that it is encountering an End of File marker within the JSON. It appears that the JSON is being truncated.
I am sending the JSON to an ASP.NET MVC controller.
Does anyone have any words of wisdom on what might be happening? Are there any ASP.NET web configuration settings that perhaps I need to adjust to prevent this issue occurring.
One thing I don't understand is why it is such an intermittent problem.
This seems to be a result of bytes being lost over 3G or EDGE connection. The best idea I can come up with is to detect on the server that the content length header is larger than the request POST body and to return a status code that tells the client to try again. The client could pass a retry count on the url and the server could read it and if it's a certain value, the server would return an error code indicating that a retry should not be attempted. Ugly I know but I can't think of a better way. This is what I am going to do for my photo uploading app.
Good luck!
the problem is in the conversion to NSData
try this
NSData *requestData = [jsonRequest dataUsingEncoding:NSUTF8StringEncoding];

NSMutableURLRequest with multiple headers

I'm trying to create a synchronous REST request to an API. The API uses HTTP Basic authentication, so in addition to sending an Accept: application/json header, I need to specify the Authorization header as well with my Base64-encoded username and password pair. When I use just one header the request executes just fine (either successfully authenticating me, or specifying my content format), but when I use both headers, it seems to ignore the Authorization line and returns "HTTP Basic access denied" (presumably a 401).
So I can't for the life of me figure out whats wrong. I'm 100% sure my credentials are valid, because executing the request via REST client works just fine. I'm pretty new to Objective-C so I think perhaps there could be some kind of design pattern I'm not following. Is it valid to call setValue:forKey on an NSMutableDictionary multiple times like that? I also tried using setValue:forHTTPHeader on the request object with the same results.
Here's the code:
NSURL *url = [NSURL URLWithString:#"http://foo.com/api/v1/bar"];
NSMutableURLRequest *request= [NSMutableURLRequest requestWithURL:url];
NSMutableDictionary *headers = [NSMutableDictionary dictionary];
NSURLResponse *urlResponse;
NSError *error;
[headers setValue:#"application/json" forKey:#"Accept"];
[headers setValue:#"Basic ..." forKey:#"Authorization"];
[request setAllHTTPHeaderFields:headers];
NSData *urlData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&urlResponse
error:&error];
NSString *responseString = [[NSString alloc] initWithData:urlData
encoding:NSUTF8StringEncoding];
NSLog(#"%#",responseString);
The answer is to use:
[request addValue:#"Basic ..." forHTTPHeaderField:#"Authorization"];
Which adds another header into the request instance.

Properly handling NSURLConnection errors

I have a simple form interface set up that send username and password information to a server: (working)
NSString *postData = [NSString stringWithFormat:#"user=%#&pass=%#",[self urlEncodeValue:sysUsername],[self urlEncodeValue:password]];
NSLog(#"Post data -> %#", postData);
///
NSData* postVariables = [postData dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSMutableURLRequest* request = [[[NSMutableURLRequest alloc] init] autorelease];
NSString* postLength = [NSString stringWithFormat:#"%d", [postVariables length]];
NSURL* postUrl = [NSURL URLWithString:#"http://localhost/~csmith/cocoa/test.php"];
[request setURL:postUrl];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody: postVariables];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
NSLog(#"Post data SENT & returned -> %#", returnData);
How do I handle connection errors such as no internet connection, firewall, etc.
Also, does this method use the system-wide proxy settings? Many of my users are behind a proxy.
Thanks a lot!
First, you shouldn't use synchronous requests, use asynchronous request instead and indicate activity using indeterminate progress indicators.
When using asynchronous requests, you have to set a delegate implement delegate methods, notably:
-connectionDidFinishLoading:
-connection:didFailWithError:
From the docs:
Unless a NSURLConnection receives a cancel message, the delegate will receive one and only one of connectionDidFinishLoading:, or connection:didFailWithError: message, but never both. In addition, once either of messages are sent, the delegate will receive no further messages for the given NSURLConnection.
As for the last question:
Also, does this method use the system-wide proxy settings?
Yes, NSURLConnection uses them automatically.
You should use asynchronous request to handle proxy and network errors. This is more efficient.
To add extra check you can add reach-ability test in your code before communications. you can find reach-ability test code here