在多个app之间共享钥匙串项(Sharing Access to Keychain Items Among a Collection of Apps)


如果你开发了多个app,而这些app之间都需要同一用户的隐私数据,那么你可以使用访问组(access groups)来安全的在这些app之间共享用户隐私。例如,你可以通过共享认证信息,使登录到其中一个app的用户自动获取其他app的使用权(换句话说,就是自动登录到其他app)。这种共享,并不需要与用户交互或者用户授权,但限制是这些app,必须要是同一个开发团队开发的app。

一个访问组就是,用以特定字符串命名的组标记的多个app的逻辑集合。在指定组中的app可以和同一组中的任何其他app共享钥匙串项(keychain items)。你可以把一个app添加到任何数量的组中,但是这个app总是至少属于一个只包含了它自己的组。也就是说,无论一个app是否被加到其他任何组中,它总是可以在私有钥匙串项上存取数据。另一方面,关于钥匙串项,总是属于确定的一个组。

重要
这种形式的钥匙串项共享,适用于所有iOS app;但对于macOS app,只有使用了iCloud keychain的app才能使用,而要使用iCloud keychain,需要设置钥匙串项的kSecAttrSynchronizable属性为YES。对于不使用iCloud keychain的macOS app,用access control lists来共享钥匙串项。可以查看Access Control Lists

设置你的App访问组(Set Your App’s Access Groups)

通过手动操作你app的entitlements,可以来控制或管理你的app所属的访问组。特别地,app属于以虚拟字符数组中的字符串命名的所有组,而这个虚拟字符数组按以下顺序串联如下几项:

Keychain access groups

可选的keychain-access-groups entitlement持有一个字符串数组,其中每项都指定一个访问组。

Application identifier

Xcode自动为每一个代码签名过程中的app添加application-identifier entitlement,application-identifier由 team ID(team identifier)加上bundle ID(bundle identifier)组成。

Application groups

当把相关的app都收集到一个应用组(Application group)时,它们将共享这个组容器的访问权,同时获得以特定方式通知组中其他app的能力。从iOS 8开始,application-groups对应的符串数组也扩充了钥匙串访问组列表。

当设置bundle ID时,Xcode会为你处理app ID(application identifier)。其他设置需要手动操作Xcode上的capabilities。

建立app私有访问组(Establish Your App’s Private Access Group)

创建app的第一步是设置它的 bundle ID,而bundle ID通常使用反转的DNS符号的形式,就像字符串com.example.AppOne。发布应用时,在代码签名你的app前,Xcode自动为bundle ID加上 team ID(Apple为每个开发团队分配的唯一字符串)作为前缀,并把这个联合字符串作为 app ID。通过把app ID加到访问组数组中,系统将把app ID作为你的app私有访问组的名称:

[$(teamID).com.example.AppOne]

因为各个app的app ID都是唯一的,并且被以代码签名的方式保护,其他app不能使用,所以没有其他app属于这个组。这个组中的任何钥匙串项都是用这个组存储的App One私有的。类似的,如果你有第二个app,a bundle ID为com.example.AppTwo,那么它自动属于它的私有组:

[$(teamID).com.example.AppTwo]

因此,默认地,每个app的钥匙串项都独立于其他所有app。

c50a9ac2-758d-4271-978f-25ab9eebe195

将app添加到一个或多个钥匙串访问组中(Add Apps to One or More Keychain Access Groups)

当希望两个app共享钥匙串项时,可以考虑把它们添加到同一钥匙串访问组中。要做到这些,你需要在Xcode中为每个App授权钥匙串分享的能力,并在各自的钥匙串组列表中添加一个统一的字符串。通常,使用反转DNS的方式命名一个钥匙串组,就像bundle ID那样,因此,这里也许可以选择com.example.SharedItems:1bb4c530-ea94-428e-bbfd-a2fae0ebc47d

正如从bundle ID得到app ID那样,Xcode 自动为钥匙串组添加team ID作为前缀。这保证了你的钥匙串组特定于你的开发团队。当为App One按上述描述完成操作后,它的app分组逻辑列表如下:

[$(teamID).com.example.SharedItems,
 $(teamID).com.example.AppOne]

如果为App Two添加同一钥匙串组,它的app分组逻辑列表如下:

