2011-03-09 11 views
56

Sono molto nuovo nello sviluppo di iOS, quindi perdonatemi se questa è una domanda per principianti. Ho un semplice meccanismo di autenticazione per la mia app che accetta l'indirizzo e-mail e la password di un utente. Ho anche un interruttore che dice "Ricordami". Se l'utente attiva questa opzione, vorrei conservare la loro email/password in modo da poterli compilare automaticamente in futuro.Salvataggio di email/password sul portachiavi in ​​iOS

Ho ottenuto che questo funzioni con il salvataggio in un file plist ma so che non è l'idea migliore poiché la password non è criptata. Ho trovato un codice di esempio per il salvataggio sul portachiavi, ma ad essere onesti, sono un po 'perso. Per la funzione seguente, non sono sicuro di come chiamarlo e come modificarlo per salvare anche l'indirizzo email.

Sto indovinando chiamare sarebbe: saveString(@"passwordgoeshere");

Grazie per tutto l'aiuto !!!

+ (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

ho fissato @ codice di Anomie a lavorare con ARC e metterlo su Github (ho collegato a questa risposta e citato l'utente in entrambi i file, ma se si vuole ulteriormente attribuzione fatemelo sapere). Ho anche modificato la formattazione un po 'e reso i nomi dei metodi un po' più generici. https://github.com/jeremangnr/JNKeychain – jere

risposta

100

Ho scritto un semplice wrapper che consente il salvataggio di qualsiasi oggetto compatibile con NSCoding sul portachiavi. Ad esempio, è possibile memorizzare l'e-mail e la password in un NSDictionary e memorizzare NSDictionary sul portachiavi utilizzando questa classe.

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

questo dovrebbe funzionare per un osx keycahin? – abe

+0

@abe: Provalo e vedi? Secondo la documentazione sono disponibili tutte le stesse funzioni, ma è possibile 'getKeychainQuery:' ha bisogno di avere parametri aggiuntivi nel dizionario. – Anomie

+0

@Anomie ho fatto un tentativo e ho trovato errori sui valori, uno dei quali è kSecClassGenericPassword ma ho controllato la documentazione che sembra essere il supporto nel framework di base ho anche incluso il framework di sicurezza qualsiasi idea su di esso potrebbe errore? grazie – abe

38

ARC Codice pronto:

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 

KeychainUserPass.m

#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

Grazie per l'ARC versione pronta. Esso funziona magicamente. –

+0

Non è un problema :) –

+0

impeccabile, grazie !! – mnl

Problemi correlati