Save and Retrieve Value via Keychain

Save and Load from KeyChain | Swift

##Simplest Source##

import Foundation
import Security

// Constant Identifiers
let userAccount = "AuthenticatedUser"
let accessGroup = "SecuritySerivice"

/**
* User defined keys for new entry
* Note: add new keys for new secure item and use them in load and save methods
*/

let passwordKey = "KeyForPassword"

// Arguments for the keychain queries
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)

public class KeychainService: NSObject {

/**
* Exposed methods to perform save and load queries.
*/

public class func savePassword(token: NSString) {
self.save(passwordKey, data: token)
}

public class func loadPassword() -> NSString? {
return self.load(passwordKey)
}

/**
* Internal methods for querying the keychain.
*/

private class func save(service: NSString, data: NSString) {
let dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!

// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])

// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)

// Add the new keychain item
SecItemAdd(keychainQuery as CFDictionaryRef, nil)
}

private class func load(service: NSString) -> NSString? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])

var dataTypeRef :AnyObject?

// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: NSString? = nil

if status == errSecSuccess {
if let retrievedData = dataTypeRef as? NSData {
contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}

return contentsOfKeychain
}
}

##Example of Calling##

KeychainService.savePassword("Pa55worD")
let password = KeychainService.loadPassword() // password = "Pa55worD"

##SWIFT 4: VERSION WITH UPDATE AND REMOVE PASSWORD

import Cocoa
import Security

// see https://stackoverflow.com/a/37539998/1694526
// Arguments for the keychain queries
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)

public class KeychainService: NSObject {

class func updatePassword(service: String, account:String, data: String) {
if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {

// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue])

let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary)

if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Read failed: \(err)")
}
}
}
}


class func removePassword(service: String, account:String) {

// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue])

// Delete any existing items
let status = SecItemDelete(keychainQuery as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Remove failed: \(err)")
}
}

}


class func savePassword(service: String, account:String, data: String) {
if let dataFromString = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {

// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])

// Add the new keychain item
let status = SecItemAdd(keychainQuery as CFDictionary, nil)

if (status != errSecSuccess) { // Always check the status
if let err = SecCopyErrorMessageString(status, nil) {
print("Write failed: \(err)")
}
}
}
}

class func loadPassword(service: String, account:String) -> String? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])

var dataTypeRef :AnyObject?

// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: String?

if status == errSecSuccess {
if let retrievedData = dataTypeRef as? Data {
contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}

return contentsOfKeychain
}

}

You need to imagine the following wired up to a text input field and a label, then having four buttons wired up, one for each of the methods.

class ViewController: NSViewController {
@IBOutlet weak var enterPassword: NSTextField!
@IBOutlet weak var retrievedPassword: NSTextField!

let service = "myService"
let account = "myAccount"

// will only work after
@IBAction func updatePassword(_ sender: Any) {
KeychainService.updatePassword(service: service, account: account, data: enterPassword.stringValue)
}

@IBAction func removePassword(_ sender: Any) {
KeychainService.removePassword(service: service, account: account)
}

@IBAction func passwordSet(_ sender: Any) {
let password = enterPassword.stringValue
KeychainService.savePassword(service: service, account: account, data: password)
}

@IBAction func passwordGet(_ sender: Any) {
if let str = KeychainService.loadPassword(service: service, account: account) {
retrievedPassword.stringValue = str
}
else {retrievedPassword.stringValue = "Password does not exist" }
}
}

##Swift 5##
Kosuke's version for swift 5

import Security
import UIKit

class KeyChain {

class func save(key: String, data: Data) -> OSStatus {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ] as [String : Any]

SecItemDelete(query as CFDictionary)

return SecItemAdd(query as CFDictionary, nil)
}

class func load(key: String) -> Data? {
let query = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
kSecReturnData as String : kCFBooleanTrue!,
kSecMatchLimit as String : kSecMatchLimitOne ] as [String : Any]

var dataTypeRef: AnyObject? = nil

let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)

if status == noErr {
return dataTypeRef as! Data?
} else {
return nil
}
}

class func createUniqueID() -> String {
let uuid: CFUUID = CFUUIDCreate(nil)
let cfStr: CFString = CFUUIDCreateString(nil, uuid)

let swiftString: String = cfStr as String
return swiftString
}
}

extension Data {

init<T>(from value: T) {
var value = value
self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
}

func to<T>(type: T.Type) -> T {
return self.withUnsafeBytes { $0.load(as: T.self) }
}
}

