ページ

2010年2月26日金曜日

Keychain Services 調査 (2) サンプルコードを読む

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

まずはサンプルコードを読んでみる。

Cocoa Example Code: Drink Holder | Simple Keychain Example
http://homepage.mac.com/agerson/examples/keychain/

このページから SimpleKeychainExample をダウンロードして Xcodeでビルド&実行してみる。
username と password を keychain に登録・取得・削除などすることができるようだ。試しに登録(Add)してみた。

キーチェーンアクセス.app で見てみると:
登録できている。右クリックして詳しい情報を見てみる。
なるほど。いろいろな属性・オプションが設定できるようだ。

ソースコードを見てみよう。
AGKeychainController は名前の通りコントローラーの役割で、Keychain Services に関わる処理は AGKeychain が担当しているようだ。ヘッダファイルは次の通り。
※以下、ソースコードを引用

AGKeychain.h

@interface AGKeychain : NSObject {

}
+ (NSString *)getPasswordFromSecKeychainItemRef:(SecKeychainItemRef)item;
+ (BOOL)checkForExistanceOfKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username;
+ (BOOL)modifyKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withNewPassword:(NSString *)newPassword;
+ (BOOL)deleteKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username;
+ (BOOL)addKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withPassword:(NSString *)password;
+ (NSString *)getPasswordFromKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username;
+ (NSString *)getPasswordFromSecKeychainItemRef:(SecKeychainItemRef)item;
@end


実装ファイルのインポート部分。Security.h を読み込んでいる。
AGKeychain.m

#import "AGKeychain.h"

#import
#import

@implementation AGKeychain
 :


この中でみかける SecKeychain で始まる型や関数が Keychain Services の APIのようだ。

登録メソッドを見てみる。

+ (BOOL)addKeychainItem:(NSString *)keychainItemName withItemKind:(NSString *)keychainItemKind forUsername:(NSString *)username withPassword:(NSString *)password
{
SecKeychainAttribute attributes[3];
    SecKeychainAttributeList list;
    SecKeychainItemRef item;
    OSStatus status;
    attributes[0].tag = kSecAccountItemAttr;
    attributes[0].data = (void *)[username UTF8String];
    attributes[0].length = [username length];
    
    attributes[1].tag = kSecDescriptionItemAttr;
    attributes[1].data = (void *)[keychainItemKind UTF8String];
    attributes[1].length = [keychainItemKind length];
attributes[2].tag = kSecLabelItemAttr;
    attributes[2].data = (void *)[keychainItemName UTF8String];
    attributes[2].length = [keychainItemName length];

    list.count = 3;
    list.attr = attributes;

    status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &list, [password length], [password UTF8String], NULL,NULL,&item);
    if (status != 0) {
        NSLog(@"Error creating new item: %d\n", (int)status);
    }
return !status;
}


やっていることは多くない。
 1. SecKeychainAttribute で属性を用意する
 2. SecKeychainItemCreateFromContent( ) を呼び出す

簡単だ。Objective-C ベースでないのが残念(だからラッパーを作る人がいるわけだ)。

他の操作もコードは難しくない。

情報取得

    result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search);

    if (result != noErr) {
        NSLog (@"status %d from SecKeychainSearchCreateFromAttributes\n", result);
    }
    
while (SecKeychainSearchCopyNext (search, &item) == noErr) {
        CFRelease (item);
        numberOfItemsFound++;
    }

SecKeychainSearchCrateFromAttributes( ) で検索し、結果を SecKeychainSearchCopyNext() で回す。

削除

result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search);
while (SecKeychainSearchCopyNext (search, &item) == noErr) {
        numberOfItemsFound++;
    }
if (numberOfItemsFound) {
status = SecKeychainItemDelete(item);
}

まず検索して SecKeyChainItemRef を取得し、それを使って削除する。

変更

result = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &list, &search);
SecKeychainSearchCopyNext (search, &item);
    status = SecKeychainItemModifyContent(item, &list, [newPassword length], [newPassword UTF8String]);

削除同様、検索後に取得した SecKeyChainItemRef に対して変更をかける。

- - - -
操作自体は簡単なようだ。となると属性の意味や運用の仕方がポイントか。