ページ

2010年3月11日木曜日

Keychain Services 調査 (11) コーディング #1 パスワードを格納する

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

(前回)Cocoaの日々: Keychain Services 調査 (10) ID/パスワード入力例

さてそろそろコーディングに入ろう。検証のゴールは以前描いた処理フローを一通り確認できるようなサンプルアプリを作る。

(参考)Cocoaの日々: Keychain Services 調査 (9) 処理フロー


今回はまずパスワードの格納をやってみる。Mac Dev Center のコードを参考にしながら進めてみた。
Mac Dev Center: Keychain Services Programming Guide: Keychain Services Tasks for Mac OS X


機能は単純で "TEST"ボタンを押すと、Keychain に決まったパスワードを登録するというもの。

AppDelegate はこんな感じ。

KeychainSample1AppDelegate.m

@implementation KeychainSample1AppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application 
}

-(IBAction)test:(id)sender
{
BOOL result = [[KeychainManager sharedManager]
  storePasswordWithServiceName:@"TEST-1"
  accountName:@"hashiguchi"
  password:@"1234567"];
NSLog(@"result=%d", result);
}

@end

KeychainManager というクラスを用意し、そこで用意したパスワード格納メソッドを呼んでいる。

KeychainManager.m

@implementation KeychainManager

#pragma mark -
#pragma mark Initilizer and Deallocation
static KeychainManager* _sharedManager = nil;

+ (KeychainManager*)sharedManager
{
if (!_sharedManager) {
_sharedManager = [[KeychainManager alloc] init];
}
return _sharedManager;
}

- (void) dealloc
{
[super dealloc];
}

#pragma mark -
#pragma mark Store & Get a password

-(BOOL)storePasswordWithServiceName:(NSString*)serviceName accountName:(NSString*)accountName password:(NSString*)password
{
OSStatus status;
const char *serviceNameUTF8 = [serviceName UTF8String];
const char *accountNameUTF8 = [accountName UTF8String];
const char *passwordUTF8 = [password UTF8String];

status = SecKeychainAddGenericPassword(NULL,
   strlen(serviceNameUTF8),
   serviceNameUTF8,
   strlen(accountNameUTF8),
   accountNameUTF8,
   strlen(passwordUTF8),
   passwordUTF8,
   NULL);
if (status == errSecSuccess) {
return YES;
} else {
NSLog(@"ERROR:SecKeychainAddGenericPassword:%d", status);
return NO;
}
}

KeychainManagerはシングルトンとした。

格納には SecKeychainAddGenericPassword を使う。
Mac Dev Center: Keychain Services Reference - SecKeychainAddGenericPassword
OSStatus SecKeychainAddGenericPassword (
   SecKeychainRef keychain,
   UInt32 serviceNameLength,
   const char *serviceName,
   UInt32 accountNameLength,
   const char *accountName,
   UInt32 passwordLength,
   const void *passwordData,
   SecKeychainItemRef *itemRef
);
Sec系関数は Objective-C ではなくCの関数なので若干の変換が必要になる。文字列はすべて UTF8 変換した char配列とする。

なおビルドする時には Security.framework をリンクする必要がある。

これが無いとビルド時に怒られる。

さて実行してみよう。起動して "TEST"ボタンを押して Keychainへパスワードを登録する。その後、キーチェーン.app で探してみると...あった。
属性を見てみる。
名前と場所に serviceName が入っている。種類は自動的に「アプリケーションパスワード」が設定されるようだ。「パスワードを表示」にチェックを入れると Keychainのパスワード入力が求められた。

パスワードを入力すると、プログラムで設定した文字列が表示された。正しく格納されたようだ。
アクセス制御では、プログラムが自動的に信頼されたアプリの一覧に追加されている。



なおもう一度 "TEST"ボタンを押すと(すなわち SecKeychainAddGenericPassword を2度実行すると)エラーとなった。

KeychainSample1[10105:80f] result=1
KeychainSample1[10105:80f] ERROR:SecKeychainAddGenericPassword:-25299
KeychainSample1[10105:80f] result=0


Mac Dev Center: Keychain Services Reference
上記によれば、エラーコード -25299 は


errSecDuplicateItem–25299
The item already exists.
Available in Mac OS X v10.2 and later.