From 24136b7f1499cc7821af63cd9b8b5a5b665a6b0b Mon Sep 17 00:00:00 2001 From: Mugunth Date: Tue, 6 Dec 2011 21:20:15 +0800 Subject: [PATCH] Added support for Client certifcate and server trust authentication, doesn't work and is not yet tested --- MKNetworkKit/MKNetworkEngine.m | 5 +- MKNetworkKit/MKNetworkOperation.h | 21 ++++++++ MKNetworkKit/MKNetworkOperation.m | 89 ++++++++++++++++++++++--------- README.mdown | 23 ++++++++ 4 files changed, 111 insertions(+), 27 deletions(-) diff --git a/MKNetworkKit/MKNetworkEngine.m b/MKNetworkKit/MKNetworkEngine.m index e261895..7241cf0 100644 --- a/MKNetworkKit/MKNetworkEngine.m +++ b/MKNetworkKit/MKNetworkEngine.m @@ -91,15 +91,14 @@ - (id) initWithHostName:(NSString*) hostName customHeaderFields:(NSDictionary*) DLog(@"Engine initialized with host: %@", hostName); self.hostName = hostName; self.reachability = [Reachability reachabilityWithHostname:self.hostName]; - [self.reachability startNotifier]; - + [self.reachability startNotifier]; } if([headers objectForKey:@"User-Agent"] == nil) { NSMutableDictionary *newHeadersDict = [headers mutableCopy]; NSString *userAgentString = [NSString stringWithFormat:@"%@/%@", - [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], + [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey]]; [newHeadersDict setObject:userAgentString forKey:@"User-Agent"]; self.customHeaders = newHeadersDict; diff --git a/MKNetworkKit/MKNetworkOperation.h b/MKNetworkKit/MKNetworkOperation.h index 055d916..4722e1a 100644 --- a/MKNetworkKit/MKNetworkOperation.h +++ b/MKNetworkKit/MKNetworkOperation.h @@ -34,6 +34,8 @@ typedef void (^MKNKImageBlock) (NSImage* fetchedImage, NSURL* url, BOOL isInCach #endif typedef void (^MKNKErrorBlock)(NSError* error); +typedef void (^MKNKAuthBlock)(NSURLAuthenticationChallenge* challenge); + /*! @header MKNetworkOperation.h @abstract Represents a single unique network operation. @@ -140,6 +142,25 @@ typedef void (^MKNKErrorBlock)(NSError* error); */ -(void) setUsername:(NSString*) name password:(NSString*) password; +/*! + * @abstract Authentication methods (Client Certificate) + * @property clientCertificate + * + * @discussion + * If your request needs to be authenticated using a client certificate, set the certificate path here + */ +@property (strong, nonatomic) NSString *clientCertificate; + +/*! + * @abstract Custom authentication handler + * @property authHandler + * + * @discussion + * If your request needs to be authenticated using a custom method (like a Web page/HTML Form), add a block method here + * and process the NSURLAuthenticationChallenge + */ +@property (nonatomic, copy) MKNKAuthBlock authHandler; + /*! * @abstract Add additional header parameters * diff --git a/MKNetworkKit/MKNetworkOperation.m b/MKNetworkKit/MKNetworkOperation.m index 245588d..25c84b3 100644 --- a/MKNetworkKit/MKNetworkOperation.m +++ b/MKNetworkKit/MKNetworkOperation.m @@ -93,6 +93,8 @@ @implementation MKNetworkOperation @synthesize username = _username; @synthesize password = _password; +@synthesize clientCertificate = _clientCertificate; +@synthesize authHandler = _authHandler; @synthesize responseBlocks = _responseBlocks; @synthesize errorBlocks = _errorBlocks; @@ -261,6 +263,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder [encoder encodeObject:self.dataToBePosted forKey:@"dataToBePosted"]; [encoder encodeObject:self.username forKey:@"username"]; [encoder encodeObject:self.password forKey:@"password"]; + [encoder encodeObject:self.clientCertificate forKey:@"clientCertificate"]; self.state = MKNetworkOperationStateReady; [encoder encodeInt32:_state forKey:@"state"]; @@ -284,7 +287,7 @@ - (id)initWithCoder:(NSCoder *)decoder self.dataToBePosted = [decoder decodeObjectForKey:@"dataToBePosted"]; self.username = [decoder decodeObjectForKey:@"username"]; self.password = [decoder decodeObjectForKey:@"password"]; - + self.clientCertificate = [decoder decodeObjectForKey:@"clientCertificate"]; [self setState:[decoder decodeInt32ForKey:@"state"]]; self.isCancelled = [decoder decodeBoolForKey:@"isCancelled"]; self.mutableData = [decoder decodeObjectForKey:@"mutableData"]; @@ -309,6 +312,7 @@ - (id)copyWithZone:(NSZone *)zone [theCopy setDataToBePosted:[self.dataToBePosted copy]]; [theCopy setUsername:[self.username copy]]; [theCopy setPassword:[self.password copy]]; + [theCopy setClientCertificate:[self.clientCertificate copy]]; [theCopy setResponseBlocks:[self.responseBlocks copy]]; [theCopy setErrorBlocks:[self.errorBlocks copy]]; [theCopy setState:self.state]; @@ -719,6 +723,7 @@ -(void) cancel { [self.connection cancel]; + self.authHandler = nil; self.mutableData = nil; self.isCancelled = YES; @@ -741,10 +746,10 @@ - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)err - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { - if ((([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodDefault) || - ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodHTTPBasic) || - ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodHTTPDigest) || - ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodNTLM)) && + if (((challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault) || + (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic) || + (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest) || + (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM)) && (self.username && self.password)) { @@ -753,26 +758,62 @@ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticatio password:self.password persistence:NSURLCredentialPersistenceForSession]; - [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; + } + else if ((challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate) && self.clientCertificate) { + + NSData *certData = [[NSData alloc] initWithContentsOfFile:self.clientCertificate]; + +#warning method not implemented. Don't use client certicate authentication for now. + SecIdentityRef myIdentity; // ??? + + SecCertificateRef myCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData); + SecCertificateRef certArray[1] = { myCert }; + CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL); + CFRelease(myCert); + NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity + certificates:(__bridge NSArray *)myCerts + persistence:NSURLCredentialPersistencePermanent]; + CFRelease(myCerts); + [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; + } + else if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) { +#warning method not tested. proceed at your own risk + SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; + SecTrustResultType result; + SecTrustEvaluate(serverTrust, &result); + + if(result == kSecTrustResultProceed) { + + [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; + } + else if(result == kSecTrustResultConfirm) { + + // ask user + BOOL userOkWithWrongCert = NO; // (ACTUALLY CHEAT., DON'T BE A F***ING BROWSER, USERS ALWAYS TAP YES WHICH IS RISKY) + if(userOkWithWrongCert) { + + // Cert not trusted, but user is OK with that + [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; + } else { + + // Cert not trusted, and user is not OK with that. Don't proceed + [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } else { + + // invalid or revoked certificate + [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } + else if (self.authHandler) { + + // forward the authentication to the view controller that created this operation + // If this happens for NSURLAuthenticationMethodHTMLForm, you have to + // do some shit work like showing a modal webview controller and close it after authentication. + // I HATE THIS. + self.authHandler(challenge); } - // else if (([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) && - // self.username) { - // - // // create a NSURLCredential object based on the certificate - // } - // else if (([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust) && - // self.username) { - // - // // create a NSURLCredential object based on the certificate - // } - // else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodHTMLForm) { - // - // // Do some shit work like showing a modal webview controller and close it after authentication. - // // I HATE THIS - // } - // else if (self.authHandler) { - // self.authHandler(challenge); // forward the authentication to the view controller that created this operation - // } else { [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; } diff --git a/README.mdown b/README.mdown index f6d93cf..5b8b34b 100644 --- a/README.mdown +++ b/README.mdown @@ -54,15 +54,38 @@ Install appledoc from gentlebytes github repo run this on the command line /usr/local/bin/appledoc --project-name MKNetworkKit -v 0.8 --project-company Steinlogic --company-id com.steinlogic.mknetworkkit -o ./Documentation -i .m -s ./MKNetworkKit . +####AppleDoc License +appledoc is licensed with modified BSD license. In plain language: you're allowed to do whatever you wish with the code, modify, redistribute, embed in your products (free or commercial), but you must include copyright, terms of usage and disclaimer as stated in the license, the same way as any other BSD licensed code. You can of course use documentation generated by appledoc for your products (free or commercial), but you must attribute appledoc either in documentation itself or other appropriate place such as your website. + +If for whatever reason you cannot agree to these terms, contact us through contact form on our about page, we'll do our best to help you out you out and find a workable solution! + +Copyright (c) 2009-2011, Gentle Bytes All rights reserved. + +Redistribution and use in source, binary forms and generated documentation, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +Redistributions of documentation generated by appledoc must include attribution to appledoc, either in documentation itself or other appropriate media. + +Neither the name of the appledoc, Gentle Bytes nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Gentle Bytes appledoc@gentlebytes.com + --- ###TODO * Method to freeze blocks (alternative techniques probably) * Multiple encoding types in URL +* Client certificate based encoding * Fix AppleDoc warnings * Example code for Mac * Cache strategy for Mac * Scheduling in current runloop + * Caching NSURLConnection based on Keep-Alive I'll be working on this soon. But you can start forking it and getting it right