私は キーチェーン用のAppleラッパー を使用し、その上にアイテムを保存しようとします(シミュレーターで実行、iOS 4.1)。
これまでにキーチェーンを使ったことがありません。
私はこのエラーを受け取ります:
キーチェーンアイテムを追加できませんでした。エラー-25299
KeychainItemWrapper.mの304行目:
// No previous item found; add the new one.
result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL);
NSAssert( result == noErr, @"Couldn't add the Keychain Item." );
これは私が保存をする方法です:
- (void) saveKey:(NSString *)key value:(NSString *)value {
KeychainItemWrapper *keyItem = [[KeychainItemWrapper alloc] initWithIdentifier:key accessGroup:nil];
[keyItem setObject:value forKey:(id)kSecValueData];
[keyItem release];
}
そして、これはAPIが保存しようとする値です:
<CFBasicHash 0x7231f60 [0x320d380]>{type = mutable dict, count = 5,
entries =>
2 : <CFString 0x2e6eb98 [0x320d380]>{contents = "labl"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
3 : <CFString 0x2e6efb8 [0x320d380]>{contents = "v_Data"} = <CFString 0x727de60 [0x320d380]>{contents = "dit8"}
4 : <CFString 0x2e6ebc8 [0x320d380]>{contents = "acct"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
5 : <CFString 0x2e6eb58 [0x320d380]>{contents = "desc"} = <CFString 0x2fb018 [0x320d380]>{contents = ""}
6 : <CFString 0x2e6ebe8 [0x320d380]>{contents = "gena"} = <CFString 0x2ffd08 [0x320d380]>{contents = "userCode"}
}
これは数か月前のものであることはわかっていますが、同じ問題が発生しただけで苦痛だったので、共有したいと思いました。私はこの行を追加してそれを解決しました:
[self.keychainItemWrapper setObject:@"MY_APP_CREDENTIALS" forKey:(id)kSecAttrService];
//@"MY_APP_CREDENTIALS" can be any string.
私はこのブログエントリが非常に役に立ったと感じました。「データベースの用語では、2つの属性kSecAttrAccount、kSecAttrServiceの一意のインデックスであると考えることができます。これらの2つの属性の組み合わせは、キーチェーンのエントリごとに一意である必要があります。」 ( http://useyourloaf.com/blog/2010/4/28/keychain-duplicate-item-when-adding-password.html から)。
また、このコードを使用するAppleのサンプルプロジェクトでは、アプリデリゲートでKeychainItemWrapperをインスタンス化します。それが必要かどうかはわかりませんが、私は彼らの例にできるだけ忠実に従うようにしています:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
//there will be some standard code here.
KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"MY_APP_CREDENTIALS" accessGroup:nil];
self.keychainWrapper = wrapper;
[self.keychainWrapper setObject:@"MYOBJECT" forKey:(id)kSecAttrService];
[wrapper release];
}
これはラッパーコードのバグだと思います。ロジックは基本的に「このエントリはすでに存在しますか?いいえ、存在しません。追加します。おっと、すでに存在しているので追加できません。」
また、kSecAttrAccountを設定する必要がある場合もあります。これは、パスワードに付随するユーザー名を保存することを目的としているため、この値も設定せずに試したことはありません。
[self.wrapper setObject:txtUserName.text forKey:(id)kSecAttrAccount];
ドキュメント によると、取得するエラー-25299は "errSecDuplicateItem"です。これは、追加しようとしているアイテムが既に存在することを意味します。 KeychainItemWrapperのリンクされたコードを見ると、SecItemCopyMatching
呼び出しがerrSecItemNotFound(–25300)以外のエラーで失敗していると思います。
Buzz Andersenによる SFHFKeychainUtils を使用すると、キーチェーンを使用して値を簡単に保存および取得できます。
これは、このライブラリの使用方法に関する小さな例です。
// To store data
NSError *error = nil;
[SFHFKeychainUtils storeUsername:username andPassword:password forServiceName:kStoredData updateExisting:YES error:&error];
// To retrieve data
NSString *password = [SFHFKeychainUtils getPasswordForUsername:username andServiceName:kStoredData error:&error];
// To delete data from keychain
[SFHFKeychainUtils deleteItemForUsername:username andServiceName:kStoredData error:&error];
キーホルダーは完全な痛みです。代わりにラッパーとして Buzz AndersenのSTUtils ライブラリを使用する必要があります。それはあなたの人生を大幅に容易にします。私はそれについて問題を経験したことがありません。
私にとっての解決策は、KeychainItemWrapper
「シングルトン」を作成し、それをアプリ全体で使用することでした。 (実際、私の場合、私はKeychainItemWrapper
- sでいっぱいのシングルトンディクショナリを持っていました。
これにより、「このアイテムはキーチェーンに存在しますか?いいえ?次に追加します。おっと!NSAssert()
が既にアイテムを追加しようとしています。存在します(エラー-25299)」
確かではありませんが、問題はキーチェーンの同期に関係しているのではないかと思います。私はNSUserDefaults
で同様の問題を抱えていました。NSUDに書き込んだ後、コードの他の場所でstandardUserDefaults
を取得してそれらから読み取りましたが、まだ更新が行われていません(私が行っていない[ud synchronize]
、まだ。)
コードでは、私のルーチンは次のようになります。
+ (KeychainItemWrapper*) keyChainWrapperForKeyID: (NSString*) keyID
{
static dispatch_once_t onceToken = 0;
static NSMutableDictionary *rfcuKeyChains = nil;
dispatch_once(&onceToken, ^{
rfcuKeyChains = [NSMutableDictionary new];
});
KeychainItemWrapper *keychain = nil;
@synchronized (rfcuKeyChains)
{
keychain = [rfcuKeyChains objectForKey: keyID];
if (keychain == nil)
{
keychain = [[KeychainItemWrapper alloc] initWithIdentifier: keyID accessGroup: nil];
[rfcuKeyChains setObject: keychain forKey: keyID];
}
}
return keychain;
}
そして、私はそれを次のように使用します:
KeychainItemWrapper *keychain = [RFCUtils keyChainWrapperForKeyID: keyID];
NSString *firstLaunch = [keychain objectForKey: (__bridge id)(kSecAttrAccount)];
if (firstLaunch == nil)
{
[keychain setObject: MY_APP_KEY forKey: (__bridge id)(kSecAttrAccount)];
}
(他の場所での同様の呼び出しなど)
上記のすべてのソリューションを試してみましたが、何もうまくいきませんでした。実際のデバイスでのみ機能し、シミュレータでは機能しませんでした。
それをシミュレーターで実行するための私の解決策は、「共有キーチェーン資格」をオンにすることでした。
私にもこの問題があり、アクセプターの回答とuseyourloafへの追加リンクのおかげで解決しました。
私が抱えていた問題は興味深いものでした。値を1つだけ保存する必要があり、それをフィールドkSecValueDataに格納することにしました。これは、Keychainの使用に関する他の投稿を見て、KeychainItemWrapperに進む前に独自の実装を開始したためです。これにより、次の問題が発生しました。最初にテストしたデバイス(iPadの第1世代)で、writeToKeychainでエラーが発生しました。デバイス(iPad 1st gen)を変更したところ、うまくいきました!最初のデバイスに戻りましたが、まだ機能しませんでした。
そのため、その時点で、デバイスのキーチェーンで以前に何か問題があり、簡単に元に戻すことができないことを知っていました。私が取得していたエラーコードは、writeToKeychainのSecItemCopyMatching(アイテムが見つからない)で-25300、SecItemAddで-25299の直後です。 (アイテムの複製)
この質問では、これはすべて理にかなっています。デバイスにはany新しいキーと一致するキーがありますが、KeychainItemWrapperはそれを削除できませんが、キーを取得できません。同じ値をフィールドkSecAttrAccountに追加するとすぐに、機能し始めました。
一言で言えば、この問題を抱えている他のユーザーにとって、あなたの問題は異なって見えるかもしれませんが、詳細に注意を払います。 -25300(アイテムが見つからない)に続いて-25299(アイテムが重複している)の場合。キーチェーンアイテムの一意性を定義するフィールドを設定していることを確認してください。 1つのデバイスで機能しない場合は、問題を1つのデバイスに特定できる可能性がある場合は、別のデバイスを試してください。 Apple keychainエラーコード: http://developer.Apple.com/library/ios/#documentation/Security/Reference/keychainservices/Reference/reference.html#//Apple_ref/doc/uid/TP30000898-CH5g-CJBEABHG (結果コードを検索)