2011-03-09 3 views
56

iOS 개발에 익숙합니다. 초보자 용 질문 인 경우 용서해주세요. 사용자의 이메일 주소와 비밀번호를 사용하는 앱의 간단한 인증 메커니즘이 있습니다. 또한 '기억하기'라는 스위치가 있습니다. 사용자가 해당 스위치를 토글하면 이메일/비밀번호를 유지하여 나중에 해당 필드가 자동으로 채워지도록 할 수 있습니다.iOS의 키 체인에 이메일/비밀번호 저장하기

나는 이것을 plist 파일에 저장하는 방법을 알고 있지만 암호가 암호화되지 않았기 때문에 이것이 최선의 방법은 아니라는 것을 알고 있습니다. 키 체인에 저장하기위한 샘플 코드를 찾았지만, 솔직히 말해서 나는 조금 잃어 버렸습니다. 아래 함수에 대해서는 어떻게 호출 할 지 모르며 이메일 주소도 저장하도록 수정하는 방법을 설명합니다.

가 나는 것입니다 전화 같은데요 : saveString(@"passwordgoeshere");

어떤 도움 주셔서 감사합니다!

+ (void)saveString:(NSString *)inputString forKey:(NSString *)account { 

    NSAssert(account != nil, @"Invalid account"); 
    NSAssert(inputString != nil, @"Invalid string"); 

    NSMutableDictionary *query = [NSMutableDictionary dictionary]; 

    [query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; 
    [query setObject:account forKey:(id)kSecAttrAccount]; 
    [query setObject:(id)kSecAttrAccessibleWhenUnlocked forKey:(id)kSecAttrAccessible]; 

    OSStatus error = SecItemCopyMatching((CFDictionaryRef)query, NULL); 
    if (error == errSecSuccess) { 
     // do update 
     NSDictionary *attributesToUpdate = [NSDictionary dictionaryWithObject:[inputString dataUsingEncoding:NSUTF8StringEncoding] 
                     forKey:(id)kSecValueData]; 

     error = SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attributesToUpdate); 
     NSAssert1(error == errSecSuccess, @"SecItemUpdate failed: %d", error); 
    } else if (error == errSecItemNotFound) { 
     // do add 
     [query setObject:[inputString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData]; 

     error = SecItemAdd((CFDictionaryRef)query, NULL); 
     NSAssert1(error == errSecSuccess, @"SecItemAdd failed: %d", error); 
    } else { 
     NSAssert1(NO, @"SecItemCopyMatching failed: %d", error); 
    } 
} 
+5

KeychainUserPass.m (나는이 대답에 연결하고 두 파일에 사용자를 언급,하지만 당신은 더 속성을 원하는 경우 알려 주시기 바랍니다). 또한 포맷팅을 약간 변경하여 메소드 이름을 좀 더 일반적인 것으로 변경했습니다. https://github.com/jeremangnr/JNKeychain – jere

답변

100

저는 NSCoding 호환 개체를 키 체인에 저장할 수있는 간단한 래퍼를 작성했습니다. 예를 들어 NSDictionary에 전자 메일과 암호를 저장하고이 클래스를 사용하여 NSDictionary를 키 체인에 저장할 수 있습니다.

SimpleKeychain.h

#import <Foundation/Foundation.h> 

@class SimpleKeychainUserPass; 

@interface SimpleKeychain : NSObject 

+ (void)save:(NSString *)service data:(id)data; 
+ (id)load:(NSString *)service; 
+ (void)delete:(NSString *)service; 

@end 

SimpleKeychain.m

#import "SimpleKeychain.h" 

@implementation SimpleKeychain 

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service { 
    return [NSMutableDictionary dictionaryWithObjectsAndKeys: 
      (id)kSecClassGenericPassword, (id)kSecClass, 
      service, (id)kSecAttrService, 
      service, (id)kSecAttrAccount, 
      (id)kSecAttrAccessibleAfterFirstUnlock, (id)kSecAttrAccessible, 
      nil]; 
} 

+ (void)save:(NSString *)service data:(id)data { 
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
    SecItemDelete((CFDictionaryRef)keychainQuery); 
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData]; 
    SecItemAdd((CFDictionaryRef)keychainQuery, NULL); 
} 

+ (id)load:(NSString *)service { 
    id ret = nil; 
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; 
    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; 
    CFDataRef keyData = NULL; 
    if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { 
     @try { 
      ret = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)keyData]; 
     } 
     @catch (NSException *e) { 
      NSLog(@"Unarchive of %@ failed: %@", service, e); 
     } 
     @finally {} 
    } 
    if (keyData) CFRelease(keyData); 
    return ret; 
} 

+ (void)delete:(NSString *)service { 
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
    SecItemDelete((CFDictionaryRef)keychainQuery); 
} 

@end 
+0

osx keycahin에서 작동합니까? – abe

+0

@abe : 시도해보십시오. 문서에 따르면 같은 기능을 모두 사용할 수 있지만'getKeychainQuery :'는 사전에 추가 매개 변수가 있어야합니다. – Anomie

+0

@Anomie @ Anomie 시도해 보았고 값 중 하나가 kSecClassGenericPassword 인 오류를 발견했지만 기초 프레임 워크에서 지원되는 것으로 보이는 설명서를 확인했습니다. 또한 보안 프레임 워크가 포함 된 y에 대한 모든 생각은 오류 일 수 있습니까? thx – abe

38

ARC 준비 코드 :

KeychainUserPass.h

#import <Foundation/Foundation.h> 

@interface KeychainUserPass : NSObject 

+ (void)save:(NSString *)service data:(id)data; 
+ (id)load:(NSString *)service; 
+ (void)delete:(NSString *)service; 

@end 

내가 ARC 작업과 Github에서에 넣어 아노미의 코드 @ 고정

#import "KeychainUserPass.h" 

@implementation KeychainUserPass 

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service { 
    return [NSMutableDictionary dictionaryWithObjectsAndKeys: 
      (__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass, 
      service, (__bridge id)kSecAttrService, 
      service, (__bridge id)kSecAttrAccount, 
      (__bridge id)kSecAttrAccessibleAfterFirstUnlock, (__bridge id)kSecAttrAccessible, 
      nil]; 
} 

+ (void)save:(NSString *)service data:(id)data { 
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery); 
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData]; 
    SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL); 
} 

+ (id)load:(NSString *)service { 
    id ret = nil; 
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 
    [keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 
    CFDataRef keyData = NULL; 
    if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { 
     @try { 
      ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData]; 
     } 
     @catch (NSException *e) { 
      NSLog(@"Unarchive of %@ failed: %@", service, e); 
     } 
     @finally {} 
    } 
    if (keyData) CFRelease(keyData); 
    return ret; 
} 

+ (void)delete:(NSString *)service { 
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery); 
} 

@end 
+3

ARC를 이용해 주셔서 감사합니다 준비가 된 버전. 그것은 매력처럼 작동합니다. –

+0

문제 없습니다 :) –

+0

flawless, thx !! – mnl

관련 문제