Keychain errSecItemNotFound

Hello Apple Developer: I encountered some issues during development. I encrypted the secret key and stored it in the Keychain, but it failed when I tried to read it. I would like to ask if there is any problem with the code I wrote. Below is my code, including the storage and retrieval

NSMutableDictionary *query = [[NSMutableDictionary alloc] initWithObjectsAndKeys:(id)kSecClassGenericPassword,(id)kSecClass,

                                  serviceID,(id)kSecAttrService,

                                  @YES,(id)kSecReturnData,nil];

    

    CFTypeRef dataTypeRef = NULL;

    NSLog(@"SecItemCopyMatching");

    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(query), &dataTypeRef);

    NSLog(@"SecItemCopyMatching end status = %d",status);

    if (status == errSecSuccess) {

        

        *privateData = CFBridgingRelease(dataTypeRef);

        return 0;

        

    }else{

        

        return status;

        

    }
NSMutableDictionary *attributespri = [[NSMutableDictionary alloc] initWithObjectsAndKeys:

        (id)kSecClassGenericPassword, (id)kSecClass,

        serviceID,                   (id)kSecAttrService,

        outData,                     (id)kSecValueData,

    nil];



    CFTypeRef dataRef = NULL;

    OSStatus priStatus = SecItemAdd((__bridge CFDictionaryRef)attributespri, &dataRef);



    if (dataRef) CFRelease(dataRef);

    return priStatus == noErr;
Answered by DTS Engineer in 884212022

I’m gonna start you out with SecItem: Fundamentals. It explains how you should think about the keychain. Once you internalise that, a lot of its behaviour makes sense.

As to what’s going on here, it’s hard to say without more context. My first question is: What platform are you on?

This matters because, if you’re on the Mac, the keychain has a lot of additional complexity. TN3137 On Mac keychain APIs and implementations explains the details.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I’m gonna start you out with SecItem: Fundamentals. It explains how you should think about the keychain. Once you internalise that, a lot of its behaviour makes sense.

As to what’s going on here, it’s hard to say without more context. My first question is: What platform are you on?

This matters because, if you’re on the Mac, the keychain has a lot of additional complexity. TN3137 On Mac keychain APIs and implementations explains the details.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I am using the iOS platform. I would like to ask: when using kSecClassGenericPassword, is the kSecAttrAccount attribute required? I did not set kSecAttrAccount and kSecAttrAccessible when saving data on older iOS versions. Will this cause problems when querying Keychain items on iOS 26?

I am using the iOS platform.

Cool.

iOS only has one keychain implementation, equivalent to the data protection keychain on macOS, and that simplifies your life.

I did not set kSecAttrAccount and kSecAttrAccessible when saving data on older iOS versions.

kSecAttrAccessible is unlikely to be the problem:

  • Your current code leaves out that attribute from the query and return dictionary you pass to SecItemCopyMatching. In that context a missing attribute is treated as wildcard, so it’ll make any item regardless of what it’s kSecAttrAccessible value is.
  • If the value is not present in the add dictionary you pass to SecItemAdd, the system will default to using no value for kSecAttrAccessible and, assuming there’s no kSecAttrAccessGroup attribute, it’ll place the item in your app’s default keychain access group. Sharing access to keychain items among a collection of apps explains how that’s calculate.

kSecAttrAccount is trickier. It’s a critical contributor to item uniqueness for generic password keychain items, along with kSecAttrService. If you’re managing a single keychain item then it’s best to set both of these to some hard-coded string. If you haven’t done that in the past then the values will actually end up being empty, which complicates matters.

To sort this out you need to know what exactly got recorded by previous versions of your code. I explain how to do that in the Lost Keychain Items and Lost Keychain Items, Redux sections of SecItem: Pitfalls and Best Practices. Alternatively, you can reset your keychain using the technique described in the Starting from Scratch section of that same post.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hello, I have already read SecItem: Pitfalls and Best Practices. If I do not set kSecAttrAccount right now, is it possible that I cannot read the keychain items correctly? In addition, if Keychain Sharing is enabled for the app, but I do not specify kSecAttrAccessGroup when saving keychain data, will the storage location be the same as when Keychain Sharing is disabled?My data stores a string of encrypted secret keys.