Example usage:

let int: Int = 555
let data = Data(from: int)
let status = KeyChain.save(key: "MyNumber", data: data)
print("status: ", status)

if let receivedData = KeyChain.load(key: "MyNumber") {
let result = receivedData.to(type: Int.self)
print("result: ", result)
}

Save and retrieve value via KeyChain

Well, I just used out source etc and made my self nice helper :
Enjoy!

 class func save(key: String, data: NSData) {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ]

SecItemDelete(query as CFDictionaryRef)

let status: OSStatus = SecItemAdd(query as CFDictionaryRef, nil)

}

class func load(key: String) -> NSData? {
let query = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
kSecReturnData as String : kCFBooleanTrue,
kSecMatchLimit as String : kSecMatchLimitOne ]

var dataTypeRef :Unmanaged<AnyObject>?

let status: OSStatus = SecItemCopyMatching(query, &dataTypeRef)

if status == noErr {
return (dataTypeRef!.takeRetainedValue() as! NSData)
} else {
return nil
}

}

class func stringToNSDATA(string : String)->NSData
{
let _Data = (string as NSString).dataUsingEncoding(NSUTF8StringEncoding)
return _Data!

}

class func NSDATAtoString(data: NSData)->String
{
var returned_string : String = NSString(data: data, encoding: NSUTF8StringEncoding)! as String
return returned_string
}

class func intToNSDATA(r_Integer : Int)->NSData
{

var SavedInt: Int = r_Integer
let _Data = NSData(bytes: &SavedInt, length: sizeof(Int))
return _Data

}
class func NSDATAtoInteger(_Data : NSData) -> Int
{
var RecievedValue : Int = 0
_Data.getBytes(&RecievedValue, length: sizeof(Int))
return RecievedValue

}
class func CreateUniqueID() -> String
{
var uuid: CFUUIDRef = CFUUIDCreate(nil)
var cfStr:CFString = CFUUIDCreateString(nil, uuid)

var nsTypeString = cfStr as NSString
var swiftString:String = nsTypeString as String
return swiftString
}

//EXAMPLES
//
// //Save And Parse Int

// var Int_Data = KeyChain.intToNSDATA(555)
// KeyChain.save("MAMA", data: Int_Data)
// var RecievedDataAfterSave = KeyChain.load("MAMA")
// var NSDataTooInt = KeyChain.NSDATAtoInteger(RecievedDataAfterSave!)
// println(NSDataTooInt)
//
//
// //Save And Parse String

// var string_Data = KeyChain.stringToNSDATA("MANIAK")
// KeyChain.save("ZAHAL", data: string_Data)
// var RecievedDataStringAfterSave = KeyChain.load("ZAHAL")
// var NSDATAtoString = KeyChain.NSDATAtoString(RecievedDataStringAfterSave!)
// println(NSDATAtoString)

Storing Integer in Keychain Swift

Strictly spoken you cannot save integers in the keychain.

But you can convert the integer to String and store it either as password or along with other data in JSON format as Secure Note.

KeychainWrapper saving data and retrieving it in the same function isn't working

I think the problem is that you are treating requestMetaField as a synchronous function, when it is asynchronous. Your HTTP request will not finish until some time after requestMetaField returns.

Another approach would be to write a version of requestMetaField that takes a callback function that is called when the request completes. Then you can update your UI from the callback.

Here's what such a requestMetaField would look like:

func requestMetaField(metaField:String, callback: @escaping (String) -> Void) {

let userEmail: String = KeychainWrapper.standard.string(forKey: "email")!;
let userPassword: String = KeychainWrapper.standard.string(forKey: "password")!;
let meta = metaField;

// Send data to server side
// Classic HTTP POST Request using JSONSerialization here.

if(resultValue=="Success") {

// Save downloaded data to keychain
self.saveDataToKeychain(value: metaValue, key: meta);

// call callback with value
callback(metaValue)
}

The code in viewDidLoad would look like:

    requestMetaField(metaField: "first_name") { firstName in 
requestMetaField(metaField: "last_name") { lastName in
DispatchQueue.main.async {
userFullName.text = "\(firstName) \(lastName)"
}
}
}

requestMetaField(metaField: "mycred_default_total") { userMinuteBalance in
DispatchQueue.main.async {
userBalance.text = "\(userMinuteBalance) min"
}
}


Related Topics



Leave a reply



Submit