2010-11-29 22 views
27

Uso el Apple wraper for the keychain y trato de guardar un elemento en él (ejecutándolo en el simulador, ios 4.1).Error al guardar en el llavero con iphone sdk

No tengo experiencia con el llavero antes.

me sale este error:

Couldn't add the Keychain Item. Error - 25299

En línea 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."); 

Esto es como lo hago con los puños:

- (void) saveKey:(NSString *)key value:(NSString *)value { 
    KeychainItemWrapper *keyItem = [[KeychainItemWrapper alloc] initWithIdentifier:key accessGroup:nil]; 
    [keyItem setObject:value forKey:(id)kSecValueData]; 
    [keyItem release]; 
} 

Y esto son los valores que la API intenta guardar:

<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"} 
} 
+0

has resuelto el problema? –

+0

Estoy atascado con esto, así que creo que una recompensa es una buena idea. Mismo errorCode pero iOS4.3 Pero supongo que este es un problema PEBKAC, y no hay problema con el SDK. –

+0

Se me ocurrió este problema cuando agregué '[keychainItemData setObject: @" "forKey: (__ ID de puente) kSecAttrService];' en '- (void) resetKeychainItem'. Luego, cuando estaba leyendo la respuesta de user379075 a continuación, sonó una campana que, si la 'reinicia', también debe configurarla y v.v. – mrd3650

Respuesta

59

Sé que esto es de hace varios meses, pero tuve el mismo problema y fue doloroso, así que pensé que iba a compartir. Lo resuelto mediante la adición de esta línea:

[self.keychainItemWrapper setObject:@"MY_APP_CREDENTIALS" forKey:(id)kSecAttrService]; 
//@"MY_APP_CREDENTIALS" can be any string. 

encontré este blog entrada muy útiles: "En términos de bases de datos se podría pensar en su ser un índice único en los dos atributos kSecAttrAccount, kSecAttrService que requieren la combinación de los dos atributos para ser únicos para cada entrada en el llavero ". (desde http://useyourloaf.com/blog/2010/4/28/keychain-duplicate-item-when-adding-password.html).

Además, en el proyecto de ejemplo de Apple que usa este código, crean una instancia de KeychainItemWrapper en el delegado de la aplicación. No sé si es necesario, pero me gusta seguir sus ejemplos lo más cerca posible:

- (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]; 
} 

Creo que esto es un error en el código de contenedor. La lógica básicamente dice "¿Ya existe esta entrada? No, no. OK, la agregaré. Oops, no puedes agregarla porque ya está allí."

También puede ser necesario para establecer kSecAttrAccount, nunca he intentado sin establecer también este valor, ya que está destinado a guardar el nombre de usuario que va con la contraseña:

[self.wrapper setObject:txtUserName.text forKey:(id)kSecAttrAccount]; 
+0

Funcionó bien luego de agregar la instrucción MY_APP_CREDENTIALS. Muchas gracias :) – ilight

+1

Awesome! ¡Gracias! :-) – jpswain

+0

Es importante tener en cuenta, creo, que al buscar elementos de llavero, debe proporcionar SÓLO los atributos de cuenta y servicio para buscar. Si proporciona más atributos, el sistema buscará una coincidencia en todos ellos, por lo que es posible que no reciba nada incluso cuando ya exista un artículo para esa cuenta y servicio, por lo que intenta agregar e ingresar con esa cuenta y servicio al llavero fallará –

4

El llavero es un dolor total. En su lugar, debe usar la biblioteca Buzz Andersen's STUtils como un contenedor. Hará su vida sustancialmente más fácil. Nunca he tenido un problema con eso.

+2

Este proyecto github ahora ha quedado obsoleto; el autor sugiere https://github.com/ldandersen/STUtils/blob/master/Security como reemplazo. –

9

De acuerdo con the documentation, el error -25299 que está recibiendo es "errSecDuplicateItem", lo que significa que el elemento que está tratando de agregar ya existe. Mirando el código vinculado para KeychainItemWrapper, supongo que la llamada SecItemCopyMatching está fallando con un error que no sea errSecItemNotFound (-25300).

6

Puede almacenar y recuperar valores fácilmente con llavero usando SFHFKeychainUtils por Buzz Andersen.

  1. Descargar y copiar en su proyecto SFHFKeychainUtils.h y .m
  2. Añadir Security.framework a su carpeta de Marco
  3. Asegúrese de que estos archivos se añaden a su objetivo
  4. importación SFHFKeychainUtils.h donde desea usarlo

