PhoneGap is a stickler for 100% valid SSL certs

[bk note: the following post was written in November of 2011. As technology continuously evolves, the solution below may very well have changed/become obsolete.]

I thought I’d share this little nugget, since it’s not very well covered within the tomes of the intertubes.

I was using PhoneGap and SenchaTouch to create a native iOS app. The app required a local form to be posted to a remote server. I couldn’t use Sencha Touch’s built-in AJAX functionality, since the aformentioned remote service expected its data to be in a custom format. I had to roll my own using the Javascript XMLHttpRequest method.

But that’s beside the point, as I believe you’d encounter this gremlin even when using the methods built into Sencha Touch.

Anyhoo. The problem surfaces when the app tries to make an AJAX request using the HTTPS protocol. Turns out that if the remote site’s SSL certificate is anything less than 100% valid (self-signed, problem in the trust chain, etc) PhoneGap will simply abort the request with an http status code of 0. I spent a while trying to find a javascript workaround, but none was to be found.

There is a solution by Aaron Saunders floating around the intertubes. He wrote it back in 2010 and it has (as he admits) plenty of typos. I spent a little bit of time bringing the code up to date and so I thought I’d share my updates to his code. Most of it has to do with ditching the deprecated <PhoneGapCommand> PhoneGap class and switching over to using the new <PGPlugin> class.

Note:  I call this a workaround, not a solution.  This workaround tells iOS to make the connection & disregard any problems it might find with the security cert. THIS CAN BE A BAD THING!  I recognize the pitfalls and I consider the workaround acceptable FOR ME, since the final apps will be installed on a small controlled population, and we’re not transferring any private data.  You can’t (and shouldn’t) hold me liable for any problems you might have resulting from the use of this code.  Know the risk, and use at your own risk.

 

PixAuth.h
//
// PixAuth.h
// phoneGap1
//
// Created by Aaron saunders on 8/25/10.
// Copyright 2010 clearlyINNOVATIVE. All rights reserved.
// Modified 2011 by briankiel.

// #import
#import <PhoneGap/PGPlugin.h>

@interface PixAuth : PGPlugin {
NSURLConnection *aConnection;
NSURLResponse *connectionResponse;
NSMutableData *connectionData;

}

– (void)loginWithBadCert:(NSMutableArray*)params withDict:(NSMutableDictionary*)options;

@end

PixAuth.m
//
// PixAuth.m
// phoneGap1
//
// Created by Aaron saunders on 8/25/10.
// Copyright 2010 clearlyINNOVATIVE. All rights reserved.
// Modified 2011 by briankiel.

#import “PixAuth.h”

@implementation PixAuth

-(void) loginWithBadCert:(NSMutableArray*)params withDict:(NSMutableDictionary*)options {
// modified this script from its original version, as the new version of phonegap.exec passes the arguments starting at index 1
// index 0 contains the plugin.method being called.
NSLog(@”in loginWithBadCert. url is: %@”, [params objectAtIndex:1]);
NSURL *serverURL = [NSURL URLWithString:[NSString stringWithFormat:@”%@”,[params objectAtIndex:1]]];
NSLog(@”url is: %@”, serverURL);

NSURLRequest *connectionRequest = [NSURLRequest requestWithURL:serverURL
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
NSURLConnection * aConnection = [[NSURLConnection alloc] initWithRequest:connectionRequest delegate:self];
connectionData = [[NSMutableData alloc] init];
}

/* NSURLConnection Delegate Methods */

– (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@”in didReceiveResponse “);
[connectionResponse release];
connectionResponse = [response retain];
[connectionData setLength:0];
}

– (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(@”in didReceiveData “);
[connectionData appendData:data];
}

//
// this method, and the one below get the magic rolling and allow my ajax request
// to function as expected, even with the bogus certificate
//
– (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return YES;
}

//
// my application requires credentials, so they are stored in the application settings
//
– (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@”in didReceiveAuthenticationChallange “);

NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:[[NSUserDefaults standardUserDefaults] stringForKey:@”emailAddress”]
password:[[NSUserDefaults standardUserDefaults] stringForKey:@”password”]
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
}

– (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(@”in connectionDidFinishLoading “);
NSString *string = [[[NSString alloc] initWithData:connectionData encoding:NSUTF8StringEncoding] autorelease];
NSLog(@”%@”, string);
}

– (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@”in didFailWithError “);
NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);
}

– (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil; // Never cache
}

@end

Place the two files above in your PhoneGap “plugins” folder, and make sure you add the plugin to your phonegap.plist file in xCode.

 

Then just add the following line to your “onDeviceReady” javascript method:

PhoneGap.exec(onSuccessHandler, onErrorHandler, "PixAuth","loginWithBadCert",["https://your.domain.with.a.bad.cert"]);

 

And you’re ready to roll.

Use it wisely.