Category Archives: Code
title-icon-code

Verifying plugin bundles using code signing

Code signing your apps is a great way of verifying that they haven’t been messed with before an end user executes your code, but you can also utilise the same techniques to implement a very secure plugin system for your apps.

If your application implements a plugin architecture based on NSBundles, you can use the following snippet in your loading code to ensure that each plugin is signed by an appropriate certificate.

You will need to create a certificate authority, and then have that sign a code signing certificate for your plugins. Doing it this way means that you can provide valid signing certificates to third parties to create plugins for your app if desired. TechRepublic have a good article on creating a CA in Keychain Access on your Mac.

Update (14 July 2012): Updated the snippet to be ARC compliant.

- (BOOL) validateSignature:(NSBundle*)pluginBundle {
NSTask * task = [[NSTask alloc] init];
NSPipe * pipe = [NSPipe pipe];
NSFileHandle * handle = [pipe fileHandleForReading];
NSData * taskData;
NSString * taskString;
NSArray* args = [NSArray arrayWithObjects:@"--verify", [NSString stringWithFormat:@"-R=anchor = \"%@\"", [[NSBundle mainBundle] pathForResource:@"BlargsoftCodeCA" ofType:@"cer"]], [pluginBundle bundlePath], nil];
[task setLaunchPath:@"/usr/bin/codesign"];
[task setStandardOutput:pipe];
[task setStandardError:pipe];
[task setArguments:args];
[task launch];
[task waitUntilExit];
taskData = [handle readDataToEndOfFile];
taskString = [[NSString alloc] initWithData:taskData encoding:NSASCIIStringEncoding];

if ([task terminationStatus] != 0) {
// Something went wrong. Check for specific errors

if ([taskString rangeOfString:@"modified"].length > 0 || [taskString rangeOfString:@"a sealed resource is missing or invalid"].length > 0) {
// The plugin has been modified or resources removed since being signed. You probably don't want to load this.
NSLog(@"Plugin modified - not loaded"); // log a real error here
} else if ([taskString rangeOfString:@"failed to satisfy"].length > 0) {
// The plugin is missing resources since being signed. Don't load.
// throw an error
NSLog(@"Plugin not signed by correct CA - not loaded"); // log a real error here
} else if ([taskString rangeOfString:@"not signed at all"].length > 0) {
// The plugin was not code signed at all. Don't load.
NSLog(@"Plugin not signed at all - don't load."); // log a real error here
} else {
// Some other codesign error
}

return FALSE;

} else {

// The plugin passed validation!
return TRUE;

}

}