How to store the userdata in system keychain instead of Default keychains

I need to store some data of my application in system keychain which should to accessible to all the users in the system. Here is the below sample code :

// Create a SecAccessControlRef for a keychain item with access control
SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(
    kCFAllocatorDefault,
    kSecAttrAccessibleWhenUnlocked, 
    kSecAccessControlUserPresence, 
    NULL
);

// Define a query dictionary for a keychain item
NSDictionary *query = @{
    (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
    (__bridge id)kSecAttrService: @"MyService",
    (__bridge id)kSecAttrAccount: @"MyAccount",
    (__bridge id)kSecValueData: [@"MyPassword" dataUsingEncoding:NSUTF8StringEncoding],
    (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)accessControl,
};

// Add the keychain item to the default keychain (login keychain)
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
if (status != errSecSuccess) {
    NSLog(@"Error adding keychain item: %d", (int)status);
}

I tried using SecKeychainOpen to access the system keychain but SecKeychainOpen is deprecated and I could not find any equivalent latest API to support that.

SecKeychainRef systemKeychain;
OSStatus status = SecKeychainOpen("/Library/Keychains/System.keychain", &systemKeychain);

if (status != errSecSuccess) {
    NSLog(@"Error opening system keychain: %d", status);
} else {
    SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(
        kCFAllocatorDefault,
        kSecAttrAccessibleWhenUnlocked, 
        kSecAccessControlUserPresence,  
        NULL
    );

    NSDictionary *query = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: @"MyService",
        (__bridge id)kSecAttrAccount: @"MyAccount",
        (__bridge id)kSecValueData: [@"MyPassword" dataUsingEncoding:NSUTF8StringEncoding],
        (__bridge id)kSecUseKeychain: (__bridge id)systemKeychain, 
        (__bridge id)kSecAttrAccessControl: (__bridge_transfer id)accessControl,
    };

    // Add the keychain item to the system keychain
    status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
    if (status != errSecSuccess) {
        NSLog(@"Error adding keychain item to system keychain: %d", (int)status);
    }
    if (systemKeychain) {
        CFRelease(systemKeychain);
    }
}

ANY suggestions will be helpful, Please help!

Replies

I need to store some data of my application in system keychain which should to accessible to all the users in the system.

You’re going to struggle to achieve this goal. Before you start, I recommend that you read TN3137 On Mac keychain APIs and implementations, because it provides critical background to this issue.

The code you posted uses kSecAttrAccessControl, which only works with the data protection keychain. There is no data protection keychain that’s available to all users on the system. The concept of ‘the System keychain’ simply doesn’t exist there.

And that means that you have to use the file-based keychain, which is not good because, as noted in TN3137, it’s on the road to deprecation. That explains why you keep bumping into deprecation warnings, and it suggests that there isn’t a good long-term solution to this problem.

IMPORTANT When it comes to locating the System keychain, do not hard code the path. Rather, call SecKeychainCopyDomainDefault with kSecPreferencesDomainSystem. This is also deprecated, of course (-:

Finally, there’s a BSD permissions issue here. The file-based keychain is stored in… well… a file, and to modify it you need permission to change that file. As things currently stand only root can modify the file, so you can’t change the System keychain from a standard app. You have to do some sort of privilege escalation. This is possible, but not easy. See BSD Privilege Escalation on macOS.

Honestly, I reckon you’d be better off rethinking your design here.

Share and Enjoy

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

@eskimo I went through TN3137 On Mac keychain APIs and implementations . I could not completely understand the difference between file based keychain and data protection keychain. Could you please help.

A file-based keychain is stored in a file with the .keychain extension. Typically you’ll find these in ~/Library/Keychains or /Library/Keychains, but it’s possible to store them anywhere.

The data protection keychain is stored in a database. On macOS, each user has their own database; there is no system database. The exact format and location of that database is not documented (although TN3137 has some hints).

At the API level, the SecItem API on macOS targets the file-based keychain by default. To target the data protection keychain, use kSecUseDataProtectionKeychain (or the older kSecAttrSynchronizable).

If you’re still confused then you’ll need to ask specific questions.

Share and Enjoy

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