سؤال

I'm struggling with iOS keychain and i can't seem to find any good documentation.

Anyway, I have two apps, and basically all i want to do is share some data in the keychain and keep some data private so that the other app cannot access it.

I've tried to implement the KeychainItemWrapper provided by Apple, but this is simply not working. I have no issue sharing data, but if i don't set an access group, the data is still shared. I working with a device not the simulator, which could lead to the same problems.

Here is my code

App 1 :

KeychainItemWrapper *item = [[KeychainItemWrapper alloc] initWithIdentifier:@"SharedKeyChainApp" accessGroup:nil];
[item setObject:@"MyAccount" forKey:(__bridge id)kSecAttrAccount];
[item setObject:@"SecureValue" forKey:(__bridge id)kSecValueData];

App 2 :

KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"SharedKeyChainApp" accessGroup:nil];
NSString *data = [keychain objectForKey:(__bridge id)kSecValueData];
NSLog(@"data is : %@",data); //Prints "data is : SecureValue"

If I remove in project properties my keychain group in one or the other app, it won't print anything. But obviously i'm not able to share data between those two apps anymore.

Thanks

هل كانت مفيدة؟

المحلول

If it's a shared keychain then it's shared. All the data in it will be accessible to any other app which can access the keychain.

You could:

  • Create 2 keychain. One shared and one private. Sharable stuff goes in shared, private stuff goes in private.
  • Encrypt data you won't want to to share with others.

I'd probably go with the first. IMHO, KeychainItemWrapper is pretty poor as grab and use code. It's old code which provides little in the way of functionality. I'm attaching a quick and dirty bit of code I wrote to play with and test out using the key chain functionality without KeychainItemWrapper. In this case I was playing with items both in "app" and "Security" to create some shared and non-shared items. You can't really tell that here since it's just some test code and sharing is under Targets->Capabilities->Keychain Sharing.

- (void)viewDidLoad {
    [super viewDidLoad];

//    [self removeKeychainItem];
//    [self addKeychainItem];
    [self searchForKeychainItems];
}

- (void)searchForKeychainItems {
    [self log:@"\n\n  EXISTING KEYCHAIN ITEM(S)"];

    NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
                            (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,       // returns password
                            (__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue, // returns rest of data
//                            (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.Security"
//                            (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.app"
                        };

    OSStatus resultCode;
    CFArrayRef *searchResults = nil;
    resultCode = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&searchResults);

    NSArray *foo = CFBridgingRelease(searchResults);
    [self log:[NSString stringWithFormat:@"Search result code: %d", (int)resultCode]];
    [self log:[NSString stringWithFormat:@"Search Results: %@", foo]];
    NSDictionary *keychainItem = foo[0];
    NSString *password = [[NSString alloc] initWithData:[keychainItem objectForKey:(__bridge id)kSecValueData] encoding:NSUTF8StringEncoding];
    [self log:[NSString stringWithFormat:@"password is `%@`", password]];
}

- (void)addKeychainItem {
    [self log:@"\n\n  ADDING KEYCHAIN ITEM"];

    NSDictionary *genericDataDictionary = @{@"authState": @"1",
                                            @"lastAuthDate": @"2/11/2014",
                                            @"otherCrap": @"poo"};
    NSData *encodedGenericData = [NSKeyedArchiver archivedDataWithRootObject:genericDataDictionary];
    NSData *encodedPassword = [@"secret" dataUsingEncoding:NSUTF8StringEncoding];

    NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrCreator: @"MyCom",
                            (__bridge id)kSecAttrComment: @"keychain tests",
                            (__bridge id)kSecAttrService: @"Credentials",
                            (__bridge id)kSecAttrAccount: @"username",
                            (__bridge id)kSecValueData: encodedPassword,
                        (__bridge id)kSecAttrGeneric: encodedGenericData,
                        (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.Security"
                        };

    OSStatus result;
    result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
    NSLog(@"Add status code: %d", (int)result);
}

- (void)removeKeychainItem {
    [self log:@"\n\n  REMOVING KEYCHAIN ITEM"];

    NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
//                            (__bridge id)kSecAttrCreator: @"MyCom",
//                            (__bridge id)kSecAttrService: @"Credentials",
                            (__bridge id)kSecAttrComment: @"New Keychain standards Test Item",
//                            (__bridge id)kSecAttrAccount: @"username",
//                            (__bridge id)kSecValueData: [@"password" dataUsingEncoding:NSUTF8StringEncoding],
//                            (__bridge id)kSecAttrGeneric: encodedGenericData
                            //                            (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.Security"
                            };

    OSStatus resultsCode;
    resultsCode = SecItemDelete((__bridge CFDictionaryRef)query);
    NSLog(@"Delete results code: %d", (int)resultsCode);
}

- (void)log:(NSString *)text {
    self.textView.text = [[self.textView.text stringByAppendingString:text] stringByAppendingString:@"\n"];
}

نصائح أخرى

One thing to be aware of is that you need to add both a private and a shared access group. The private access group should match the app's bundle ID and come before the shared one.

<dict>
<key>keychain-access-groups</key>
    <array>
        <string>$(AppIdentifierPrefix)<APP BUNDLE ID></string>
        <string>$(AppIdentifierPrefix)com.mybusiness.shared</string>
    </array>
</dict>

If you only add a single named access group then this will be the default for all keychain items effectively sharing everything.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top