What's the easiest way to programmatically (from within my app) get all items stored in the keychain?
It probably has something to do with SecItemCopyMatching(), but the documentation for that function is not very clear (and I failed to find a decent sample on the web).
SecItemCopyMatching is the right call for that. First we build our query dictionary so that the items' attributes are returned in dictionaries, and that all items are returned:
NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
(__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
nil];
As SecItemCopyMatching requires at least the class of the returned SecItems, we create an array with all the classes…
NSArray *secItemClasses = [NSArray arrayWithObjects:
(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity,
nil];
...and for each class, set the class in our query, call SecItemCopyMatching, and log the result.
for (id secItemClass in secItemClasses) {
[query setObject:secItemClass forKey:(__bridge id)kSecClass];
CFTypeRef result = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
NSLog(#"%#", (__bridge id)result);
if (result != NULL) CFRelease(result);
}
In production code, you should check that the OSStatus returned by SecItemCopyMatching is either errSecItemNotFound (no items found) or errSecSuccess (at least one item was found).
A Swift 4 update to #Cosmin's Swift 3 answer.
open func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : kCFBooleanTrue,
kSecReturnAttributes as String : kCFBooleanTrue,
kSecReturnRef as String : kCFBooleanTrue,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
return values
}
Swift 3+ version that returns also the keys (kSecAttrAccount):
open func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
let query: [String: Any] = [
kSecClass : secClass,
kSecReturnData : kCFBooleanTrue,
kSecReturnAttributes : kCFBooleanTrue,
kSecReturnRef : kCFBooleanTrue,
kSecMatchLimit : kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount] as? String,
let value = item[kSecValueData] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
return values
}
The other Swift code snippets all seem a bit convoluted. You don't really have to mess around the MutablePointers that much, and you probably want to have proper error management. I implemented my version in Swift just by tweaking the code in the Apple documentation. Here it is in for those using Xcode 11.
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword, // change the kSecClass for your needs
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: true,
kSecReturnRef as String: true]
var items_ref: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &items_ref)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
let items = items_ref as! Array<Dictionary<String, Any>>
// Now loop over the items and do something with each item
for item in items {
// Sample code: prints the account name
print(item[kSecAttrAccount as String] as? String)
}
Swift 3 version with xcode 9.1
func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : kCFBooleanTrue,
kSecReturnAttributes as String : kCFBooleanTrue,
kSecReturnRef as String : kCFBooleanTrue,
kSecMatchLimit as String : kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String:String]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8)
}
}
}
return values
}
Can be called like :
debugPrint(getAllKeyChainItemsOfClass(kSecClassGenericPassword as String))
Updated to include kSecClassIdentity and kSecClassCertificate information in dictionary
I also don't think calling withUnsafeMutablePointer(to:_:) is necessary.
func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:AnyObject] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : true,
kSecReturnAttributes as String : true,
kSecReturnRef as String : true,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
// this also works, although I am not sure if it is as save as calling withUnsafeMutabePointer
// let lastResultCode = SecItemCopyMatching(query as CFDictionary, &result)
var values = [String: AnyObject]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8) as AnyObject?
}
// including identities and certificates in dictionary
else if let key = item[kSecAttrLabel as String] as? String,
let value = item[kSecValueRef as String] {
values[key] = value as AnyObject
}
}
}
return values
}
Related
I am looking for a way to share account credentials like userID and accessToken between my iOS app and my WatchOS app. As i understand i can use the WatchConnectivity framework but that does not seem reliable. For example if my iOS app is killed i could not find a way to force wake the app to fetch the data.
The other think that i tried was keychain sharing which also does not seem to work and gives
keyStore.retrieve SecItemCopyMatching error -25300
error
Below is the code that i am using, which i got from this tutorial.
class KeyStore {
let account = "accessToken"
let group = "[TeamID].[BundleID]" //Setup same as in keychain sharing options in capabilities
func store(token : String) {
let data = token.data(using: .utf8)!
let addquery: [String: Any] = [kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccount as String: account,
kSecValueData as String: data,
kSecAttrSynchronizable as String : kCFBooleanTrue!,
kSecAttrAccessGroup as String : group
]
SecItemDelete(addquery as CFDictionary)
let status : OSStatus = SecItemAdd(addquery as CFDictionary, nil)
guard status == errSecSuccess else {
os_log("store: whoops")
return
}
}
func clear() {
let addquery: [String: Any] = [kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccount as String: account,
kSecAttrSynchronizable as String : kCFBooleanTrue!,
kSecAttrAccessGroup as String : group
]
SecItemDelete(addquery as CFDictionary)
}
func retrieve() -> String? {
let getquery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: kCFBooleanTrue!,
kSecMatchLimit as String : kSecMatchLimitOne,
kSecAttrSynchronizable as String : kCFBooleanTrue!,
kSecAttrAccessGroup as String : group
]
var item: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &item)
guard status == errSecSuccess else {
os_log("keyStore.retrieve SecItemCopyMatching error \(status)")
return nil
}
guard let data = item as? Data? else {
os_log("keyStore.retrieve not data")
return nil
}
return String(data: data!, encoding: String.Encoding.utf8)
}
func getAllKeychainItems() throws {
let classes = [kSecClassGenericPassword as String, // Generic password items
kSecClassInternetPassword as String, // Internet password items
kSecClassCertificate as String, // Certificate items
kSecClassKey as String, // Cryptographic key items
kSecClassIdentity as String,
kSecAttrAccount as String] // Identity items
classes.forEach { secClass in
let items = getAllKeyChainItemsOfClass( secClass )
NSLog(items.description)
}
}
func getAllKeyChainItemsOfClass(_ secClass: String) -> [String: AnyObject] {
let query: [String: Any] = [
kSecClass as String : secClass,
kSecReturnData as String : true,
kSecReturnAttributes as String : true,
kSecReturnRef as String : true,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let lastResultCode = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
var values = [String: AnyObject]()
if lastResultCode == noErr {
let array = result as? Array<Dictionary<String, Any>>
for item in array! {
if let key = item[kSecAttrAccount as String] as? String,
let value = item[kSecValueData as String] as? Data {
values[key] = String(data: value, encoding:.utf8) as AnyObject?
}
else if let key = item[kSecAttrLabel as String] as? String,
let value = item[kSecValueRef as String] {
values[key] = value as AnyObject
}
}
}
return values
}
}
Any references that could help me share login credentials even when the app is killed would really help
I'm trying to store an Integer and retrieve it using KeyChain.
This is how I save it:
func SaveNumberOfImagesTaken()
{
let key = "IMAGE_TAKEN"
var taken = 10
let data = NSKeyedArchiver.archivedDataWithRootObject(taken)
let query : [String:AnyObject] = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
kSecValueData as String : data
]
let status : OSStatus = SecItemAdd(query as CFDictionaryRef, nil)
}
This is how I try to retrieve it:
func CheckIfKeyChainValueExitss() -> AnyObject? {
var key = "IMAGE_TAKEN"
let query : [String:AnyObject] = [
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 let op = dataTypeRef?.toOpaque() {
let data = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
if let string: AnyObject? = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? AnyObject? {
if key == "IMAGE_TAKEN"
{
return string as! String!
}
else if string == nil
{
return nil
}
}
}
return nil
}
I'm getting the following error:
Could not cast value of type '__NSCFNumber' to 'NSString'
I tried playing with the variables but without success.
I've update Eric's version for Swift 5:
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) }
}
}
I've update Eric's version for Swift 3:
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.pointee }
}
}
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)
}
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)
This is Sazzad Hissain Khan's answer rewritten for iOS without non-Swifty NS-prefixed attributes and a cleaner code.
import Security
class KeychainService {
class func updatePassword(service: String, account: String, data: String) {
guard let dataFromString = data.data(using: .utf8, allowLossyConversion: false) else {
return
}
let status = SecItemUpdate(modifierQuery(service: service, account: account), [kSecValueData: dataFromString] as CFDictionary)
checkError(status)
}
class func removePassword(service: String, account: String) {
let status = SecItemDelete(modifierQuery(service: service, account: account))
checkError(status)
}
class func savePassword(service: String, account: String, data: String) {
guard let dataFromString = data.data(using: .utf8, allowLossyConversion: false) else {
return
}
let keychainQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: dataFromString]
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
checkError(status)
}
class func loadPassword(service: String, account: String) -> String? {
var dataTypeRef: CFTypeRef?
let status = SecItemCopyMatching(modifierQuery(service: service, account: account), &dataTypeRef)
if status == errSecSuccess,
let retrievedData = dataTypeRef as? Data {
return String(data: retrievedData, encoding: .utf8)
} else {
checkError(status)
return nil
}
}
fileprivate static func modifierQuery(service: String, account: String) -> CFDictionary {
let keychainQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecReturnData: kCFBooleanTrue]
return keychainQuery as CFDictionary
}
fileprivate static func checkError(_ status: OSStatus) {
if status != errSecSuccess {
if #available(iOS 11.3, *),
let err = SecCopyErrorMessageString(status, nil) {
print("Operation failed: \(err)")
} else {
print("Operation failed: \(status). Check the error message through https://osstatus.com.")
}
}
}
}
Roi Mulia's answer works very well, here's a version with a few minimal adjustments for Swift 2:
class KeyChain {
class func save(key: String, data: NSData) -> OSStatus {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ]
SecItemDelete(query as CFDictionaryRef)
return 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:AnyObject? = nil
let status: OSStatus = SecItemCopyMatching(query, &dataTypeRef)
if status == noErr {
return (dataTypeRef! 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
{
let 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
{
let uuid: CFUUIDRef = CFUUIDCreate(nil)
let cfStr:CFString = CFUUIDCreateString(nil, uuid)
let nsTypeString = cfStr as NSString
let swiftString:String = nsTypeString as String
return swiftString
}
}
Example usage:
let data = KeyChain.intToNSDATA(555)
let status = KeyChain.save("MyNumber", data: data)
print(status)
if let receivedData = KeyChain.load("MyNumber") {
let result = KeyChain.NSDATAtoInteger(receivedData)
print(result)
}
I tried to make it as simple as possible.
fileprivate class KeychainService {
static func updatePassword(_ password: String, serviceKey: String) {
guard let dataFromString = password.data(using: .utf8) else { return }
let keychainQuery: [CFString : Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: serviceKey,
kSecValueData: dataFromString]
SecItemDelete(keychainQuery as CFDictionary)
SecItemAdd(keychainQuery as CFDictionary, nil)
}
static func removePassword(serviceKey: String) {
let keychainQuery: [CFString : Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: serviceKey]
SecItemDelete(keychainQuery as CFDictionary)
}
static func loadPassword(serviceKey: String) -> String? {
let keychainQuery: [CFString : Any] = [kSecClass : kSecClassGenericPassword,
kSecAttrService : serviceKey,
kSecReturnData: kCFBooleanTrue,
kSecMatchLimitOne: kSecMatchLimitOne]
var dataTypeRef: AnyObject?
SecItemCopyMatching(keychainQuery as CFDictionary, &dataTypeRef)
guard let retrievedData = dataTypeRef as? Data else { return nil }
return String(data: retrievedData, encoding: .utf8)
}
static func flush() {
let secItemClasses = [kSecClassGenericPassword]
for itemClass in secItemClasses {
let spec: NSDictionary = [kSecClass: itemClass]
SecItemDelete(spec)
}
}
}
Example how to save & retrieve a struct User, a pretty common use-case:
import Security
import UIKit
class KeyChain {
struct User {
let identifier: Int64
let password: String
}
private static let service = "MyService"
static func save(user: User) -> Bool {
let identifier = Data(from: user.identifier)
let password = user.password.data(using: .utf8)!
let query = [kSecClass as String : kSecClassGenericPassword as String,
kSecAttrService as String : service,
kSecAttrAccount as String : identifier,
kSecValueData as String : password]
as [String : Any]
let deleteStatus = SecItemDelete(query as CFDictionary)
if deleteStatus == noErr || deleteStatus == errSecItemNotFound {
return SecItemAdd(query as CFDictionary, nil) == noErr
}
return false
}
static func retrieveUser() -> User? {
let query = [kSecClass as String : kSecClassGenericPassword,
kSecAttrService as String : service,
kSecReturnAttributes as String : kCFBooleanTrue!,
kSecReturnData as String: kCFBooleanTrue!]
as [String : Any]
var result: AnyObject? = nil
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == noErr,
let dict = result as? [String: Any],
let passwordData = dict[String(kSecValueData)] as? Data,
let password = String(data: passwordData, encoding: .utf8),
let identifier = (dict[String(kSecAttrAccount)] as? Data)?.to(type: Int64.self) {
return User(identifier: identifier, password: password)
} else {
return nil
}
}
}
private 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 {
withUnsafeBytes { $0.load(as: T.self) }
}
}
You are storing a number, not a string, so you are getting back an NSNumber, not a string. The exception is pretty clear - you can't downcast an NSNumber to a String - you can use stringValue() to get the string representation of an NSNumber
if let op = dataTypeRef?.toOpaque() {
let data = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
if let string: AnyObject? = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? AnyObject? {
if key == "IMAGE_TAKEN"
{
return string.stringValue() as! String!
}
else if string == nil
{
return nil
}
}
}
I am developing an iOS project with XCode8 + Swift3.
I have created the following two functions to store string to keychain and read it back from keychain:
var query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "my service",
kSecAttrAccount as String: "my-key"
]
func storeString(value: String) -> Bool {
if let data = value.data(using: .utf8) {
// delete data if exist
SecItemDelete(query as CFDictionary)
// add value to query
query[kSecValueData as String] = data
// add to keychain
let status = SecItemAdd(query as CFDictionary, nil)
return status == noErr
}
return false
}
func readString() -> String? {
// update query
query[kSecReturnData as String] = kCFBooleanTrue
query[kSecMatchLimit as String] = kSecMatchLimit
var result: AnyObject?
// fetch items from keychain
let status: OSStatus = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
// I always get error -50 here
if status == noErr {
if let resultData = result as? Data {
if let strVal = String(data: resultData, encoding: .utf8) {
return strVal
}
}
}
return nil
}
I invoke the functions:
boolean hasStored = storeString(value: "John")
let readVal = readString()
I got hasStored is true, but readVal always nil. I investigated my functions, I see I always get error -50 as status code in reader function (see my comment in my function).
Why? Why I can't read the value I stored in keychain (I am not sure whether it is really stored but I do get status == noErr always retuns true in storeString(value:) function)
There is a typo in your code:
query[kSecMatchLimit as String] = kSecMatchLimit
// ^~~~~~~~~~~~~~
kSecMatchLimit is the key and not a valid value. The expected value should be a CFNumber or kSecMatchLimitOne or kSecMatchLimitAll. If you expect a single item to be returned, use kSecMatchLimitOne. See also Search Attribute Keys and Values.
I am trying to upgrade the code that I got from this answer for generating CSR, from Swift 2 to Swift 3.
I have most of the code upgraded, but the following code in the Utility block of the original answer failed with the error:
'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to temporarily view memory as another layout-compatible type.
The error occurs at the line:
let status: OSStatus = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as NSDictionary, UnsafeMutablePointer($0)) }
func loadKeySecKeyFromKeyChain(key: String) -> SecKey{
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): KEY_SIZE as AnyObject,
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): key as AnyObject,
kSecReturnRef as String : kCFBooleanTrue ]
var dataTypeRef: Unmanaged<AnyObject>? = nil
var resultData: SecKey? = nil
let status: OSStatus = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as NSDictionary, UnsafeMutablePointer($0)) }
NSLog("SecItemCopyMatching: " + status.description)
if status == errSecSuccess {
NSLog("private or public debug description is: " + dataTypeRef.debugDescription)
resultData = (dataTypeRef!.takeRetainedValue() as! SecKey)
NSLog("SecItemCopyMatching returns SecKey: " + resultData.debugDescription)
return resultData!
} else {
return resultData!
}
}
I have been stuck on this for a whole day, is there any suggestions for how to resolve this error?
Just use SecItemCopyMatching. I was able to convert it to Swift 3 and successfully generate CSR.
// Finds the SecKeyRef corresponding to the parameter key and returns it
func loadKeySecKeyFromKeyChain(key: String) -> SecKey {
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): KEY_SIZE as AnyObject,
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): key as AnyObject,
kSecReturnRef as String : kCFBooleanTrue ]
var dataTypeRef: Unmanaged<AnyObject>? = nil
var resultData: SecKey? = nil
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess {
resultData = result as! SecKey
return resultData!
} else {
return resultData!
}
}
I'm trying to store an Integer and retrieve it using KeyChain.
This is how I save it:
func SaveNumberOfImagesTaken()
{
let key = "IMAGE_TAKEN"
var taken = 10
let data = NSKeyedArchiver.archivedDataWithRootObject(taken)
let query : [String:AnyObject] = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
kSecValueData as String : data
]
let status : OSStatus = SecItemAdd(query as CFDictionaryRef, nil)
}
This is how I try to retrieve it:
func CheckIfKeyChainValueExitss() -> AnyObject? {
var key = "IMAGE_TAKEN"
let query : [String:AnyObject] = [
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 let op = dataTypeRef?.toOpaque() {
let data = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
if let string: AnyObject? = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? AnyObject? {
if key == "IMAGE_TAKEN"
{
return string as! String!
}
else if string == nil
{
return nil
}
}
}
return nil
}
I'm getting the following error:
Could not cast value of type '__NSCFNumber' to 'NSString'
I tried playing with the variables but without success.
I've update Eric's version for Swift 5:
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) }
}
}
I've update Eric's version for Swift 3:
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.pointee }
}
}
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)
}
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)
This is Sazzad Hissain Khan's answer rewritten for iOS without non-Swifty NS-prefixed attributes and a cleaner code.
import Security
class KeychainService {
class func updatePassword(service: String, account: String, data: String) {
guard let dataFromString = data.data(using: .utf8, allowLossyConversion: false) else {
return
}
let status = SecItemUpdate(modifierQuery(service: service, account: account), [kSecValueData: dataFromString] as CFDictionary)
checkError(status)
}
class func removePassword(service: String, account: String) {
let status = SecItemDelete(modifierQuery(service: service, account: account))
checkError(status)
}
class func savePassword(service: String, account: String, data: String) {
guard let dataFromString = data.data(using: .utf8, allowLossyConversion: false) else {
return
}
let keychainQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: dataFromString]
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
checkError(status)
}
class func loadPassword(service: String, account: String) -> String? {
var dataTypeRef: CFTypeRef?
let status = SecItemCopyMatching(modifierQuery(service: service, account: account), &dataTypeRef)
if status == errSecSuccess,
let retrievedData = dataTypeRef as? Data {
return String(data: retrievedData, encoding: .utf8)
} else {
checkError(status)
return nil
}
}
fileprivate static func modifierQuery(service: String, account: String) -> CFDictionary {
let keychainQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecReturnData: kCFBooleanTrue]
return keychainQuery as CFDictionary
}
fileprivate static func checkError(_ status: OSStatus) {
if status != errSecSuccess {
if #available(iOS 11.3, *),
let err = SecCopyErrorMessageString(status, nil) {
print("Operation failed: \(err)")
} else {
print("Operation failed: \(status). Check the error message through https://osstatus.com.")
}
}
}
}
Roi Mulia's answer works very well, here's a version with a few minimal adjustments for Swift 2:
class KeyChain {
class func save(key: String, data: NSData) -> OSStatus {
let query = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrAccount as String : key,
kSecValueData as String : data ]
SecItemDelete(query as CFDictionaryRef)
return 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:AnyObject? = nil
let status: OSStatus = SecItemCopyMatching(query, &dataTypeRef)
if status == noErr {
return (dataTypeRef! 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
{
let 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
{
let uuid: CFUUIDRef = CFUUIDCreate(nil)
let cfStr:CFString = CFUUIDCreateString(nil, uuid)
let nsTypeString = cfStr as NSString
let swiftString:String = nsTypeString as String
return swiftString
}
}
Example usage:
let data = KeyChain.intToNSDATA(555)
let status = KeyChain.save("MyNumber", data: data)
print(status)
if let receivedData = KeyChain.load("MyNumber") {
let result = KeyChain.NSDATAtoInteger(receivedData)
print(result)
}
I tried to make it as simple as possible.
fileprivate class KeychainService {
static func updatePassword(_ password: String, serviceKey: String) {
guard let dataFromString = password.data(using: .utf8) else { return }
let keychainQuery: [CFString : Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: serviceKey,
kSecValueData: dataFromString]
SecItemDelete(keychainQuery as CFDictionary)
SecItemAdd(keychainQuery as CFDictionary, nil)
}
static func removePassword(serviceKey: String) {
let keychainQuery: [CFString : Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: serviceKey]
SecItemDelete(keychainQuery as CFDictionary)
}
static func loadPassword(serviceKey: String) -> String? {
let keychainQuery: [CFString : Any] = [kSecClass : kSecClassGenericPassword,
kSecAttrService : serviceKey,
kSecReturnData: kCFBooleanTrue,
kSecMatchLimitOne: kSecMatchLimitOne]
var dataTypeRef: AnyObject?
SecItemCopyMatching(keychainQuery as CFDictionary, &dataTypeRef)
guard let retrievedData = dataTypeRef as? Data else { return nil }
return String(data: retrievedData, encoding: .utf8)
}
static func flush() {
let secItemClasses = [kSecClassGenericPassword]
for itemClass in secItemClasses {
let spec: NSDictionary = [kSecClass: itemClass]
SecItemDelete(spec)
}
}
}
Example how to save & retrieve a struct User, a pretty common use-case:
import Security
import UIKit
class KeyChain {
struct User {
let identifier: Int64
let password: String
}
private static let service = "MyService"
static func save(user: User) -> Bool {
let identifier = Data(from: user.identifier)
let password = user.password.data(using: .utf8)!
let query = [kSecClass as String : kSecClassGenericPassword as String,
kSecAttrService as String : service,
kSecAttrAccount as String : identifier,
kSecValueData as String : password]
as [String : Any]
let deleteStatus = SecItemDelete(query as CFDictionary)
if deleteStatus == noErr || deleteStatus == errSecItemNotFound {
return SecItemAdd(query as CFDictionary, nil) == noErr
}
return false
}
static func retrieveUser() -> User? {
let query = [kSecClass as String : kSecClassGenericPassword,
kSecAttrService as String : service,
kSecReturnAttributes as String : kCFBooleanTrue!,
kSecReturnData as String: kCFBooleanTrue!]
as [String : Any]
var result: AnyObject? = nil
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == noErr,
let dict = result as? [String: Any],
let passwordData = dict[String(kSecValueData)] as? Data,
let password = String(data: passwordData, encoding: .utf8),
let identifier = (dict[String(kSecAttrAccount)] as? Data)?.to(type: Int64.self) {
return User(identifier: identifier, password: password)
} else {
return nil
}
}
}
private 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 {
withUnsafeBytes { $0.load(as: T.self) }
}
}
You are storing a number, not a string, so you are getting back an NSNumber, not a string. The exception is pretty clear - you can't downcast an NSNumber to a String - you can use stringValue() to get the string representation of an NSNumber
if let op = dataTypeRef?.toOpaque() {
let data = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
if let string: AnyObject? = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? AnyObject? {
if key == "IMAGE_TAKEN"
{
return string.stringValue() as! String!
}
else if string == nil
{
return nil
}
}
}