2

I have an app(app1) which is in appstore with bundle id com.x.y Now i was developing another app(app2) under same developer account with bundle id com.x.z

I wanted to make the keychain value stored in app1 available to app2.

This availablity of keychain is determined by keychain-access-groups. so if i add prefix( current Team id) to both the bundle id i am able to get the values. example teamid.com.x.y teamid.com.x.z

Issue is When i add the prefix to app1 which is in appstore, it ask for login credentials again which i dont want as app has lots of users. I wasn't using prefix earlier i just added them. Is there a way i could get the keychain access for both the apps without having user to login again.

1 Answers1

1

First, it's important to realize that Xcode is already adding the AppIdentifierPrefix to your identifier. It unfortunately hides it in the GUI, but if you open the entitlements plist, you'll see it. This is the identifier that is used to sign the app, and it's the piece that is used to enforce access control. I don't believe the teamid prefix you're adding really does anything. I generally would recommend an access group com.x.shared or com.x.appgroup.shared and not use com.x.z (I'm assuming com.x.y already exists, so you can't change that).

I'm assuming here that you don't want to have to force users to upgrade App1, correct? I'm moving forward on that assumption.

If you can upgrade App1 (not require an upgrade, but make sure that all new customers have an upgraded version), then only store in com.x.y if it exists. Otherwise, store in com.x.shared:

  • When you read from the keychain, don't use an access group. This will get the first matching record.
  • When you write to the keychain, use the access group that was in the record you read.

If you don't want to upgrade App1 at all right now (required or not), then just always read and write to com.x.y in App2.

When you're ready to end-of-life the com.x.y group (if you're able to finally upgrade all App1 supported users), then you can switch to:

  • Read from com.x.y. If it's found, delete it, and recreate it as com.x.shared. You can do this one-time in the application startup (just write an NSUserDefaults that says you've done it.
  • From then on, always use com.x.shared explicitly.

The key tool here is that when you ask for an explicitly access group, you have to provide the whole thing, including you AppId (which isn't displayed in the Xcode GUI). You can of course hard-code it, but a better solution is to dynamically query it. I use an updated version of David H's code:

- (NSString *)bundleSeedID {
  NSDictionary *query = @{ (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                           (__bridge id)kSecAttrAccount : @"bundleSeedIDQuery",
                           (__bridge id)kSecAttrService : @"",
                           (__bridge id)kSecReturnAttributes : (id)kCFBooleanTrue
                           };
  CFDictionaryRef result = nil;
  OSStatus status = SecItemCopyMatching((__bridge CFTypeRef)query,
                                        (CFTypeRef *)&result);
  if (status == errSecItemNotFound)
    status = SecItemAdd((__bridge CFTypeRef)query, (CFTypeRef *)&result);
  if (status != errSecSuccess)
    return nil;
  NSString *accessGroup = [(__bridge NSDictionary *)result
                           objectForKey:(__bridge id)kSecAttrAccessGroup];
  NSArray *components = [accessGroup componentsSeparatedByString:@"."];
  NSString *bundleSeedID = components[0];
  CFRelease(result);
  return bundleSeedID;
}

This will tell you your prefix at runtime. It does so b creating a bogus keychain entry, then querying it and seeing what access group was attached to it.

You may be interested in the first section of Getting Security and Privacy Right from Renaissance.io 2014. You can skip to "Protecting Secrets with Keychain."

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanx a lot Rob. Really appreciate your time. This does make so much sense. M implimenting above and will vote up for you and let you know if issue faced . – Abhijeet_dce Aug 24 '15 at 14:37
  • this indeed helpful. so in app2 i made acess group com.x.y and i am able to read the keychain. I will push app1 update before i push app2(new) to appstore. so should i be migrating app1 keychain to com.x.shared in app1 update. and use app2 with acess group com.x.shared?? – Abhijeet_dce Aug 24 '15 at 14:54
  • @Abhijeet_dce yes, but remember that people may install app2 before upgrading app1, so you may still need to read and write the old key in app2 in the meantime. – Rob Napier Aug 24 '15 at 15:31