Request NSURLSession with SSL .cert and .key files - objective-c

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.

Related

NSURLSession and API

I am extremely confused with the NSUrlSession and the API. This is my first time trying to use an API so please explain this in the simplest form possible.
I found an API which gets the weather, I have made a string for the weather location. then did all the NSUrl / nsurlrequest. My goal is to output everything so I can see the keys of that API. Heres what I have so far but all It displays is 'Program ended with exit code 0'
I don't really know what is happening during the NSUrlsession because I learned how to use API with the NSUrlConnection via a youtube video.
NSString *location = #"London";
NSString *weatherString = [NSString stringWithFormat:#"https://api.openweathermap.org/data/2.5/weather?q=%#", location];
NSURL *weatherURL = [NSURL URLWithString:weatherString];
NSURLRequest *request = [NSURLRequest requestWithURL:weatherURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error){
NSDictionary *weatherDictionary = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];
NSLog(#"%#", [weatherDictionary description]);
}];
It's hard to tell from this snippet, but one or more of the following problems are likely causing your issues:
You are retaining a reference to that task somewhere, right?
From the documentation for dataTaskWithRequest, you need to call [task resume] to actually start the task.
That URL won't work, because the api.openweathermap.org site doesn't support HTTPS. You'll need to change it to http, and possibly add an exception in the app's Info.plist to allow non-secure connections (they're disabled by default for new apps).
After you fix all that, you'll need an API key for the request to actually succeed.

AFNetworking upload task with credentials (HTTP Basic Auth)

I need to upload video files from my app to a server. I tried doing so via [AFHTTPRequestOperationManager post:parameters:success:failure] but unfortunately kept getting request timeouts. I'm now trying something similar to Creating an Upload Task from the AF Docs.
I read on SO and the AF Docs about setSessionDidReceiveAuthenticationChallengeBlock: and tried to implement the whole upload malarky as follows:
__block ApiManager *myself = self;
// Construct the URL
NSString *strUrl = [NSString stringWithFormat:#"%#/%#", defaultUrl, [self getPathForEndpoint:endpoint]];
NSURL *URL = [NSURL URLWithString:strUrl];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:#"POST"];
// Build a session manager
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// Set authentication handler
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *credential) {
*credential = myself.credentials;
return NSURLSessionAuthChallengeUseCredential;
}];
// Create the upload task
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
[myself endpoint:endpoint returnedFailure:error];
} else {
[myself endpoint:endpoint returnedSuccess:responseObject];
}
}];
// and run with it
[uploadTask resume];
The myself.credentials object has been set previously to have the correct username and password. Whenever this request fires, I get 401 unauthorised as a response. I tried putting NSLog(#"CHALLENGE") inside the challenge block above, but it never seems to get called, so AFNetworking isn't giving me a way to supply credentials. I know that this works perfectly well on the server side because I've tested it with Postman.
How can I get AFNetworking to let me supply credentials for HTTP Basic Auth with this upload task?
I'm not sure about AFNetworking's setSessionDidReceiveAuthenticationChallengeBlock, but as long as you have an NSMutableURLRequest you may set the Authorization HTTP Header directly on the request object:
[request setValue:base64AuthorizationString forHTTPHeaderField:#"Authorization"];
Keep in mind that the value must be base64 representation of the username:password string.
Otherwise, for GET, POST, etc. requests on a session manager, you may set the credentials on the request serializer used by the session manager. See:
[AFHTTPRequestSerializer setAuthorizationHeaderFieldWithUsername:password:]
[AFHTTPRequestSerializer clearAuthorizationHeader]

Error in GET on https ?? Can't set NSLocalizedRecoverySuggestion

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.

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.

NSURLCredential not working for synchronuos request in iphone

I am using Synchronous request and passing the credentials but I am getting authentication error in response. Below is my code of request and response from server.
Request:-
NSURLCredential *userCredentials = [NSURLCredential credentialWithUser:#"username"
password:#"paswd"
persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:#"http://webaddress.inc.com"
port:80
protocol:#"http"
realm:#"webaddress.inc.com"
authenticationMethod:nil];
[[NSURLCredentialStorage sharedCredentialStorage] setCredential:userCredentials
forProtectionSpace:space];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://webaddress.inc.com"]
cachePolicy:NSURLCacheStorageNotAllowed
timeoutInterval:30];
NSURLResponse *response;
NSError *error;
NSData *returnData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
/////////// Response
401 Authorization Required
Note: If I am sending the request with async request it is working fine as in that case
didReceiveAuthenticationChallenge is giving them the required credentials when asked for.
I am sure there might be something missing in my code.
Waiting for your valuable inputs.
Have you ever tried ASIHTTPRequest which makes easier http operations in IPhone enviroment. And It has really simple and good documentation. I used to use it inside of my projects, It was working awesome.