iOS 9 : SecItemCopyMatching returns successful status code but key is nil

I am adding a public key to the keyChain using SecItemAdd and then later retreive it using the SecItemCopyMatching

Now up to the point where I add the key with a PersistentRef the PersistRef is not nil, when I modify my query to get the SecKeyRef using SecItemCopyMatching, the result is nil. It is working fine on iOS 8. Find below the code and let me know if I need to add something specifically for iOS9 or am I missing something here.



NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];

if(!data){

return nil;

}

NSString *tag = @"publicTag";

NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];


// delete any old key

NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];

[publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];

[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

[publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];

SecItemDelete((__bridge CFDictionaryRef)publicKey);


[publicKey setObject:data forKey:(__bridge id)kSecValueData];

[publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)

kSecAttrKeyClass];

[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)

kSecReturnPersistentRef];

CFTypeRef persistKey = nil;

OSStatus status = ((__bridge CFDictionaryRef)publicKey, &persistKey); // status is 0 and persistKey is also not nil

if (persistKey != nil){

CFRelease(persistKey);

}

if ((status != noErr) && (status != errSecDuplicateItem)) {

return nil;

}

[publicKey removeObjectForKey:(__bridge id)kSecValueData];

[publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];

[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];

[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

SecKeyRef keyRef = nil;

status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef); // status is 0 but keyRef is nil here

if(status != noErr){

return nil;

}

return keyRef;



it returns the keyRef Object in iOS 8 but returns nil iOS 9.

Replies

I experience the same issue in my old code now. it works well on iOS8 but I get nil in keyRef when run on iOS9 (built with iOS8.4SDK)


Have you found a fix for this?

I am also experiencing this issue with the exact same code.


Any guidance would be appreciated.

Folks, please read my posts on this thread and use the advice therein to check that your public key is well formed.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I'm also seeing this. Code which has worked for a long time suddenly stopped working wiht iOS 9, with no other symptoms than that SecItemCopyMatching suddenly returns nil. I have checked my key with dumpasn1.

I'm also seeing this. Code which has worked for a long time suddenly stopped working wiht iOS 9, with no other symptoms than that SecItemCopyMatching suddenly returns nil. I have checked my key with dumpasn1.

I’m happy to take a look but you’ll need to post the relevant code snippets and a hex dump of a test key that reproduces the problem.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you Eskimo!


But I think I found the bug, and it seems to be in iOS and the way it handles the (now rare case) where the exponent of the RSA key is small. I get my code to work if I change the DER-encoding of the one-byte exponent of the public key from


02 01 <exponent>


to


02 03 00 00 <exponent>


dumpasn1 doesn't like this format that the iOS 9 libraries now require, it says "Error: Integer has non-DER encoding"

BTW it seems like the non-conform encoding with superfluous bytes is backwards compatible (at least with iOS 8.4), so the change to iOS 9 consists in demanding a non-DER encoding where a DER encoding used to be allowed. Ooops.

… it seems to be in iOS and the way it handles the (now rare case) where the exponent of the RSA key is small.

Weird. Please file a bug and then post your bug number, just for the record.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I have posted a bug with number 22555015!

You might find the header stripping code at http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ fixes the problem. It certainly has for me - I was trying to add a public key that was generated by OpenSSL by a third party, and was getting the null reference. After stripping the binary key with the method shown in this blog post, iOS 9 is happy to accept the key and gives me a valid key reference back.

Just encountered an issue with storing public keys on iOS 9. The link you provided was exactly what I needed to fix it. Thanks!

Just FYI, as of iOS 10 you no longer need to use the hackish

SecItemAdd
approach to create a key from its key bytes; you can now create the key directly using
SecKeyCreateWithData
.

You do, however, still need the code that strips the ASN.1

SubjectPublicKeyInfo
header to reveal the
RSAPublicKey
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"