9

まず、キーチェーンによる秘密の保護に関する WWDC 2013 セッションを見てきました。基本的なパスコード ストアを実行したいと考えています。ビデオ全体を見ましたが、ビデオの最初の 10 分間で必要なものを見つけました。簡単に思えますが、データのエンコードと取得がどのように機能するのか完全には理解していません。

問題: secItemCopyMatching の後、NSString に変換する前に、NSData オブジェクトをチェックして nil でないことを確認します。問題は、それが常に nil であることです。以下は、キーチェーンのエントリまたは更新を保存する方法と、それを取得する方法です。ヘルプと説明をいただければ幸いです。

更新 (編集): Fruity Geek、返信ありがとうございます。__bridge を使用して以下のコードを更新しました。私の問題は、パスワードを正しく保存および取得していますか? 両方とも間違っているのでしょうか、それともどちらか一方だけですか? 私の NSData インスタンスは常に nil です。リターン コードを確認しており、SecItemAdd と SecItemUpdate (キーチェーン エントリが存在する場合) が正しく機能しています。保存されているデータ (パスコード) の文字列値を取得して、ユーザーが入力したパスコードと比較できないようです。助けてくれたみんなに感謝します。これが私が今していることです:

更新#2:(Fruity Geekの回答と最終作業バージョンで編集。私の編集には、以下のコードへの変更のみが含まれます。)

キーチェーン エントリを設定します。

NSData *secret = [_backupPassword dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{
    (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
    (__bridge id)kSecAttrService: twServiceName,
    (__bridge id)kSecAttrAccount: twAccountName,
    (__bridge id)kSecValueData: secret,
};
OSStatus status =
    SecItemAdd((__bridge CFDictionaryRef)query, NULL);

if (status == errSecDuplicateItem) {
    // this item exists in the keychain already, update it
    query = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: twServiceName,
        (__bridge id)kSecAttrAccount: twAccountName,
    };
    NSDictionary *changes = @{
        (__bridge id)kSecValueData: secret,
    };
    status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)changes);
}

キーチェーンからパスワードを取得します。

NSDictionary *query = @{
    (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
    (__bridge id)kSecAttrService: twServiceName,
    (__bridge id)kSecAttrAccount: twAccountName,
    (__bridge id)kSecReturnData: @YES,
};
NSData *data = NULL;
CFTypeRef dataTypeRef = (__bridge CFTypeRef)data;
OSStatus status =
    SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef);

NSData *data = (__bridge NSData *)dataTypeRef;

NSString *passcode = @"none";
if (status == errSecSuccess) {
    // we found a keychain entry, set the passcode
    if (data)
        passcode = [NSString stringWithUTF8String:[data bytes]];
}

twServiceName と twAccountName は静的 NSString です。

私が言ったように、私は __bridge または CFTypeRef で何をしているのかよくわかりません。私はリンゴのドキュメント、ここや他のサイトの多数の投稿を調べましたが、キーチェーンとこれらの用語は私にとってまったく新しいものであり、まだ理解しようとしています. ここの誰かが私の誤りを指摘し、理解を助けてくれることを願っています。助けてくれてありがとう。

iOS 7 / Xcode 5

4

1 に答える 1

12

Core Foundation オブジェクトを所有しておらず (それらを作成またはコピーしていない)、それらを保持または解放したくないのでCFBridgingRelease、andCFBridgingRetainは正しくありません。(__bridge id)Objective-C オブジェクトにキャストする場合はいつでも、代わりに使用してください。

(__bridge id)kSecAttrService

__bridge と CFBridgingRelease/CFBridgingRetain はいつ使用する必要がありますか?

データ変数と dataTypeRef は 2 つの異なるポインターです。SecItemCopyMatching で dataTypeRef のみがデータで埋められました。CFTypeRef が SecItemCopyMatching によって取り込まれた後にNSData にキャストして、データが常に nil になるとは限りません

CFTypeRef dataTypeRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef);
NSData *data = (__bridge NSData *)dataTypeRef;

すべての SecItem 関数呼び出しによって返される OSStatus を詳しく調べる必要があります。成功しないリターン コードが多数あります。あなたの場合、SecItemAdd で重複したアイテムを検出し、まったく同じアイテムに更新します (何もしません)。代わりに、最初に SecItemCopyMatching を使用して取得してみてください。一致するものが見つからない場合は、SecItemAdd を使用します。一致が見つかった場合は、SecItemUpdate を使用します。

Apple のサンプル コードはひどく、ARC 用に書かれておらず、紛らわしいですが、存在します。特に、writeToKeychainメソッドは必要なものです。 https://developer.apple.com/library/ios/documentation/Security/Conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html#//apple_ref/doc/uid/TP30000897-CH208-SW1

于 2013-10-09T23:23:23.047 に答える