How to Validate SSL certificate - ios5

I want to validate SSL certificate in my app and i am using AFNetworking for validating certificate.
For SSL validation i am using openssl,libcrypto.a and libssl.a
My problem is that validation process was complete with NSURLConnection delegate methods, but using AFNetworking its not working.
NSURL *url = [NSURL URLWithString:#"https://www.google.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:req];
[operation setCompletionBlockWithSuccess: ^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *stringResponse = [[NSString alloc] initWithData:responseObject
encoding:NSUTF8StringEncoding];
// [self.webView loadHTMLString:stringResponse baseURL:nil];
NSLog(#"Responce-->>%#",stringResponse);
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
// [self.webView loadHTMLString:error.localizedDescription baseURL:nil];
NSLog(#"Responce-->>%#",error.localizedDescription);
}];
[operation start];
[operation setWillSendRequestForAuthenticationChallengeBlock:^(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge)
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
// By now, the OS will already have built a SecTrustRef instance for
// the server certificates; we just need to evaluate it
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecTrustResultType res;
OSStatus status = SecTrustEvaluate(serverTrust, &res);
bool verified = FALSE;
if (status == errSecSuccess && ((res == kSecTrustResultProceed) || (res == kSecTrustResultUnspecified)))
{
NSLog(#"iOS certificate chain validation for host %# passed", challenge.protectionSpace.host);
verified = verifyWithOpenSSL(serverTrust);
}
else
{
NSLog(#"iOS certificate chain validation for host %# failed", challenge.protectionSpace.host);
}
if (verified)
{
// If *both* verifications succeeded, then continue with the connection
NSURLCredential *successCredential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:successCredential
forAuthenticationChallenge:challenge];
}
else
{
[challenge.sender cancelAuthenticationChallenge:challenge];
}
} else {
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}];
This is a code of AFNetworking for validation, I don't know whether it is wrong or correct.
But this process was completely work with NSURLConnection.
So please help.

AFNetworking 2 makes this really easy. First, add your SSL certificate to your app bundle. The certificate should have a .cer extension. Second, set a securityPolicy for your operation:
operation.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
By default, AFNetworking will validate the remote certificate against the one bundled with your app, ensuring that the domain name is correct, that both were issued with the same keypair, and that the certificate chain is valid.

Related

iphone: secure restfull server "The certificate for this server is invalid

I am trying to consume secure restful service which gives error
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 “xxx.xxx.xxx.xxx” which could put your confidential information at risk."
working on xCode 4.2, where is the mistake or any step missing.
using following code
RegisterUser.f
#interface RegisterUser : UIViewController<UITextFieldDelegate,
UIScrollViewDelegate, NSURLConnectionDelegate>
RegisterUser.m
- (IBAction)SubmitBtnAction:(id)sender {
NSURL *url = [NSURL URLWithString:#"https://xx.xx.xx.xxx:8223/jaxrs/tunedoorgateway/getCountries"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] >0 && error == nil)
{
NSLog(#"Data = %#", data);
// DO YOUR WORK HERE
}
else if ([data length] == 0 && error == nil)
{
NSLog(#"Nothing was downloaded.");
}
else if (error != nil){
NSLog(#"Error = %#", error);
}
}];
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
NSLog(#"This is canAuthenticateAgainstProtectionSpace");
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
// if ([trustedHosts containsObject:challenge.protectionSpace.host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
NSLog(#"This is didReceiveAuthenticationChallenge");
// [[challenge sender] cancelAuthenticationChallenge:challenge];
}
I feel this might be because of DNS, something like your server is not registered.
Try using this for development:
Create an NSURLRequest+NSURLRequestSSLY.h file and add these lines to it
#import <Foundation/Foundation.h>
#interface NSURLRequest (NSURLRequestSSLY)
+(BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
#end
Create an NSURLRequest+NSURLRequestSSLY.m file and add these lines to it
#import "NSURLRequest+NSURLRequestSSLY.h"
#implementation NSURLRequest (NSURLRequestSSLY)
+(BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host
{
return YES;
}
#end
And don't forget to remove it before publishing as your app might get rejected.
The failure is not in your code. You are using a HTTPS server which does not provide a known certificate. If you have setup the server yourself you have to go and buy a singed certificate from one of the big certification authorities which are trusted by iOS and most other operating systems.
For development purposes you can test your REST service by ignoring the non-trusted certificate. Follow this guide for doing that: http://www.cocoanetics.com/2009/11/ignoring-certificate-errors-on-nsurlrequest/
But for production use I recommend you do not use this method since it will bring a security leak to your application. If you do ignore security you can also just use HTTP instead of HTTPS.
I have tried several way and found out it is related with apple developer centre certificate.
Upgrade your provisioning and try it again. I have tried posting data with iPad, iPhone 5 and 5S but iPhone 4.
When I have update my provisioning and installing on my iPhone 4, works with now issue.

AFNetworking authentication with web service

Having an issue which is more of a design consideration than that of code.
My iOS app interfaces with a json web service. I am using AFNetworking and my issue is basically I need the init function (which authenticates the AFHTTPClient and retrieves a token) to complete entirely before I make any additional requests (that require said token).
From the code below, I would be interested in hearing design approaches to achieving this, I would prefer to keep all requests async an alternative solution would be to make the request in initWithHost:port:user:pass synchronous (not using AFNetworking) which I am aware is bad practice and want to avoid.
DCWebServiceManager.h
#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"
#interface DCWebServiceManager : NSObject
{
NSString *hostServer;
NSString *hostPort;
NSString *hostUser;
NSString *hostPass;
NSString *hostToken;
AFHTTPClient *httpClient;
}
// Designated Initialiser
- (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass;
// Instance Methods
- (void)getFileList;
#end
DCWebServiceManager.m
#import "DCWebServiceManager.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "AFJSONRequestOperation.h"
#implementation DCWebServiceManager
- (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass
{
self = [super init];
if (self)
{
hostServer = host;
hostPort = port;
hostUser = user;
hostPass = pass;
NSString *apiPath = [NSString stringWithFormat:#"http://%#:%#/", hostServer, hostPort];
httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:apiPath]];
[httpClient setAuthorizationHeaderWithUsername:hostUser password:hostPass];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"authenticate.php" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
// Do operations to parse request token to be used in
// all requests going forward...
// ...
// ...
// Results in setting: hostToken = '<PARSED_TOKEN>'
NSLog(#"HostToken: >>%#<<", hostToken);
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"ERROR: %#", operation.responseString);
}];
[operation start];
}
return self;
}
- (void)getFileList
{
// *************************
// The issue is here, getFileList gets called before the hostToken is retrieved..
// Make the authenticate request in initWithHost:port:user:pass a synchronous request perhaps??
// *************************
NSLog(#"IN GETFILELIST: %#", hostToken); // Results in host token being nil!!!
NSString *queryString = [NSString stringWithFormat:#"?list&token=%s", hostToken];
NSMutableURLRequest *listRequest = [httpClient requestWithMethod:#"GET" path:queryString parameters:nil];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:listRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON){
NSLog(#"SUCCESS!");
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"ERROR!: %#", error);
}];
[operation start];
}
#end
ViewController.m
....
DCWebServiceManager *manager = [[DCWebServiceManager alloc] initWithHost:#"localhost" port:#"23312" user:#"FOO" pass:#"BAR"];
[manager getFileList];
// OUTPUTS
IN GETFILELIST: (nil)
HostToken: >>sdf5fdsfs46a6cawca6<<
....
...
I'd suggest subclassing AFHTTPClient and adding a +sharedInstance and property for the token.
+ (MyGClient *)sharedInstanceWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass {
static MyClient *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[... your code from the init ...]
});
return sharedInstance;
}
You can then override enqueueHTTPRequestOperationWithRequest:success:failure to check for the token before enqueueing further operations.
Additionally, you can collect the operations and enqueue them as soon as the token is set by overriding the setter for the property.
Like #patric.schenke said, you can subclass AFHTTPClient if you want to clean up some of your code, but the real issue is that you need to make another request to authenticate (if your token is nil) before making the request to getFileList.
I would recommend using blocks in the same way that AFNetworking is using blocks to remain asynchronous. Move your HTTP call into its own method and call it only when your hostToken is nil:
- (void)getFileList
{
if (self.token == nil) {
[self updateTokenThenWhenComplete:^(void){
// make HTTP call to get file list
}];
} else {
// make HTTP call to get file list
}
}
- (void)updateTokenThenWhenComplete:(void (^))callback
{
//... make HTTP request
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
self.token = responseObject.token;
callback();
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
//...
}];
}

How to proceed to next API request after successful authentication in Google Auth 2.0 in iPhone?

How to proceed to next api request after successful authication in google auth2.0 in iPhone? I got return data with keys. I need to use calendar api and task api.
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
NSString *nextURLString = [NSString stringWithFormat:#"https://www.google.com/calendar/feeds/default/allcalendars/mymail#gmail.com"];
//i just gave this url for testing. dont know which url is required here.
NSURL *nextURL = [NSURL URLWithString:nextURLString];
NSURLRequest *request = [NSURLRequest requestWithURL:nextURL];
GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request];
if (error != nil) {
// Authentication failed
} else {
// Authentication succeeded
[fetcher setAuthorizer:auth];
}
}

http media streaming over https by MVMoviePlayerViewController for iphone

I want to implement a function of http media streaming over https by MVMoviePlayerViewController. I try to access https server by NSURLConnection then open media by MVMoviePlayerViewController. I can successfully open the image url by UIWebView through this method under simulator and device and open media url like mp3 and mp4 by MVMoviePlayerViewController under simulator. The strange thing is that I get failed to open media url by MVMoviePlayerViewController under device. The following are error log.
An instance 0x4b4650 of class AVPlayerItem was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
(
Context: 0x0, Property: 0x208c20> Context: 0x0, Property: 0xa8626b0>
The following are some of my code.
-(void)viewDidLoad
{
NSString *stringPath = [NSString stringWithFormat:#"https://xxxxx/xxx.mp4";
NSURL *urlPath = [NSURL URLWithString:[stringPath stringByAddingPercentEscapesUsingEncoding:NSUTF8St ringEncoding]];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:urlPath];
NSURLConnection *httpConnection = [[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self] autorelease];
}
- (BOOL)connection : (NSURLConnection *)connection canAuthenticateAgainstProtectionSpace : (NSURLProtectionSpace *)protectionSpace
{
return YES;
}
- (void)connection : (NSURLConnection *)connection didReceiveAuthenticationChallenge : (NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] > 0)
{
return;
}
NSString *authenticationMethod = challenge.protectionSpace.authenticationMethod;
if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTru st])
{
SecTrustRef secTrustRef = challenge.protectionSpace.serverTrust;
[challenge.sender useCredential:[NSURLCredential credentialForTrust:secTrustRef forAuthenticationChallenge:challenge];
}
else
{
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:#"adminr" password:#"admin" persistence:NSURLCredentialPersistenceForSession];
[challenge.sender useCredential:newCredential forAuthenticationChallenge:challenge];
}
}
- (void)connection : (NSURLConnection *)connection didReceiveResponse : (NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
assert([httpResponse isKindOfClass:[NSHTTPURLResponse class]]);
NSLog(#"didReceiveResponse, status code : %d", httpResponse.statusCode);
NSString *stringPath = [NSString stringWithFormat:#"%#", testPath];
NSURL *urlPath = [NSURL URLWithString:[stringPath stringByAddingPercentEscapesUsingEncoding:NSUTF8St ringEncoding]];
if ((httpResponse.statusCode / 100) == 2)
{
MPMoviePlayerViewController *movieViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:urlPath];
[self presentMoviePlayerViewControllerAnimated:movieView Controller];
}
}
STEPS TO REPRODUCE
using NSURLConnection API to connect to an untrusted certification https server.
I will receive didReceiveAuthenticationChallenge (NSURLConnection delegate) and get NSURLAuthenticationMethodServerTrust authenticationMethod. I use SecTrustRef and NSURLCredential to access to untrusted certification https server.
I will receive didReceiveAuthenticationChallenge (NSURLConnection delegate) and use NSURLCredential to set username and password to access to untrusted certification https server.
I all receive didReceiveResponse (NSURLConnection delegate) and get http response statusCode 200 OK.
I try to open https media url by MPMoviePlayerViewController API.

Using Google Reader API and OAuth in iPhone app

I want to make an app that uses on the Google Reader API. But I'm finding out that there isn't an offical API for it - is there a problem using the unofficial API, in terms of App Store guidelines/approval? Would other apps (Reeder, etc) use this?
Also what is the best method for logging in? Is OAuth the preffered method? Is using Janrain a good idea?
Frankly Apple doesn't care if you use Google's unofficial API.
I worked for a customer on a RSS reader app that used Google Reader for syncing. We didn't use OAuth but the standard HTTP login which returns you a cookie where you'll have to extract a token from to use in consecutive calls to the various reader URLs.
I can post you the login code from my (old) proof of concept app.
It uses ASIHTTP and some custom string categories. The idea is to send a login request, get the response and extract the session ID/auth code from the response's cookie header. Then you can use that session ID/auth code for consecutive calls.
#pragma mark -
#pragma mark login
//this is your sessionID token you get from the login
//use this in consecutive calls to google reader
//this method returns you the header string you have to add to your request
//[request addRequestHeader: #"Cookie" value: [self sidHeader]];
- (NSString *) sidHeader
{
return [NSString stringWithFormat: #"SID=%#", [self sid]];
}
- (NSString *) authHeader
{
return [NSString stringWithFormat: #"GoogleLogin auth=%#",[self auth]];
}
//login to your google account and get the session ID
- (void) login
{
NSString *username = #"my.googlelogin#gmail.com";
NSString *password = #"mypassword123";
NSString *loginUrl = #"https://www.google.com/accounts/ClientLogin?client=NNW-Mac";
NSString *source = #"NNW-Mac"; //let's fake NetNewsWire
NSString *continueUrl = #"http://www.google.com";
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString: loginUrl]]; // log in & get cookies
[request addRequestHeader: #"User-Agent" value: #"NetNewsWire/3.2b25 (Mac OS X; http://www.newsgator.com/Individuals/NetNewsWire/)"];
[request setPostValue: username forKey: #"Email"];
[request setPostValue: password forKey: #"Passwd"];
[request setPostValue: #"reader" forKey: #"service"];
[request setPostValue: source forKey: #"source"];
[request setPostValue: continueUrl forKey: #"continue"];
[request setDelegate: self];
[request setDidFailSelector: #selector(loginRequestFailed:)];
[request setDidFinishSelector: #selector(loginRequestFinished:)];
[request start];
}
-(void)loginRequestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
//login failed
if ([responseString containsString: #"Error=BadAuthentication" ignoringCase: YES])
{
[self setLastError: [self errorWithDescription: #"Bad Username/Passsword" code: 0x001 andErrorLevel: 0x00]];
if ([delegate respondsToSelector: #selector(gReaderLoginDidFail:)])
{
[delegate gReaderLoginDidFail: self];
}
return NO;
}
//captcha required
if ([responseString containsString: #"CaptchaRequired" ignoringCase: YES])
{
[self setLastError: [self errorWithDescription: #"Captcha Required" code: 0x001 andErrorLevel: 0x00]];
if ([delegate respondsToSelector: #selector(gReaderLoginDidFail:)])
{
[delegate gReaderLoginDidFail: self];
}
return NO;
}
//extract SID + auth
NSArray *respArray = [responseString componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
NSString *sidString = [respArray objectAtIndex: 0];
sidString = [sidString stringByReplacingOccurrencesOfString: #"SID=" withString: #""];
[self setSid: sidString];
NSString *authString = [respArray objectAtIndex: 2];
authString = [authString stringByReplacingOccurrencesOfString: #"Auth=" withString: #""];
[self setAuth: authString];
//mesage delegate of success
if ([delegate respondsToSelector: #selector(gReaderLoginDidSucceed:)])
{
[delegate gReaderLoginDidSucceed: self];
}
return YES;
}
- (void)loginRequestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
//NSLog(#"login request failed with error: %#", [error localizedDescription]);
[self setLastError: error];
if ([delegate respondsToSelector: #selector(gReaderLoginDidFail:)])
{
[delegate gReaderLoginDidFail: self];
}
}
After login you can use sid and auth to forge requests to the Reader's API endpoints.
Example:
- (ASIHTTPRequest *) requestForAPIEndpoint: (NSString *) apiEndpoint
{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString: apiEndpoint]];
[request addRequestHeader: #"User-Agent" value: #"NetNewsWire/3.2b25 (Mac OS X; http://www.newsgator.com/Individuals/NetNewsWire/)"];
[request addRequestHeader: #"Cookie" value: [self sidHeader]];
[request addRequestHeader: #"Authorization" value: [self authHeader]];
return request;
}
An interesting read about Google Reader and its private API is http://timbroder.com/2007/08/google-reader-api-functions.html
Please make sure to read the latest comments :)
/edit: I updated the code to use the auth header (which google introduced in june this year). I guess this would be the place to put your OAuth token in if you would use OAuth. guess
Ive since found this: "The Google Data APIs Objective-C Client Library provides an iPhone static library, a Mac OS X framework, and source code that make it easy to access data through Google Data APIs. " code.google.com/p/gdata-objectivec-client - which is great!
It doesn't include the Reader API however (because it's not been released).
I have been able to access the API by changing (in the OAuthSampleTouch example)
NSString *scope = #"http://www.google.com/m8/feeds/";
in OAuthSampleRootViewControllerTouch.m to
NSString *scope = #"http://www.google.com/reader/api/*";
and
urlStr = #"http://www.google.com/m8/feeds/contacts/default/thin";
to
urlStr = #"http://www.google.com/reader/atom/user/-/label/Design";
where Design is a folder name - check this http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI its a great help.
Update
I have since found that this technique to be the best / lightest / less-complicated :
Native Google Reader iPhone Application