swiftkeychainwrapper - Agregar elementos y consultar el llavero de iOS con Swift
save key in keychain swift (5)
Creo que he encontrado la solución. He editado mi publicación anterior para incluir el código que funciona (al menos para mí). También he publicado un blog sobre esto aquí: usando el llavero iOS con Swift (código de ejemplo) .
Actualización 11 de agosto: he actualizado el código en la publicación del blog según los comentarios de rshelby. Echar un vistazo.
Actualización: convertí esto en una biblioteca de llaveros escrita en Swift llamada Locksmith .
Salvar
class func save(service: NSString, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
// Instantiate a new default keychain query
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
// Add the new keychain item
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
// Check that it worked ok
println("Saving status code is: /(status)")
}
Carga
class func load(service: NSString) -> AnyObject? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, kCFBooleanTrue, kSecMatchLimitOne], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit])
// I''m not too sure what''s happening here...
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
println("Loading status code is: /(status)")
// I''m not too sure what''s happening here...
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
println("Retrieved the following data from the keychain: /(retrievedData)")
var str = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
println("The decoded string is /(str)")
} else {
println("Nothing was retrieved from the keychain.")
}
return nil
}
Uso (ver controlador)
KeychainService.saveToken("sometoken")
KeychainService.loadToken()
que utiliza estos métodos de conveniencia
class func saveToken(token: NSString) {
self.save("service", data: token)
}
class func loadToken() {
var token = self.load("service")
if let t = token {
println("The token is: /(t)")
}
}
Esto conduce a la salida en la consola:
Saving status code is: 0
Loading status code is: 0
Retrieved the following data from the keychain: <736f6d65 746f6b65 6e>
The decoded string is sometoken
Tengo problemas para convertir todos los ejemplos de código de Objective C
que están disponibles para agregar datos y consultar datos desde el iOS Keychain
en Swift . Estoy intentando hacer un almacenamiento básico de una cadena (un token de acceso) y volver a leerlo. He echado un vistazo a algunas de las otras preguntas sobre el desbordamiento de pila, pero no logro que funcione. He intentado reconstruir una solución de varias fuentes.
Edición 1: lo intenté con una configuración más básica, porque pensé que mi auto.defaultKeychainQuery podría haber estado arruinando las cosas. He actualizado el código de abajo a la última versión.
Edición 2: Lo tengo funcionando. No estaba agregando el valor de datos a la consulta de guardar correctamente. Necesitaba convertir la cadena a NSData. He actualizado el código de abajo a la versión de trabajo más reciente.
Edición 3: como señala Xerxes a continuación, este código no funciona con versiones de Xcode superiores a Beta 1 debido a algún problema con los diccionarios. Si sabe de una solución para esto, hágamelo saber.
Actualización: convertí esto en una biblioteca de llaveros escrita en Swift llamada Locksmith .
Salvar
class func save(service: NSString, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
// Instantiate a new default keychain query
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
// Add the new keychain item
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
// Check that it worked ok
println("Saving status code is: /(status)")
}
Carga
class func load(service: NSString) -> AnyObject? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, kCFBooleanTrue, kSecMatchLimitOne], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit])
// I''m not too sure what''s happening here...
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
println("Loading status code is: /(status)")
// I''m not too sure what''s happening here...
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
println("Retrieved the following data from the keychain: /(retrievedData)")
var str = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
println("The decoded string is /(str)")
} else {
println("Nothing was retrieved from the keychain.")
}
return nil
}
Uso (ver controlador)
KeychainService.saveToken("sometoken")
KeychainService.loadToken()
que utiliza estos métodos de conveniencia
class func saveToken(token: NSString) {
self.save("service", data: token)
}
class func loadToken() {
var token = self.load("service")
if let t = token {
println("The token is: /(t)")
}
}
Esto conduce a la salida en la consola:
Saving status code is: 0
Loading status code is: 0
Retrieved the following data from the keychain: <736f6d65 746f6b65 6e>
The decoded string is sometoken
Muchas gracias por tu ayuda. No estoy muy seguro de qué hacer con dataTypeRef una vez que lo tengo, o si tiene algún dato dado el código anterior.
Escribí una aplicación de demostración y funciones de ayuda para esta sencilla tarea: escribir / leer una cadena de texto para una clave dada en Keychain.
https://github.com/marketplacer/keychain-swift
let keychain = KeychainSwift()
keychain.set("hello world", forKey: "my key")
keychain.get("my key")
keychain.delete("my key")
Mi interpretación sobre cómo agregar, obtener y eliminar contraseñas (para aquellos que son perezosos a usar las bibliotecas presentadas en este hilo):
// Saving password associated with the login and service
let userAccount = "user''s login"
let service = "service name"
let passwordData: NSData = self.textfield_password.text!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: userAccount,
kSecAttrService: service,
kSecValueData: passwordData]
SecItemDelete(keychainQuery as CFDictionaryRef) //Deletes the item just in case it already exists
let keychain_save_status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
print("Keychain saving code is: /(keychain_save_status)")
...
// Getting the password associated with the login and service
let userAccount = "user''s login"
let service = "service name"
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: userAccount,
kSecReturnData: kCFBooleanTrue,
kSecMatchLimit: kSecMatchLimitOne]
var rawResult: AnyObject?
let keychain_get_status: OSStatus = SecItemCopyMatching(keychainQuery, &rawResult)
print("Keychain getting code is: /(keychain_get_status)")
if (keychain_get_status == errSecSuccess) {
let retrievedData = rawResult as? NSData
let pass = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding)
print("Username: /(userAccount), password: /(pass!)")
// Do your work with the retrieved password here
} else {
print("No login data found in Keychain.")
...
//Deleting user''s credentials from Keychain. Password is optional for the query when you delete, in most cases you won''t know it after all.
let userAccount = "user''s login"
let service = "service name"
let keychainQuery: [NSString: NSObject] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: userAccount,
kSecAttrService: service]
let keychain_delete_status: OSStatus = SecItemDelete(keychainQuery as CFDictionaryRef)
print("Keychain deleting code is: /(keychain_delete_status)")
Los códigos de resultados y otra información útil se pueden encontrar en la documentación oficial: https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/
Para que esto funcione, deberá recuperar los valores retenidos de las constantes del llavero y almacenarlas primero de la siguiente manera:
let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString
A continuación, puede hacer referencia a los valores en el NSMutableDictionary así:
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
Escribí una publicación de blog al respecto en: http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/
¡Espero que esto ayude!
Rshelby
Para usuarios de Swift
Código de una sola línea para agregar / recuperar / actualizar campos en Llavero:
https://github.com/jrendel/SwiftKeychainWrapper
Uso
Agregue un valor de cadena a llavero:
let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey")
Recupere un valor de cadena de llavero:
let retrievedString: String? = KeychainWrapper.stringForKey("myKey")
Eliminar un valor de cadena de llavero:
let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey")