Created
June 7, 2017 10:16
Simple password storage, update, retrieval and deletion in Keychain macOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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