If I do not set kSecAttrAccount right now, is it possible that I cannot read the keychain items correctly?

It’s hard to answer this because you’ve phrased it as a negative question. So, lemme rephrase it:

  • If I’m using the data protection keychain (the default on iOS, something you have to opt in to on macOS),
  • And I create a generic password keychain item without specifying kSecAttrAccount,
  • Will I be able to access that item’s contents using SecItemCopyMatching?

The answer to that is “Yes.”

At the end of this post I’ve included some code that I used to test this. (Sorry it’s in Swift. I built this by cobbling together various existing bits of code that were all written in Swift, and I don’t have time today to convert them to Objective-C.)

Having said that, my advice is that, when you creating a generic password item you always populate both kSecAttrService and kSecAttrAccount. If you don’t have a sensible value to use for one of these, hard code some fixed string.

One of the reasons for that recommendation is that there’s a fundamental ambiguity between:

  • Not supplying an attribute
  • Supplying an empty attribute

This breaks down as follows:

  • In an add dictionary, not supplying an attribute is equivalent to supplying the empty attribute.
  • In a pure query dictionary and a query and return dictionary, not supplying an attribute acts as a wildcard [1]. In contrast, if you supply the empty attribute you’ll only match items where that attribute is empty.
  • In an update dictionary, not supplying an attribute leaves the attribute unchanged.

Also, if you call SecItemCopyMatching and pass in kSecReturnAttributes, the system might omit an attribute from the returned dictionary if its value is empty.

Oh, and this is all assuming that you’re using the data protection keychain. My experience is that, on macOS, the file-based keychain shim tends to run into problems when you explore subtle edge cases like this.

This is a huge field of potential pitfall, and hence my advice.

if Keychain Sharing is enabled for the app, but I do not specify kSecAttrAccessGroup when saving keychain data, will the storage location be the same as when Keychain Sharing is disabled?

That depends on how you set up keychain sharing. Sharing access to keychain items among a collection of apps explains:

  • How the system uses entitlements to determine your keychain access group list
  • And that the first item in that list becomes your default keychain access group

When you enable the Keychain Sharing capability, that changes the keychain access group list. If you want the default keychain access group to stay the same, it’s important to check the new list against the old list and take action if the first item changes. The Lost Keychain Items section of SecItem: Pitfalls and Best Practices walks you through a concrete example of that [2].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] There are exceptions here. For example, on macOS you don’t get wildcard behaviour if you omit the kSecAttrSynchronizable attribute. Rather, you must explicitly pass in the kSecAttrSynchronizableAny value.

[2] I’ve just updated that example to cover the most common case, that is, when folks add the Keychain Sharing capability to an app that previously didn’t use it. So thanks for asking questions like this! It helps me reassess whether my advice actually addresses the problems that folks see in practice.


enum KeychainBasics {

    static func addPassword(_ password: String) throws {
        try secCall { SecItemAdd([
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: "keychain-basics-service",
            // kSecAttrAccount: "keychain-basics-account",
            kSecValueData: Data(password.utf8),
        ] as NSDictionary, nil) }
    }
    
    static func copyPassword() throws -> String {
        let copyResult = try secCall { SecItemCopyMatching([
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: "keychain-basics-service",
            // kSecAttrAccount: "keychain-basics-account",
            kSecAttrAccount: "",
            kSecReturnData: true,
        ] as NSDictionary, $0) } as! Data
        // The following will ‘repair’ malformed UTF-8 by replacing it with the
        // U+FFFD REPLACEMENT CHARACTER.
        return String(decoding: copyResult, as: UTF8.self)
    }
    
    static func reset() throws {
        try secCall { SecItemDelete([
            kSecClass: kSecClassGenericPassword,
        ] as NSDictionary) }
    }
}
Keychain errSecItemNotFound
 
 
Q