21

我知道我可以通过启用应用程序组和使用 NSUserDefaults 在我的共享扩展程序及其包含的应用程序之间共享数据(请参阅在 iOS 8 共享扩展程序和主应用程序之间共享数据)。

但是,我存储的数据是敏感的,所以我希望使用钥匙串。因此,用户将在包含的应用程序中输入帐户信息,然后共享扩展程序将读取该数据以执行预期的共享操作。

有谁知道这是否可能?我对它的第一次破解表明扩展程序和包含应用程序具有单独的钥匙串(在包含应用程序中使用密钥保存数据时,在尝试为扩展程序中的该密钥返回数据时返回 null)。

谢谢!

PS Using Lockbox for Keychain access,但如果它过于抽象而无法正常工作,我可以放弃它。 https://github.com/granoff/Lockbox

4

5 回答 5

22

使钥匙串在 Xcode 8 中共享。

1) 在 Capabilities 的 App 目标中找到并打开“Keychain Sharing”,添加一个 Keychain Group 密钥(一个反向域样式的字符串,如 com.myappdomain.myappname)

2)对扩展目标执行完全相同的操作。确保应用程序和扩展程序的钥匙串组密钥相同。

以您通常的方式从 Keychain 添加和检索数据,无需对代码进行特殊更改。例如,这是我在主应用程序中将数据放入 Keychain 的方式(有点过时,但在 Swift 3 中仍然有效):

let login = loginString
let domain = domainString
let passwordData: Data = passwordString.data(using: String.Encoding.utf8, allowLossyConversion: false)!
let keychainQuery: [NSString: NSObject] = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: login as NSObject,  // login and domain strings help identify
    kSecAttrService: domain as NSObject, // the required record in the Keychain
    kSecValueData: passwordData as NSObject]
SecItemDelete(keychainQuery as CFDictionary) //Deletes the item just in case it already exists
let keychainSaveStatus: OSStatus = SecItemAdd(keychainQuery as CFDictionary, nil)

然后在扩展中检索它:

let keychainQuery: [NSString: NSObject] = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: login as NSObject,
    kSecAttrService: domain as NSObject,
    kSecReturnData: kCFBooleanTrue,
    kSecMatchLimit: kSecMatchLimitOne]
var rawResult: AnyObject?
let keychain_get_status: OSStatus = SecItemCopyMatching(keychainQuery as CFDictionary, &rawResult)

if (keychain_get_status == errSecSuccess) {
    if let retrievedData = rawResult as? Data,
        let password = String(data: retrievedData, encoding: String.Encoding.utf8) {
       // "password" contains the password string now
    }
}

请注意,您仍然需要将“登录”和“域”传递给扩展程序,以便识别正确的记录。这可以通过 NSUserDefaults 来完成。请参阅此答案以了解如何执行此操作。

于 2017-01-11T10:26:24.887 回答
9

这是可以做到的。它是创建一个框架来进行钥匙串访问,并在“功能”下打开“激活钥匙串共享”的组合。这个链接告诉我我需要知道的:http ://swiftandpainless.com/ios8-share-extension-with-a-shared-keychain/

于 2014-11-02T06:06:03.227 回答
1

我为应用程序和共享扩展的目标启用了应用程序组功能。

只需转到 Singing&Capabilities 并为两个目标添加“应用程序组”功能。然后,您应该创建一次组 id 标识符(称为“创建新容器”),例如让我们创建group.lolrandomname. 然后您应该在两个目标中选择/启用此应用程序组 ID。

现在在代码中,您现在必须在每次要使用共享钥匙串时添加此属性: kSecAttrAccessGroup: "group.lolrandomname",

它工作正常:我可以从应用程序和我的共享扩展访问相同的存储项目:)

这是在共享钥匙串中存储某些内容的示例(除了查询中的一个属性外,所有内容都相同):

let myAwesomeString = "hello world"

let query = [kSecClass: kSecClassGenericPassword,
             kSecAttrAccount: "com.myapp.an-awesome-string",
             kSecAttrAccessGroup: "group.lolrandomname",  // <<-- this!
             kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
             kSecValueData: myAwesomeString.data(using: .utf8)!] as [String: Any]

let status = SecItemAdd(query as CFDictionary, nil)

guard status == errSecSuccess else {
    print(status.humanReadable)
    throw KeyStoreError("Unable to store item: \(status.humanReadable)")
}

阅读和删除请看SecItemCopyMatchingand SecItemDelete,但不要忘记,就像我说的,把kSecAttrAccessGroup钥匙放在query!

于 2021-07-15T17:22:56.617 回答
0

使用标准的 Objective-C KeychainItemWrapper 类并在桥接头中使用 #import "KeychainItemWrapper.h" 条目:

    func btnSaveAction() {

    let appGroupID = "group.com.yourcompany.appid"
    let keychain = KeychainItemWrapper(identifier: "Password", accessGroup:appGroupID)
    keychain.setObject(self.txtfldPassword.text!, forKey:kSecValueData)
    keychain.setObject(self.txtfldEmail.text!, forKey:kSecAttrAccount)

    }

在 Watch 扩展端(Swift):

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)

    let appGroupID = "group.com.yourcompany.appid"
    let keychain = KeychainItemWrapper(identifier: "Password", accessGroup:appGroupID)
    println(keychain.objectForKey(kSecAttrAccount))
    println(keychain.objectForKey(kSecValueData))

}

在 Objective C 中,watchkit 扩展:

NSString *appGroupID = @"group.com.yourcompany.appid";
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"Password" accessGroup:appGroupID];
[keychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
NSLog(@"account = %@", [keychain objectForKey:(__bridge id)(kSecAttrAccount)]);
NSLog(@"password =%@", [keychain objectForKey:(__bridge id)(kSecValueData)]);

不要忘记为同一钥匙串组的手机应用程序和手表套件扩展打开“功能”下的“钥匙串共享”:“group.com.yourcompany.appid”

于 2015-06-09T11:16:07.080 回答
-1

使用以下链接中的 KeychainItemWrapper 类并将您的组标识符作为访问组传递。

https://developer.apple.com/library/ios/samplecode/GenericKeychain/Listings/Classes_KeychainItemWrapper_m.html

于 2015-04-07T13:16:13.943 回答