在iOS扩展之间共享,并且它包含带钥匙串的应用程序?

问题描述:

我知道我可以通过启用应用程序组和使用NSUserDefaults在我的共享扩展和其包含的应用程序之间共享数据(请参阅Sharing data between an iOS 8 share extension and main app)。在iOS扩展之间共享,并且它包含带钥匙串的应用程序?

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

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

谢谢!

P.S.使用Lockbox进行Keychain访问,但如果它太抽象以使其工作,我可以抛弃它。 https://github.com/granoff/Lockbox

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

+4

这是非常有用的不链接到其他网站或至少添加相关的来源的答案,该链接不再可用。 – leolobato 2015-07-29 14:30:14

+0

这里有相当多的信息,我不喜欢蒸馏和粘贴,但这里是Internet Archive版本:https://web.archive.org/web/20141028160328/http://dasdev.de/2014/ 08/12/ios8-share-extension-with-a-shared-keychain – 2015-07-30 15:12:22

+0

GitHub repo仍然存在https://github.com/dasdom/KeychainDemo太 – 2015-10-22 09:55:30

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

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

+0

此链接不再有效。 – melbic 2016-12-22 10:44:59

使用标准的Objective-C KeychainItemWrapper类和与所述桥接报头的#进口的条目 “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) 

    } 

在观看伸展侧(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”

为了使钥匙扣在Xcode 8.

1)共享在你的应用目标在功能找到并打开“钥匙扣共享”,添加一个钥匙串组密钥(反向域风格串等com.myappdomain.myappname)

2 )对扩展目标完全相同。确保钥匙串组密钥对于应用和扩展都是相同的。

以通常的方式添加和检索Keychain中的数据,代码中无需进行特殊更改。例如,这里是我如何把数据放到钥匙串在主应用程序(有点老套,但是在斯威夫特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(keychainQueryForPass 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完成。有关如何操作,请参阅this answer