In my app, I want to be able to sync a configuration that gets created by the user. I wanted to use iCloud to sync that configuration so that it is always the same on all devices. But, I use the keychain to store the password.
Is there a way to also sync keychain data?
On your iPhone, iPad, or iPod touch, tap Settings > [your name] > iCloud > Keychain > Advanced. On your Mac, choose Apple menu > System Preferences, then click Apple ID. Click iCloud in the sidebar, then select Keychain. If you're using macOS Mojave or earlier, click iCloud, then click Options next to Keychain.
iCloud Keychain can also keep the accounts you use in Mail, Contacts, Calendar, and Messages up to date across all your iPhone and iPad devices and Mac computers.
When a user backs up iPhone data, the keychain data is backed up but the secrets in the keychain remain encrypted in the backup. The keychain password is not included in the backup.
iCloud Keychain is a new feature in iOS 7.0.3 and OS X Mavericks 10.9. Specify the kSecAttrSynchronizable
attribute when adding a keychain item using the SecItem API.
These are the utility methods I've made for keychain. kSecAttrSynchronizable is what makes iCloud Sync work. Hope they help.
Load item
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass,
service, (__bridge id)kSecAttrService,
service, (__bridge id)kSecAttrAccount,
service, (__bridge id)kSecAttrSynchronizable,
(__bridge id)kSecAttrAccessibleAfterFirstUnlock, (__bridge id)kSecAttrAccessible,
nil];
}
+ (void)save:(NSString *)service data:(id)data {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}
+ (void)remove:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}
+(NSString *)keychainItem:(NSString *)service{
id data = [self load:service];
if([data isKindOfClass:[NSString class]]){
return data;
}
return @"";
}
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
}
@catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
}
@finally {}
}
if (keyData) CFRelease(keyData);
return ret;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With