[$(teamID).com.example.SharedItems,
 $(teamID).com.example.AppTwo]

事实上,两个app获得了用来共享钥匙串项的重叠区域。
e9fc8605-bbe0-49d3-a4c1-5835564d92ee-1

注意到代表着 app IDs的特定区域仍然存在,这说明允许每一个app继续能够访问它自己的私有项。但是这两个app目前都属于欧同一个共享组,这合格共享组使它们能够共享钥匙串项。正如这样,只要你喜欢,你可以把一个app添加到许多不同的组中。

用App组去扩展钥匙串和非钥匙串数据的访问共享(Use App Groups to Expand Sharing of Keychain and Non-Keychain Data)

当你的app属于一个app组时,那么这个app可以和同组中的其他app共享各种非钥匙串数据(non-keychain data )。例如,你可以用initWithSuiteName: 函数创建新的NSUserDefaults实例来共享同一app组中的app的设置信息。如钥匙串访问组那样,你需要在Xcode中为app组(app groups)设置授权。

iOS 8开始,当app属于一个app组时,也可以用这个途径共享钥匙串项。例如,如果把App One添加到group.com.example.AppSuite这个app组中:

e4160d54-ee42-4c72-84d9-83c1432bdcca

App One的访问组列表扩大为:

[$(teamID).com.example.SharedItems,
 $(teamID).com.example.AppOne,
 $(teamID).group.com.example.AppSuite]

这让它能和App Suite group 组中的任何app共享钥匙串项(不同于和钥匙串访问组(keychain access group)中的app之间的共享)。

App Groups 和 Keychain Access Groups 之间的不同

App groups 和 Keychain Access Groups 并不是互斥的—你可以在同一app中同时使用两者—但它们在几个重要方面表现不同,这也许会有助于你决定在特定场景中使用哪个。

首先,如上所说,使用App groups可以共享除钥匙串项以外的其他数据。你也许会想要使用这种额外的共享功能,或者现在你已经这样用了,因此不在需要添加Keychain Access Groups。另一方面,也许你一点都不想用这种额外的共享功能,而更想用Keychain Access Groups。

其次,顺序问题。系统把access groups列表中的第一个认定为app的默认访问组。添加钥匙串项时,如果没有另外指定,钥匙串服务会假设一个默认访问组。app组(app group )永远都不能成为默认组,因为 app ID 总是存在并且比访问组列表出现早。然而一个钥匙串组可以用成为默认组,因为它在app ID之前出现。特别地,你在相应的capability中指定的第一个钥匙串访问组变成app的默认访问组。如果你不指定任何钥匙串访问组,app ID对应的组就会变成默认的组。

设置一个钥匙串访问组(Set a Keychain Item’s Access Group)

不像app可以属于很多访问组,钥匙串项只属于单独的一个用 kSecAttrAccessGroup属性标记的组。从钥匙次换项的角度,世界就是一个互斥组的集合,钥匙串项只属于他们中的其中一个。ea02cc8d-a561-4a47-a405-57dea126b8cf

当创建新的钥匙串项时,可以用kSecAttrAccessGroup指定一个组并添加到查询字典中(add query)。例如,在上面的定义的共享组中定义一个新的网络密码项:

let accessGroup = "<# Your Team ID #>.com.example.SharedItems"
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrAccount as String: account,
                            kSecAttrServer as String: server,
                            kSecAttrAccessGroup as String: accessGroup,
                            kSecValueData as String: password]

可以使用任何你的app所属于的组。如果尝试使用一个你的app不属于其中的组,上述操作将会失败。这也包括尝试使用空字符串作为kSecAttrAccessGroup的值,因为空字符串也代表一个合法组。

如果不指定任何访问组,钥匙串服务会使用app默认的访问组,如 Set Your App’s Access Groups中描述的那样,通常这个默认组是访问组列表中的第一个组。

当使用SecItemCopyMatching 函数搜索钥匙串项时,同样,你可以在查询字典中(search query)指定一个访问组,限制在特定访问组中进行搜索。如果不指定的话,搜索会返回所匹配的组中的所有项。

原文:Sharing Access to Keychain Items Among a Collection of Apps

知识共享许可协议本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,请务必在遵守许可协议的前提下转载。
发布时间:2019-01-15 17:44:28 阅读:1313 标签:翻译技术安全