Es posible almacenar NSDictionary
en el llavero iPhone
, usando KeychainItemWrapper
(o sin)? Si no es posible, ¿tiene otra solución?Tienda NSDiccionario en llavero
Respuesta
Codificación: [dic description]
Decodificación: [dic propertyList]
No puedo antes de mañana ... – malinois
Puede almacenar cualquier cosa, solo necesita serializarlo.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dictionary];
Debería poder almacenar esa información en el llavero.
Debe serializar correctamente el NSDictionary
antes de almacenarlo en el llavero. Usando :
[dic description]
[dic propertyList]
que va a terminar con una colección de sólo NSDictionary
NSString
objetos. Si desea mantener los tipos de datos de los objetos, puede usar NSPropertyListSerialization
.
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"arbitraryId" accessGroup:nil]
NSString *error;
//The following NSData object may be stored in the Keychain
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[keychain setObject:dictionaryRep forKey:kSecValueData];
//When the NSData object object is retrieved from the Keychain, you convert it back to NSDictionary type
dictionaryRep = [keychain objectForKey:kSecValueData];
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:dictionaryRep mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
if (error) {
NSLog(@"%@", error);
}
El NSDictionary
devuelto por la segunda llamada a NSPropertyListSerialization
mantendrá los tipos de datos originales dentro de la colección NSDictionary
.
He editado el código para reflejar con mayor precisión cómo se usa con KeychainItemWrapper. –
Almacena los datos en 'kSecAttrService', que no es un campo encriptado. Creo que quisiste usar 'kSecValueData' aquí, que es la carga cifrada. –
Tu código no funciona en ios7 por algún motivo. Consideraría actualizarlo para ser más claro. Por ejemplo, dices que necesitamos usar [dic description] pero en tu ejemplo no hay una variable dic. – user798719
Descubrí que el envoltorio de llavero solo quiere cuerdas. Ni siquiera NSData. Entonces, para almacenar un diccionario, deberá hacer lo que Bret sugirió, pero con un paso adicional para convertir la serialización de NSData a una cadena. De esta manera:
NSString *error;
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:MY_STRING accessGroup:nil];
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dictToSave format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
NSString *xml = [[NSString alloc] initWithBytes:[dictionaryRep bytes] length:[dictionaryRep length] encoding:NSUTF8StringEncoding];
[keychain setObject:xml forKey:(__bridge id)(kSecValueData)];
lectura de vuelta:
NSError *error;
NSString *xml = [keychain objectForKey:(__bridge id)(kSecValueData)];
if (xml && xml.length) {
NSData *dictionaryRep = [xml dataUsingEncoding:NSUTF8StringEncoding];
dict = [NSPropertyListSerialization propertyListWithData:dictionaryRep options:NSPropertyListImmutable format:nil error:&error];
if (error) {
NSLog(@"%@", error);
}
}
No todos los datos son válidos UTF-8 por lo que esto no funcionará. La mejor opción es codificar a Base64. – zaph
Podría funcionar; después de todo, el XML comienza reclamando la codificación UTF-8, xml version = "1.0" encoding = "UTF-8"?>. Creo que Apple codifica datos como Base64 en el XML (ver https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/SerializePlist/SerializePlist.html para ver un ejemplo). Si eso falla, su respaldo a Base64 es una buena idea. –
Usando la dependencia KeychainItemWrapper
requiere modificar el código/muestra de la biblioteca para aceptar NSData
como la carga útil cifrada, que no es a prueba de futuro. Además, al hacer la secuencia de conversión NSDictionary > NSData > NSString
solo para que pueda usar KeychainItemWrapper
es ineficiente: KeychainItemWrapper
convertirá de nuevo su cadena en NSData
, para encriptarla.
Aquí hay una solución completa que resuelve lo anterior utilizando directamente la biblioteca de llavero. Se implementa como una categoría por lo que utilizar de esta manera:
// to store your dictionary
[myDict storeToKeychainWithKey:@"myStorageKey"];
// to retrieve it
NSDictionary *myDict = [NSDictionary dictionaryFromKeychainWithKey:@"myStorageKey"];
// to delete it
[myDict deleteFromKeychainWithKey:@"myStorageKey"];
y aquí está la Categoría:
@implementation NSDictionary (Keychain)
-(void) storeToKeychainWithKey:(NSString *)aKey {
// serialize dict
NSString *error;
NSData *serializedDictionary = [NSPropertyListSerialization dataFromPropertyList:self format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// encrypt in keychain
if(!error) {
// first, delete potential existing entries with this key (it won't auto update)
[self deleteFromKeychainWithKey:aKey];
// setup keychain storage properties
NSDictionary *storageQuery = @{
(id)kSecAttrAccount: aKey,
(id)kSecValueData: serializedDictionary,
(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlocked
};
OSStatus osStatus = SecItemAdd((CFDictionaryRef)storageQuery, nil);
if(osStatus != noErr) {
// do someting with error
}
}
}
+(NSDictionary *) dictionaryFromKeychainWithKey:(NSString *)aKey {
// setup keychain query properties
NSDictionary *readQuery = @{
(id)kSecAttrAccount: aKey,
(id)kSecReturnData: (id)kCFBooleanTrue,
(id)kSecClass: (id)kSecClassGenericPassword
};
NSData *serializedDictionary = nil;
OSStatus osStatus = SecItemCopyMatching((CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if(osStatus == noErr) {
// deserialize dictionary
NSString *error;
NSDictionary *storedDictionary = [NSPropertyListSerialization propertyListFromData:serializedDictionary mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
if(error) {
NSLog(@"%@", error);
}
return storedDictionary;
}
else {
// do something with error
return nil;
}
}
-(void) deleteFromKeychainWithKey:(NSString *)aKey {
// setup keychain query properties
NSDictionary *deletableItemsQuery = @{
(id)kSecAttrAccount: aKey,
(id)kSecClass: (id)kSecClassGenericPassword,
(id)kSecMatchLimit: (id)kSecMatchLimitAll,
(id)kSecReturnAttributes: (id)kCFBooleanTrue
};
NSArray *itemList = nil;
OSStatus osStatus = SecItemCopyMatching((CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
// each item in the array is a dictionary
for (NSDictionary *item in itemList) {
NSMutableDictionary *deleteQuery = [item mutableCopy];
[deleteQuery setValue:(id)kSecClassGenericPassword forKey:(id)kSecClass];
// do delete
osStatus = SecItemDelete((CFDictionaryRef)deleteQuery);
if(osStatus != noErr) {
// do something with error
}
[deleteQuery release];
}
}
@end
De hecho, se puede modificar fácilmente para almacenar cualquier tipo de objeto serializable en el llavero, no solo un diccionario. Simplemente realice una representación NSData
del objeto que desea almacenar.
Se realizaron algunos cambios menores en la categoría Dts. Convertido a ARC y utilizando NSKeyedArchiver para almacenar objetos personalizados.
@implementation NSDictionary (Keychain)
-(void) storeToKeychainWithKey:(NSString *)aKey {
// serialize dict
NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:self];
// encrypt in keychain
// first, delete potential existing entries with this key (it won't auto update)
[self deleteFromKeychainWithKey:aKey];
// setup keychain storage properties
NSDictionary *storageQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecValueData: serializedDictionary,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
};
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)storageQuery, nil);
if(osStatus != noErr) {
// do someting with error
}
}
+(NSDictionary *) dictionaryFromKeychainWithKey:(NSString *)aKey {
// setup keychain query properties
NSDictionary *readQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword
};
CFDataRef serializedDictionary = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if(osStatus == noErr) {
// deserialize dictionary
NSData *data = (__bridge NSData *)serializedDictionary;
NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return storedDictionary;
}
else {
// do something with error
return nil;
}
}
-(void) deleteFromKeychainWithKey:(NSString *)aKey {
// setup keychain query properties
NSDictionary *deletableItemsQuery = @{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue
};
CFArrayRef itemList = nil;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
// each item in the array is a dictionary
NSArray *itemListArray = (__bridge NSArray *)itemList;
for (NSDictionary *item in itemListArray) {
NSMutableDictionary *deleteQuery = [item mutableCopy];
[deleteQuery setValue:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// do delete
osStatus = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
if(osStatus != noErr) {
// do something with error
}
}
}
@end
I añadido soporte grupo de acceso y seguridad simulador para solución Amols:
//
// NSDictionary+SharedKeyChain.h
// LHSharedKeyChain
//
#import <Foundation/Foundation.h>
@interface NSDictionary (SharedKeyChain)
/**
* Returns a previously stored dictionary from the KeyChain.
*
* @param key NSString The name of the dictionary. There can be multiple dictionaries stored in the KeyChain.
* @param accessGroup NSString Access group for shared KeyChains, set to nil for no group.
*
* @return NSDictionary A dictionary that has been stored in the Keychain, nil if no dictionary for the key and accessGroup exist.
*/
+ (NSDictionary *)dictionaryFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
/**
* Deletes a previously stored dictionary from the KeyChain.
*
* @param key NSString The name of the dictionary. There can be multiple dictionaries stored in the KeyChain.
* @param accessGroup NSString Access group for shared KeyChains, set to nil for no group.
*/
+ (void)deleteFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
/**
* Save dictionary instance to the KeyChain. Any previously existing data with the same key and accessGroup will be overwritten.
*
* @param key NSString The name of the dictionary. There can be multiple dictionaries stored in the KeyChain.
* @param accessGroup NSString Access group for shared KeyChains, set to nil for no group.
*/
- (void)storeToKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
@end
//
// NSDictionary+SharedKeyChain.m
// LHSharedKeyChain
//
#import "NSDictionary+SharedKeyChain.h"
@implementation NSDictionary (SharedKeyChain)
- (void)storeToKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
{
// serialize dict
NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:self];
// encrypt in keychain
// first, delete potential existing entries with this key (it won't auto update)
[NSDictionary deleteFromKeychainWithKey:key accessGroup:accessGroup];
// setup keychain storage properties
NSDictionary *storageQuery = @{
(__bridge id)kSecAttrAccount: key,
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
(__bridge id)kSecAttrAccessGroup: accessGroup,
#endif
(__bridge id)kSecValueData: serializedDictionary,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
};
OSStatus status = SecItemAdd ((__bridge CFDictionaryRef)storageQuery, nil);
if (status != noErr)
{
NSLog (@"%d %@", (int)status, @"Couldn't save to Keychain.");
}
}
+ (NSDictionary *)dictionaryFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
{
// setup keychain query properties
NSDictionary *readQuery = @{
(__bridge id)kSecAttrAccount: key,
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
(__bridge id)kSecAttrAccessGroup: accessGroup,
#endif
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword
};
CFDataRef serializedDictionary = NULL;
OSStatus status = SecItemCopyMatching ((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if (status == noErr)
{
// deserialize dictionary
NSData *data = (__bridge NSData *)serializedDictionary;
NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return storedDictionary;
}
else
{
NSLog (@"%d %@", (int)status, @"Couldn't read from Keychain.");
return nil;
}
}
+ (void)deleteFromKeychainWithKey:(NSString *)key accessGroup:(NSString *)accessGroup;
{
// setup keychain query properties
NSDictionary *deletableItemsQuery = @{
(__bridge id)kSecAttrAccount: key,
#if TARGET_IPHONE_SIMULATOR
// Ignore the access group if running on the iPhone simulator.
//
// Apps that are built for the simulator aren't signed, so there's no keychain access group
// for the simulator to check. This means that all apps can see all keychain items when run
// on the simulator.
//
// If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the
// simulator will return -25243 (errSecNoAccessForItem).
#else
(__bridge id)kSecAttrAccessGroup: accessGroup,
#endif
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue
};
CFArrayRef itemList = nil;
OSStatus status = SecItemCopyMatching ((__bridge CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
// each item in the array is a dictionary
NSArray *itemListArray = (__bridge NSArray *)itemList;
for (NSDictionary *item in itemListArray)
{
NSMutableDictionary *deleteQuery = [item mutableCopy];
[deleteQuery setValue:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// do delete
status = SecItemDelete ((__bridge CFDictionaryRef)deleteQuery);
if (status != noErr)
{
NSLog (@"%d %@", (int)status, @"Couldn't delete from Keychain.");
}
}
}
@end
- 1. NSDiccionario a NSArray?
- 2. Obtener certificados en llavero
- 3. NSDiccionario de poligonal recursivo de estructura desconocida
- 4. NSDiccionario ordenar por claves como flotadores
- 5. Cómo guardar CFUUID en llavero
- 6. Llavero tipo iPhone en Android?
- 7. iOS guarde varias contraseñas en el llavero
- 8. ¿Puedo acceder al llavero en el iPhone?
- 9. ¿Funciona realmente el llavero en iOS?
- 10. Desbloquear llavero OSX sin contraseña?
- 11. Mac OS X Acceso a llavero: "No se pudo encontrar un llavero predeterminado" error
- 12. OCR un llavero RSA (token de seguridad)
- 13. Android Llavero para credenciales de usuario
- 14. Se le pide '(nulo)' Llavero GNOME
- 15. Importar claves RSA al llavero iPhone?
- 16. Cómo obtener atributos de elementos de llavero
- 17. Guardar una contraseña en llavero en el simulador
- 18. ¿Caché o tienda en sesión?
- 19. Interfaz de cacao con llavero MacOS X
- 20. valor predeterminado kSecAttrAccessible para elementos de llavero?
- 21. Restablecer Llavero de la aplicación de iPhone
- 22. Uso de un llavero no predeterminado en XCode
- 23. iOS: Certificado SSL de instalación previa en llavero - programáticamente
- 24. Hendidura del "Estilo Lisp" en los idiomas de llavero
- 25. Error al guardar en el llavero con iphone sdk
- 26. Enumerar todos los elementos de Llavero en mi aplicación iOS
- 27. ¿Qué significa la sintaxis de llavero $ {var%. *} En bash?
- 28. Tienda Android SQLite
- 29. tienda Extjs encontrar todos
- 30. iphone - certificado de distribución que no se muestra en el acceso al llavero
Sí, pero cuando leí los datos, tengo una referencia a un NSString vacía. – malinois