web-dev-qa-db-ja.com

iOS拡張機能とそれに含まれるアプリをキーチェーンで共有しますか?

アプリグループを有効にし、NSUserDefaultsを使用することで、共有拡張機能とそれに含まれるアプリの間でデータを共有できることを理解しています( iOS 8共有拡張機能とメインアプリ間のデータ共有 を参照)。

ただし、保存しているデータは機密性が高いため、キーチェーンを使用したいと思いました。そのため、ユーザーは含まれているアプリにアカウント情報を入力し、共有拡張機能はそのデータを読み取って目的の共有アクションを実行します。

これが可能かどうか誰かが知っていますか?それに対する私の最初の亀裂は、拡張機能とそれを含むアプリが別々のキーチェーンを持っていることを示唆しています(拡張機能でそのキーのデータを返そうとすると、包含アプリのキーでデータを保存するとnullが返されます)。

ありがとう!

P.S.キーチェーンアクセスにロックボックスを使用しますが、抽象化が多すぎて機能しない場合は、ロックボックスを破棄することができます。 https://github.com/granoff/Lockbox

18
Jim Biancolo

キーチェーンをXcode8で共有するため。

1)機能のアプリターゲットで「キーチェーン共有」を見つけてオンにし、キーチェーングループキー(com.myappdomain.myappnameのようなリバースドメインスタイルの文字列)を追加します

2)拡張ターゲットについてもまったく同じことを行います。キーチェーングループキーが、アプリと拡張機能の両方で同じであることを確認してください。

通常の方法でキーチェーンにデータを追加および取得します。コードに特別な変更を加える必要はありません。たとえば、メインアプリのキーチェーンにデータを配置する方法は次のとおりです(少し古風ですが、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を介して実行できます。これを行う方法については、 この回答 を参照してください。

19
Vitalii

これは行うことができます。これは、キーチェーンアクセスを行うためのフレームワークを作成することと、[機能]の下の[キーチェーン共有をアクティブ化する]をオンにすることの組み合わせです。このリンクは私が知る必要があることを教えてくれました: http://swiftandpainless.com/ios8-share-extension-with-a-shared-keychain/

9
Jim Biancolo

標準のObjective-CKeychainItemWrapperクラスを使用し、ブリッジヘッダーに#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)

    }

ウォッチ拡張側(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では、ウォッチキットの拡張機能は次のとおりです。

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」の電話アプリと時計キット拡張機能の両方で、「機能」の下にある「キーチェーン共有」をオンにすることを忘れないでください。

0
Thiru