Este es un pequeño ejemplo de cómo usar esta biblioteca.

// 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]; 
+0

¿Qué es kStoredData en este contexto? Obtengo un error de compilación al intentar hacer esto. –

+0

En este caso, kStoredData es una clave definida ... Puede usar cualquier clave ... En este ejemplo, kStoreData es una clave para un nombre de servicio como @ "com.company.app.serviceName". Puede definir una clave como esta en su archivo .h y luego usarla para almacenar y recuperar sus datos – matteodv

0

tuve este problema también y lo resolvió gracias a la respuesta accepter y el enlace adicional para useyourloaf.

el problema que tenía era interesante, tenía que salvar a un solo valor y decidí guardarlo en el campo kSecValueData. Eso es porque vi otra mensajes sobre el uso del llavero y comencé mi propia implementación b antes de pasar a KeychainItemWrapper. Esto causó el siguiente problema: en el primer dispositivo que estaba probando (iPad 1st gen) recibí un error en writeToKeychain. Cambié el dispositivo (también ipad 1st gen) y funcionó! Volver al primer dispositivo todavía no funcionaba.

Así que supe que sabía que anteriormente había hecho algo mal en el llavero del dispositivo y no podía revertirlo fácilmente. Los códigos de error que obtenía eran: -25300 en SecItemCopyMatching de writeToKeychain (elemento no encontrado) y justo después de -25299 en SecItemAdd. (Duplicado artículo)

Con esta pregunta, todo esto tenía sentido: el dispositivo tiene una clave que coincida con cualquier duplicado de la llave, pero el KeychainItemWrapper no puede eliminarlo, pero la clave no puede ser recuperado. Tan pronto como agregué el mismo valor al campo kSecAttrAccount, comenzó a funcionar.

En resumen, para otros usuarios que tengan este problema, el problema puede ser diferente, pero preste atención a los detalles. Si tiene -25300 (elemento no encontrado) seguido de -25299 (artículo duplicado); asegúrese de establecer un campo que defina la singularidad de su elemento de llavero. Si no funciona en un dispositivo, pruebe con otro si puede, puede aislar el problema en un dispositivo. Apple keychain Códigos de error: http://developer.apple.com/library/ios/#documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/doc/uid/TP30000898-CH5g-CJBEABHG (busque códigos de resultado)

+0

¿Cómo puedo encontrar el artículo que tengo guardado anteriormente? He enumerado todos los artículos de esta manera http://stackoverflow.com/questions/10966969/enumerate-all-keychain-items-in-my-ios-application, y no hay ninguno. ¿Dónde se esconde? – below

1

Para mí, la solución fue que creé un KeychainItemWrapper "singleton" y lo uso en toda la aplicación. (En realidad, en mi caso, tenía un diccionario singleton lleno de KeychainItemWrapper -s, porque uso más de uno.)

Esto resolvió el problema donde estaba llegando a una ruta de código que efectivamente decía "¿existe este elemento? en el llavero? No? Entonces agrégalo. ¡Vaya! NSAssert() que estoy tratando de agregar un artículo que ya existe (Error -25299) "

Aunque no estoy seguro, sospecho que el problema tiene que ver con sincronización de llavero. He tenido problemas similares con NSUserDefaults, cuando escribo en NSUD, en otro lugar del código, obtengo el standardUserDefaults y leo de ellos, y la actualización aún no se ha realizado (porque aún no lo hice [ud synchronize])

en el código, mi rutina es así:

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

y lo uso como esto: (., etc., llamadas similares en otros lugares)

KeychainItemWrapper *keychain = [RFCUtils keyChainWrapperForKeyID: keyID]; 
NSString *firstLaunch = [keychain objectForKey: (__bridge id)(kSecAttrAccount)]; 
if (firstLaunch == nil) 
{ 
    [keychain setObject: MY_APP_KEY forKey: (__bridge id)(kSecAttrAccount)]; 
} 

0

Intenté todas las soluciones, escuche arriba, pero nada funcionó para mí. Solo funcionaba en un dispositivo real pero no en el simulador.

Mi solución para ejecutarlo en el simulador fue activar "Compartir derechos de llavero".

Share Keychain entitlement

Cuestiones relacionadas