使用Swift添加项目和查询iOS Keychain

我无法转换所有可用于添加数据和从iOS Keychain查询数据到SwiftObjective C代码示例。 我正在尝试对字符串(访问令牌)进行基本存储并将其读回。 我已经看过Stack Overflow上的其他一些问题,但我无法让它工作。 我试图从各种来源拼凑出一个解决方案。

编辑1:我尝试了一个更基本的设置,因为我认为我的self.defaultKeychainQuery可能搞乱了。 我已将下面的代码更新为最新版本。

编辑2:搞定了。 我没有正确地将数据值添加到保存查询中。 我需要将字符串转换为NSData。 我已将下面的代码更新为最新的工作版本。

编辑3:正如Xerxes指出的那样,由于字典存在一些问题,此代码不适用于高于Beta 1的Xcode版本。 如果您知道对此的修复,请告诉我。

更新:我把它变成了一个用Swift编写的一个名为Locksmith的钥匙串库 。


保存

 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)") } 

加载

  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? // 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.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 } 

用法(视图控制器)

 KeychainService.saveToken("sometoken") KeychainService.loadToken() 

它使用这些方便的方法

 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)") } } 

这导致控制台中的输出:

 Saving status code is: 0 Loading status code is: 0 Retrieved the following data from the keychain:  The decoded string is sometoken 

非常感谢你的帮助。 我不太确定在获得dataTypeRef后如何处理它,或者如果它有任何给定上述代码的数据。

为了使其工作,您需要检索钥匙串常量的保留值,然后先存储,如下所示:

 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 

然后,您可以像这样引用NSMutableDictionary中的值:

 var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) 

我写了一篇关于它的博客文章: http : //rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/

希望这可以帮助!

rshelby

我为这个简单的任务编写了一个演示应用程序和辅助函数:在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") 

对于Swift用户

用于在Keychain中添加/检索/更新字段的单行代码:
https://github.com/jrendel/SwiftKeychainWrapper

用法

向keychain添加字符串值:

 let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey") 

从钥匙串中检索字符串值:

 let retrievedString: String? = KeychainWrapper.stringForKey("myKey") 

从钥匙串中删除字符串值:

 let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey") 

我对如何添加,获取,删除密码的解释(对于那些懒惰使用此线程中提供的库的人):

 // 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)") 

结果代码和其他有用信息可以在官方文档中找到: https : //developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/

我想我已经找到了解决方案。 我编辑了上面的post,包括有效的代码(至少对我而言)。 我也在这里写博客: 使用iOS Keychain和Swift(示例代码) 。

8月11日更新:我根据rshelby的评论更新了博客文章中的代码。 看一看。

更新:我把它变成了一个用Swift编写的一个名为Locksmith的钥匙串库 。


保存

 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)") } 

加载

  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? // 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.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 } 

用法(视图控制器)

 KeychainService.saveToken("sometoken") KeychainService.loadToken() 

它使用这些方便的方法

 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)") } } 

这导致控制台中的输出:

 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