Skip to content

Instantly share code, notes, and snippets.

@sketchytech
Created June 7, 2017 10:16
Simple password storage, update, retrieval and deletion in Keychain macOS
import Cocoa
import Security
// Adaptation of 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: NSString, account:NSString, data: NSString) {
let dataFromString: NSData = data.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)! as NSData
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary)
if (status != errSecSuccess) {
print("Read failed: \(SecCopyErrorMessageString(status, nil))")
}
}
class func removePassword(service: NSString, account:NSString) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
// Delete any existing items
let status = SecItemDelete(keychainQuery as CFDictionary)
if (status != errSecSuccess) {
print("Delete failed: \(SecCopyErrorMessageString(status, nil))")
}
}
class func savePassword(service: NSString, account:NSString, data: NSString) {
let dataFromString: NSData = data.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)! as NSData
// 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
print("Write failed: \(SecCopyErrorMessageString(status, nil)).")
}
}
class func loadPassword(service: NSString, account: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, 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: NSString? = nil
if status == errSecSuccess {
if let retrievedData = dataTypeRef as? NSData {
contentsOfKeychain = NSString(data: retrievedData as Data, encoding: String.Encoding.utf8.rawValue)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment