Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SecItemCopyMatching returns nil value without any error

I'm trying to add a RSA public key to my iPhone's keychain using CryptoExercise's SecKeyWrapper addPeerPublicKey:keyBits: method. The logic of this method is that it first tries to add the key to the keychain and if it already there (sanityCheck==errSecDuplicateItem) it tries to retrieve this key from the keychain by calling SecKeyItemCopyMatching().

That is exactly what happens in my case: the key is already in the keychain, so the call to SecKeyItemAdd() returns errSecDuplicateItem.

Then it tries to retrieve the existing key but SecKeyItemCopyMatching() returns 0 (indicating that there was no error) but the second parameter (peerKeyRef) remains desesperately nil.

How is this possible? What is wrong with this?

Here is the code of [SecKeyWrapper addPeerPublicKey:keyBits:] from CryptoExercise sample for reference:

- (SecKeyRef)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKey {
    OSStatus sanityCheck = noErr;
    SecKeyRef peerKeyRef = NULL;
    CFTypeRef persistPeer = NULL;

    LOGGING_FACILITY( peerName != nil, @"Peer name parameter is nil." );
    LOGGING_FACILITY( publicKey != nil, @"Public key parameter is nil." );

    NSData *peerTag = [[NSData alloc] initWithBytes:(const void *) [peerName UTF8String] length:[peerName length]];
    NSMutableDictionary *peerPublicKeyAttr = [[NSMutableDictionary alloc] init];

    [peerPublicKeyAttr setObject:(__bridge id) kSecClassKey forKey:(__bridge id) kSecClass];
    [peerPublicKeyAttr setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id) kSecAttrKeyType];
    [peerPublicKeyAttr setObject:peerTag forKey:(__bridge id) kSecAttrApplicationTag];
    [peerPublicKeyAttr setObject:publicKey forKey:(__bridge id) kSecValueData];
    [peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecReturnPersistentRef];

    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *) &persistPeer);

    // The nice thing about persistent references is that you can write their value out to disk and
    // then use them later. I don't do that here but it certainly can make sense for other situations
    // where you don't want to have to keep building up dictionaries of attributes to get a reference.
    //
    // Also take a look at SecKeyWrapper's methods (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)key
    // & (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef.

    LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecDuplicateItem, @"Problem adding the peer public key to the keychain, OSStatus == %ld.", sanityCheck );

    if (persistPeer) {
        peerKeyRef = [self getKeyRefWithPersistentKeyRef:persistPeer];
    } else {
        [peerPublicKeyAttr removeObjectForKey:(__bridge id) kSecValueData];
        [peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecReturnRef];
        // Let's retry a different way.
        sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *) &peerKeyRef);
    }

    LOGGING_FACILITY1( sanityCheck == noErr && peerKeyRef != NULL, @"Problem acquiring reference to the public key, OSStatus == %ld.", sanityCheck );

    if (persistPeer) CFRelease(persistPeer);
    return peerKeyRef;
}
like image 716
Sebastien Avatar asked Jul 12 '13 19:07

Sebastien


1 Answers

I had the same problem and I assume you try to import a RSA key which was not exported from another iOS device.

The reason seems to be an incompatible key format - in detail iOS expects some ASN1 header NOT to be set. Why the functions returns OK is for me only explainable with a bug...

Check out the code at http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ this is the correct solution and works for me - so thanks to Chris Luke

like image 113
Haschmi Avatar answered Oct 17 '22 01:10

Haschmi