SecItemAdd with kSecUseKeychain returns error -50

Hi all,

I'm working on a macOS project with C interface. And I'm trying to import my private key to a SecKeychainRef, but I always got error code -50. Would you have any advice or suggestion in it? Thanks in advance.

Here is my code:

// Get default keychain
SecKeychainRef import_keychain = NULL;
OSStatus keychain_status = SecKeychainCopyDefault(&import_keychain);

// Create key from ECC key data in X963 format
CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(default_alloc, 0, NULL, NULL);
CFDictionarySetValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
CFDictionarySetValue(parameters, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
CFDictionarySetValue(parameters, kSecAttrApplicationLabel, cfLabel);
SecKeyRef private_key= SecKeyCreateWithData(hard_code_key_ref, parameters, &key_error);

// Add seckey to key chain
CFMutableDictionaryRef secItemParams = CFDictionaryCreateMutable(default_alloc, 0, NULL, NULL);
CFDictionarySetValue(secItemParams, kSecClass, kSecClassKey);
CFDictionarySetValue(secItemParams, kSecValueRef, privateKey);
CFDictionarySetValue(secItemParams, kSecUseKeychain, import_keychain);

OSStatus key_status = SecItemAdd(secItemParams, NULL);

I also tried to test "SecItemAdd" with password value, but it also failed with -25308. I'm not sure if it is related or not. Here is the test code:


            CFStringRef server = CFStringCreateWithCString(default_alloc, "example.com", kCFStringEncodingUTF8);
            CFStringRef username = CFStringCreateWithCString(default_alloc, "username", kCFStringEncodingUTF8);
            CFStringRef password = CFStringCreateWithCString(default_alloc, "password", kCFStringEncodingUTF8);
            CFMutableDictionaryRef secItemParams = CFDictionaryCreateMutable(default_alloc, 0, NULL, NULL);
            CFDictionarySetValue(secItemParams, kSecClass, kSecClassInternetPassword);
            CFDictionarySetValue(secItemParams, kSecValueData, password);
            CFDictionarySetValue(secItemParams, kSecAttrAccount, username);
            CFDictionarySetValue(secItemParams, kSecAttrServer, server);
            
            CFDictionarySetValue(secItemParams, kSecUseKeychain, import_keychain);
            CFDictionarySetValue(secItemParams, kSecAttrAccessible, kSecAttrAccessibleAlways);

            OSStatus key_status = SecItemAdd(secItemParams, NULL);

The above code failed with "OSStatus -25308 : User interaction is not allowed." Any advice is welcomed. Thank you!

Error -50 is errSecParam, which is not helpful. For the moment I recommend that you focus on the password case.

errSecInteractionNotAllowed usually means that the keychain needs to interact with the user to complete this task but user interaction isn’t allowed because of the execution context. What execution context are you running this code in?

I recommend that you create a generic password than an Internet one. The latter is unnecessarily complex. For a generic password you just need to populate kSecClass, kSecAttrService, kSecAttrAccount, and kSecValueData.

To get started I recommend that you create a tiny test app and debug your code in that context.

ps kSecAttrAccessible is not relevant if you’re using the file-based keychain. See On Mac Keychains for the tangled backstory about keychains on the Mac.

Share and Enjoy

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

Thank you so much for the reply!

As -50 is a vague error code, I'm lost what should I look into. I know that I got the error after I added the kSecUseKeychain attribute. And looking at the documentation, I failed to find more information related to the keychain attribute Item Attribute Keys and Values. Would you have any information related?

Would you mind also providing more information about execution context? I'm not sure what is it about.

For the password example, I tried with a generic password, it pop out the same error: errSecInteractionNotAllowed. I guess the issue was with the execution context?

Would you mind also providing more information about execution context? I'm not sure what is it about.

I’m referring to the circumstances under which you code runs. Is this a standard GUI app? One launched by the user from the Finder? Or run using Xcode’s Product > Run? Or something that runs in the background, like a launchd daemon or agent?

For your initial bring up I recommend that you put this code into a standard GUI app. If you’re not already doing that, create a new test app from one of the built-in templates. Choose Objective-C as the language so that it’s easy to call into your C code.

Share and Enjoy

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

Hi Quinn,

thank you so much for the instruction. I was able to run the generic password sample.

I created a simple C command line program, compiled and run with cmake/clang from terminal. (well, vscode terminal to be specific.) The program was able to run the generic password test after I switch to a machine I had full access permission. I assume the generic password was failed because of permission. However, adding the ECC key still failed with OSStatus -50 in the C program.

I also tried with Objective-C in XCode, and got the same result.

Also, I will get error "OSStatus -34018 : A required entitlement isn't present.", if I don't specify the keychain to add to. I'm not sure if that is one of the cause.

I will continue to try out different attributes and keys to see if there is any inspiration. I would really appreciate if you have further suggestion about what should I do next. Thank you.

However, adding the ECC key still failed with OSStatus -50 in the C program.

Right. To understand what’s going on here you must first understand the Mac keychain backstory, which is explained in On Mac Keychains.

Something in your ECC code is assuming the data protection keychain. A command-line tool can’t easily use the data protection keychain because:

  • Access to that keychain is gated by restricted entitlements, specifically those listed in Sharing Access to Keychain Items Among a Collection of Apps.

  • A restricted entitlement must be authorised by a provisioning profile

  • There’s no easy way to set up a command-line tool with a provisioning profile.

This lack of entitlements is what’s triggering the -34018 error, aka errSecMissingEntitlement [1].

For more background on entitlements and profiles, see TN3125 Inside Code Signing: Provisioning Profiles.

How you fix this depends on the intended execution context for this code. My experience is that most folks who default to testing in a command-line tool aren’t building an app, but rather a daemon or some other infrastructure. Is that the case here? If so, things are going to get complex and I need info about your planned execution context to advise you about the best path forward.

If, however, you intend to run this code in a normal app then the way forward is easy:

  1. Test your code in an app rather than in a command-line tool.

  2. Create an App ID for your app.

  3. Claim that App ID in your your code signing entitlements.

  4. Authorise that entitlement with a provisioning profile.

If you’re using Xcode, it takes care of most of these details. If you’re using third-party tooling, I recommend that you ask about this via the vendor’s support channel.

Share and Enjoy

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

[1] That error has its own long and complicated backstory. If you’re curious, read Error -34018 errSecMissingEntitlement.

Hi eskimo,

You are right. I'm trying to build a client daemon which setup network connection with SSL. I'm trying to create an identity with the ECC key and the certificate (in RSA PEM format. I was able to setup the certificate with SecItemImport as it supports RSA PEM format) And setup the identity as following:

// Add ECC Key to keychain (failed here)
CFMutableDictionaryRef secItemParams = CFDictionaryCreateMutable(default_alloc, 0, NULL, NULL);
CFDictionarySetValue(secItemParams, kSecClass, kSecClassKey);
CFDictionarySetValue(secItemParams, kSecValueRef, privateKey);
CFDictionarySetValue(secItemParams, kSecUseKeychain, import_keychain);
OSStatus key_status = SecItemAdd(secItemParams, NULL);

// setup identity
SecIdentityCreateWithCertificate(keychain, certificate, &output_identity);
identity = CFArrayCreate(default_alloc, (const void **)certs, 1L, &kCFTypeArrayCallBacks);
// Set certificate using the identity
SSLSetCertificate(ctx, identity);

I'm a little confused about the keychain implementation here. As mentioned in On Mac Keychains:

The Keychain and SecKeychain APIs only talk to the file-based keychain. The SecItem API talks to either implementation. Specifically, it talks to the data protection keychain if you supply either the kSecUseDataProtectionKeychain or the kSecAttrSynchronizable attribute. If not, it talks to the file-based keychain.

The keychain should default to file-based, since I'm working with MacOS and I didn't set either kSecAttrSynchronizable or kSecUseDataProtectionKeychain. I didn't get why the keychain is considered as data protection here.

However, inspired by the article, I manually set the above two attributes to false. And eventually I got the error: OSStatus -25304 : The specified item is no longer valid. It may have been deleted from the keychain. It seems like I did something wrong when I create the SecKeyRef, so it failed to find it. I applied SecKeyCreateWithData to create the SecKey as described here Storing CryptoKit Keys in the Keychain. Though the example is for CryptoKit, I assume the API should also work for pure data... or am I wrong here?

Here is the implementation, and key_error returns NULL. I assume the key creation succeed.

            CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(my_alloc, 0, NULL, NULL);
            CFDictionarySetValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
            CFDictionarySetValue(parameters, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
            CFDictionarySetValue(parameters, kSecUseDataProtectionKeychain, kCFBooleanFalse);
            CFDictionarySetValue(parameters, kSecAttrSynchronizable, kCFBooleanFalse);
            CFDictionarySetValue(parameters, kSecUseKeychain, keychain); // Not sure if the keychain attribute works here ...?
            // key_data is the binary data read from ANSI file, which is a ECC key in X963 format. 
            SecKeyRef privKey = SecKeyCreateWithData(key_data, parameters, &key_error); 

I'm trying to build a client daemon which setup network connection with SSL.

Please clarify what you mean by “client daemon”? Presumably the “client” part refers to the TLS, that is, your daemon is a TLS client. Is that right? And what about the “daemon” part? How does this code get executed? Is it a launchd daemon? Or agent? Or something else?

Share and Enjoy

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

Sorry, I was mean to say "client" only. I'm working on a library to help setup network connection with TLS, and testing with a TLS client build upon it.

I'm working on a library to help setup network connection with TLS

Right, but what contexts are you expecting that library to be used in?

This matters because the data protection keychain is only available a per-user context. If you plan to run this in a daemon context, you have to stick with the file-based keychain.

Share and Enjoy

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

Though it really depends on the library user, I would say there is a high possibility that the library is used for a daemon context. Regardless of that, as the previous implementation for RSA keys is using file-based keychain, I would like to keep it consistent and stick with the file-based keychain. Could I import ECC key as file-based keychain? Would there be any attribute I need to take care for that?

stick with the file-based keychain.

Agreed.

Could I import ECC key as file-based keychain?

The file-based keychain can cope with EC keys but importing them can be a challenge. The go-to API for that is SecItemImport, but the last time I looked at that I found that it can’t always import an EC key that’s it itself [1] generated (r. 59424536)-:

If you post a hex dump of an example EC key that you’d like to import, I’ll see what I can work out.

Share and Enjoy

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

[1] Well, SecItemExport.

I really appreciate your help here. Here is the hex dump of a sample EC key in X963 format I'm using for test. Would this work?

    unsigned char hard_code_key[] = {
        0x04, 0x0F, 0x12, 0x9D, 0x04, 0x61, 0x7F, 0x4A, 0xB3, 0xC4, 0xE9, 0x99, 0xCF, 0xFF, 0x37, 0x04,
        0x57, 0xA5, 0x7D, 0xFA, 0xC3, 0xE4, 0x2A, 0x25, 0xA9, 0xE4, 0x8D, 0x77, 0xF6, 0x54, 0x41, 0x8E,
        0x40, 0x4A, 0x2C, 0x55, 0xF2, 0xAE, 0xDC, 0x37, 0xB2, 0x7F, 0x0B, 0x6A, 0x13, 0x00, 0xED, 0xF2,
        0x1D, 0x19, 0xE4, 0x3E, 0xDA, 0x28, 0x41, 0x9C, 0xB2, 0x14, 0x18, 0xD4, 0x61, 0x10, 0xD1, 0x79,
        0x61, 0x7B, 0x7B, 0xE9, 0x64, 0x38, 0x2C, 0x7E, 0x20, 0x88, 0x28, 0x09, 0x42, 0x9F, 0xCD, 0x51, 
        0x39, 0x91, 0x0A, 0x4F, 0xAF, 0x5B, 0xC3, 0xAD, 0xA9, 0x79, 0xE6, 0x87, 0xA9, 0x76, 0xEA, 0x13, 
        0xAC};

Thanks for that.

Importing and exporting EC keys with the file-based keychain is a bit of a hassle:

  • The legacy import and export API, like SecItemImport, dates from an era before EC keys were supported and thus doesn’t have good support for them.

  • The iOS-style API, like SecItemAdd, was designed for the data protection keychain and, when you explore the edges, that default shows through, and hence your hitting -34018.

The file-based keychain can definitely store an EC key. For example, this code generates one:

let key = try secCall { SecKeyCreateRandomKey([
    kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
    kSecAttrKeySizeInBits: 256,
    kSecPrivateKeyAttrs: [
        kSecAttrIsPermanent: true,
    ],
] as NSDictionary, $0) }
print(key)

Note If you run this from an SSH session it fails with errSecInteractionNotAllowed (-25308) which is super weird.

However, I played around with various techniques for importing one and hit roadblocks at every turn.

At this point I’m beyond the level of time that I can spend on an issue here on DevForums. I recommend that you open a DTS tech support incident, which will allow me to allocate more time to your issue. When you open the TSI, make sure to reference this DevForums thread for context.

Share and Enjoy

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

Thank you so much for your help, otherwise I could not locate the issue. I really appreciated it! I will see if I can figure it out.

SecItemAdd with kSecUseKeychain returns error -50
 
 
Q