If you have this problem, most likely this is because the private key you created is not actually stored in the keychain. I realized this when stopping and restarting the application and signing the message did not work.
So, here are my methods to make this work.
This generates a key pair
- (void)generateKeyPair:(NSUInteger)keySize { OSStatus sanityCheck = noErr; publicKeyRef = NULL; privateKeyRef = NULL; LOGGING_FACILITY1( keySize == 512 || keySize == 1024 || keySize == 2048, @"%d is an invalid and unsupported key size.", keySize );
** EDIT **
IOS 9 introduces a new feature called Secure Enclave. If you want to generate a key that will be stored there and only there, you will need to use the 256-bit EC key, as this is the only type supported by the enclave. Instead, keyPairDict will look like this:
NSDictionary *keyPairDict = @{ (__bridge id)kSecAttrTokenID: (__bridge id)kSecAttrTokenIDSecureEnclave, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC,
I know the parameters are correct, but I have not tested Secure Enclave yet, so let me know if this does not work for some reason.
Also, for your reference: the 256-bit EC key is equivalent to the 3072-bit RSA key.
The query used to retrieve the key below will also differ:
NSDictionary *queryKey = @{ (__bridge id) kSecClass : (__bridge id) kSecClassKey, (__bridge id) kSecAttrApplicationTag : tempTag, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeEC };
Since Secure Enclave is secure, you most likely will not be able to get the private key bit. Most likely, you can create a link. But you still don't need to process the secret key data.
** END EDIT **
This method extracts the actual bits from the keychain, not just the link
- (NSData *)getKeyBitsFromKey:(SecKeyRef)givenKey { static const uint8_t publicKeyIdentifier[] = "com.sample.temp"; NSData *tempTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)]; NSDictionary *queryKey = @{ (__bridge id) kSecClass : (__bridge id) kSecClassKey, (__bridge id) kSecAttrApplicationTag : tempTag, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA };
This method stores a bit in the keychain.
- (void)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate { OSStatus sanityCheck = noErr; NSData *tag; id keyClass; if (isPrivate) { tag = privateTag; keyClass = (__bridge id) kSecAttrKeyClassPrivate; } else { tag = publicTag; keyClass = (__bridge id) kSecAttrKeyClassPublic; } NSDictionary *saveDict = @{ (__bridge id) kSecClass : (__bridge id) kSecClassKey, (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA, (__bridge id) kSecAttrApplicationTag : tag, (__bridge id) kSecAttrKeyClass : keyClass, (__bridge id) kSecValueData : key, (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize], (__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize], (__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse, (__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue, (__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse, (__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue, (__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse, (__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue, (__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse }; SecKeyRef savedKey = NULL; sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey); if (sanityCheck != errSecSuccess) { LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck); } }
And then you subscribe like this:
- (NSData *)getSignatureBytes:(NSData *)plainText { OSStatus sanityCheck = noErr; NSData *signedHash = nil; uint8_t *signedHashBytes = NULL; size_t signedHashBytesSize = 0; SecKeyRef privateKey = NULL; privateKey = [self getKeyRef:YES]; signedHashBytesSize = SecKeyGetBlockSize(privateKey);