Skip to content

Instantly share code, notes, and snippets.

@adil-hussain-84
Last active June 6, 2017 16:13
Show Gist options
  • Save adil-hussain-84/0b74fc7b34f3df11b125d3e1cb1b8257 to your computer and use it in GitHub Desktop.
Save adil-hussain-84/0b74fc7b34f3df11b125d3e1cb1b8257 to your computer and use it in GitHub Desktop.
Helper class which adds/gets/deletes RSA public keys in the iOS Keychain for signature verification.
#import <Foundation/Foundation.h>
@interface MJRSecKeyRefProvider : NSObject
/**
* @param tag the identifier with which to save keys in the Keychain.
*/
- (instancetype)initWithTag:(NSString *)tag;
/**
* Saves the provided public key in the Keychain
* and provides a SecKeyRef object that links to it.
*
* @param publicKeyData the encoded public key.
* @param keySizeInBits the size of the key in bits (i.e. 2048, 4096 etc).
* @return a SecKeyRef object for the key after saving the key in the keychain, or nil in case of error.
*/
- (SecKeyRef)provideForPublicKeyData:(NSData *)publicKeyData
keySizeInBits:(NSNumber *)keySizeInBits;
@end
#import "MJRSecKeyRefProvider.h"
@implementation MJRSecKeyRefProvider {
NSString *_tag;
}
- (instancetype)initWithTag:(NSString *)tag {
if (self = [super init]) {
_tag = tag;
}
return self;
}
- (SecKeyRef)provideForPublicKeyData:(NSData *)publicKeyData
keySizeInBits:(NSNumber *)keySizeInBits {
BOOL deletePublicKeySuccess = [self deletePublicKey];
if (!deletePublicKeySuccess) {
return nil;
}
BOOL savePublicKeySuccess = [self savePublicKeyData:publicKeyData
keySizeInBits:keySizeInBits];
if (!savePublicKeySuccess) {
return nil;
}
return [self getPublicKey];
}
- (BOOL)savePublicKeyData:(NSData *)publicKeyData
keySizeInBits:(NSNumber *)keySizeInBits {
NSMutableDictionary *query = [self getQuery];
query[(__bridge id) kSecAttrEffectiveKeySize] = keySizeInBits;
query[(__bridge id) kSecAttrKeySizeInBits] = keySizeInBits;
query[(__bridge id) kSecAttrCanDecrypt] = (__bridge id) kCFBooleanFalse;
query[(__bridge id) kSecAttrCanDerive] = (__bridge id) kCFBooleanFalse;
query[(__bridge id) kSecAttrCanEncrypt] = (__bridge id) kCFBooleanFalse;
query[(__bridge id) kSecAttrCanSign] = (__bridge id) kCFBooleanFalse;
query[(__bridge id) kSecAttrCanVerify] = (__bridge id) kCFBooleanTrue;
query[(__bridge id) kSecAttrCanUnwrap] = (__bridge id) kCFBooleanFalse;
query[(__bridge id) kSecAttrCanWrap] = (__bridge id) kCFBooleanFalse;
query[(__bridge id) kSecValueData] = [self stripPublicKeyHeader:publicKeyData];
SecKeyRef secKeyRef = NULL;
OSStatus result = SecItemAdd((__bridge CFDictionaryRef) query, (CFTypeRef *) &secKeyRef);
return result == errSecSuccess;
}
- (SecKeyRef)getPublicKey {
NSMutableDictionary *query = [self getQuery];
query[(__bridge id) kSecReturnRef] = (__bridge id) kCFBooleanTrue;
SecKeyRef secKeyRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, (CFTypeRef *) &secKeyRef);
if (status != errSecSuccess) {
return nil;
}
return secKeyRef;
}
- (BOOL)deletePublicKey {
NSMutableDictionary *query = [self getQuery];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);
return (status == errSecSuccess) || (status == errSecItemNotFound);
}
#pragma mark - Private methods
- (NSMutableDictionary *)getQuery {
NSData *tagData = [_tag dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *query = [NSMutableDictionary dictionary];
query[(__bridge id) kSecAttrApplicationTag] = tagData;
query[(__bridge id) kSecAttrKeyClass] = (__bridge id) kSecAttrKeyClassPublic;
query[(__bridge id) kSecAttrKeyType] = (__bridge id) kSecAttrKeyTypeRSA;
query[(__bridge id) kSecClass] = (__bridge id) kSecClassKey;
return query;
}
/**
* This method exists to strip the ASN.1 SubjectPublicKeyInfo header if present in the key.
* iOS 9 silently fails to import a key with this structure
* and does not return a SecKeyRef to the key in question.
*
* For reference, see the below:
* https://forums.developer.apple.com/thread/13748
* https://forums.developer.apple.com/thread/15129
* http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/
*/
- (NSData *)stripPublicKeyHeader:(NSData *)d_key {
if (d_key == nil) return nil;
unsigned int len = (unsigned int) [d_key length];
if (!len) return nil;
unsigned char *c_key = (unsigned char *) [d_key bytes];
unsigned int idx = 0;
if (c_key[idx++] != 0x30) return nil;
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
// PKCS #1 rsaEncryption szOID_RSA_RSA
static unsigned char seqiod[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00
};
if (memcmp(&c_key[idx], seqiod, 15)) return nil;
idx += 15;
if (c_key[idx++] != 0x03) return nil;
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
if (c_key[idx++] != '\0') return nil;
return [NSData dataWithBytes:&c_key[idx] length:len - idx];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment