Attempting to use Apple's KeychainItemWrapper translates to Swift

Sigh, I have been working on this all day ... here is my nightmare:

I am trying to use KeychainItemWrapper made by Apple. But I "translated" my Objective-C codes into Swift:

import Foundation import Security class MyKeychainItemWrapper: NSObject { var keychainItemData: NSMutableDictionary? var genericPasswordQuery: NSMutableDictionary = NSMutableDictionary() init(identifier: String, accessGroup: String?) { super.init() // Begin Keychain search setup. The genericPasswordQuery leverages the special user // defined attribute kSecAttrGeneric to distinguish itself between other generic Keychain // items which may be included by the same application. genericPasswordQuery.setObject(kSecClassGenericPassword, forKey: kSecClass) genericPasswordQuery.setObject(identifier, forKey: kSecAttrGeneric) // The keychain access group attribute determines if this item can be shared // amongst multiple apps whose code signing entitlements contain the same keychain access group. println(accessGroup) if (!(accessGroup == nil)) { genericPasswordQuery.setObject(accessGroup!, forKey: kSecAttrAccessGroup) } // Use the proper search constants, return only the attributes of the first match. genericPasswordQuery.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit) genericPasswordQuery.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes) var tempQuery: NSDictionary = NSDictionary(dictionary: genericPasswordQuery) var outDictionary: Unmanaged<AnyObject>? = nil var status: OSStatus = SecItemCopyMatching(tempQuery as CFDictionaryRef, &outDictionary) println(status == noErr) if (status == noErr) { // Stick these default values into keychain item if nothing found. resetKeychainItem() // Add the generic attribute and the keychain access group. keychainItemData!.setObject(identifier, forKey: kSecAttrGeneric) if (!(accessGroup == nil)) { keychainItemData!.setObject(accessGroup!, forKey: kSecAttrAccessGroup) } } else { // load the saved data from Keychain. keychainItemData = secItemFormatToDictionary(outDictionary?.takeRetainedValue() as NSDictionary) } } 

Then in my AppDelegate.swift application I try to use it:

 import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var passwordItem: MyKeychainItemWrapper = MyKeychainItemWrapper(identifier: "Password", accessGroup: nil) ... 

So, the initializer is called, but for some reason I ALWAYS, ALWAYS receive

Subject 1: EXC_BREAKPOINT (code = EXC_ARM_BREAKPOINT, subcode = 0xe7ffdefe)

enter image description here

I tried commenting out the lines of the problem, and then getting this error in another if (): enter image description here

I even tried:

 var mmm: Bool = (accessGroup == nil) if (!mmm) { genericPasswordQuery.setObject(accessGroup!, forKey: kSecAttrAccessGroup) } 

But the same error in the same place, i.e. if (..)

I'm so confused right now. Am I missing something here or?

Environment: Xcode6-beta6, iOS 8 beta 5 on non-jailbroken iPhone 5.

+6
source share
4 answers

Swift 3

 import UIKit import Security let kSecClassGenericPasswordValue = String(format: kSecClassGenericPassword as String) let kSecClassValue = String(format: kSecClass as String) let kSecAttrServiceValue = String(format: kSecAttrService as String) let kSecValueDataValue = String(format: kSecValueData as String) let kSecMatchLimitValue = String(format: kSecMatchLimit as String) let kSecReturnDataValue = String(format: kSecReturnData as String) let kSecMatchLimitOneValue = String(format: kSecMatchLimitOne as String) let kSecAttrAccountValue = String(format: kSecAttrAccount as String) struct KeychainAccess { func setPasscode(identifier: String, passcode: String) { if let dataFromString = passcode.data(using: String.Encoding.utf8) { let keychainQuery = [ kSecClassValue: kSecClassGenericPasswordValue, kSecAttrServiceValue: identifier, kSecValueDataValue: dataFromString ] as CFDictionary SecItemDelete(keychainQuery) print(SecItemAdd(keychainQuery, nil)) } } func getPasscode(identifier: String) -> String? { let keychainQuery = [ kSecClassValue: kSecClassGenericPasswordValue, kSecAttrServiceValue: identifier, kSecReturnDataValue: kCFBooleanTrue, kSecMatchLimitValue: kSecMatchLimitOneValue ] as CFDictionary var dataTypeRef: AnyObject? let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) var passcode: String? if (status == errSecSuccess) { if let retrievedData = dataTypeRef as? Data, let result = String(data: retrievedData, encoding: String.Encoding.utf8) { passcode = result as String } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return passcode } } 

Swift 2

 import UIKit; import Security; let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword); let kSecClassValue = NSString(format: kSecClass); let kSecAttrServiceValue = NSString(format: kSecAttrService); let kSecValueDataValue = NSString(format: kSecValueData); let kSecMatchLimitValue = NSString(format: kSecMatchLimit); let kSecReturnDataValue = NSString(format: kSecReturnData); let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne); let kSecAttrAccountValue = NSString(format: kSecAttrAccount); class KeychainAccess: NSObject { func setPasscode(identifier: String, passcode: String) { let dataFromString: NSData = passcode.dataUsingEncoding(NSUTF8StringEncoding)!; let keychainQuery = NSDictionary( objects: [kSecClassGenericPasswordValue, identifier, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecValueDataValue]); SecItemDelete(keychainQuery as CFDictionaryRef); let status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil); } func getPasscode(identifier: String) -> NSString? { let keychainQuery = NSDictionary( objects: [kSecClassGenericPasswordValue, identifier, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecReturnDataValue, kSecMatchLimitValue]); var dataTypeRef: AnyObject? let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) var passcode: NSString?; if (status == errSecSuccess) { let retrievedData: NSData? = dataTypeRef as? NSData if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) { passcode = result as String } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return passcode; } } 

Then from anywhere, just call:

 func setPasscode(passcode: String) { let keychainAccess = KeychainAccess(); keychainAccess.setPasscode("YourAppIdentifier", passcode:passcode); } func getPasscode() -> NSString { let keychainAccess = KeychainAccess(); return keychainAccess.getPasscode("YourAppIdentifier")!; } func deletePasscode() { let keychainAccess = KeychainAccess(); keychainAccess.setPasscode("YourAppIdentifier", passcode:""); } 
+17
source

Updates for Swift 2.

Here is an example implementation that might help:

 import Security class ZLKeychainService: NSObject { var service = "Service" var keychainQuery :[NSString: AnyObject]! = nil func save(name name: NSString, value: NSString) -> OSStatus? { let statusAdd :OSStatus? guard let dataFromString: NSData = value.dataUsingEncoding(NSUTF8StringEncoding) else { return nil } keychainQuery = [ kSecClass : kSecClassGenericPassword, kSecAttrService : service, kSecAttrAccount : name, kSecValueData : dataFromString] if keychainQuery == nil { return nil } SecItemDelete(keychainQuery as CFDictionaryRef) statusAdd = SecItemAdd(keychainQuery! as CFDictionaryRef, nil) return statusAdd; } func load(name name: NSString) -> String? { var contentsOfKeychain :String? keychainQuery = [ kSecClass : kSecClassGenericPassword, kSecAttrService : service, kSecAttrAccount : name, kSecReturnData : kCFBooleanTrue, kSecMatchLimit : kSecMatchLimitOne] if keychainQuery == nil { return nil } var dataTypeRef: AnyObject? let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) if (status == errSecSuccess) { let retrievedData: NSData? = dataTypeRef as? NSData if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) { contentsOfKeychain = result as String } } else { print("Nothing was retrieved from the keychain. Status code \(status)") } return contentsOfKeychain } } //Test: let userName = "TestUser" let userValue: NSString = "TestValue" print("userName: '\(userName)'") print("userValue: '\(userValue)'") let kcs = ZLKeychainService() kcs.save(name:userName, value: userValue) print("Keychain Query \(kcs.keychainQuery)") if let recoveredToken = kcs.load(name:userName) { print("Recovered Value: '\(recoveredToken)'") } 

Output:

username: 'TestUser'
userValue: 'TestValue'
Keychain request [acct: TestUser, v_Data: <54657374 56616c75 65>, svce: Service, class: genp]
Recovered value: 'TestValue'

+4
source

official GenericKeychain

There are several quick versions, the best of them:

jrendel / SwiftKeychainWrapperGitHub

how to use it:

  • download file: KeychainWrapper.swift
  • write code to install / receive / uninstall:

     let StrUsernameKey:String = "username" let StrPasswordKey:String = "password" let saveSuccessful: Bool = KeychainWrapper.setString(usernameTextField.text!, forKey: StrUsernameKey) print("saveSuccessful=\(saveSuccessful)") //saveSuccessful=true let retrievedString: String? = KeychainWrapper.stringForKey(StrUsernameKey) print("retrievedString=\(retrievedString)") //retrievedString=Optional("yourLastStoredUsernameString") let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey(StrUsernameKey) print("removeSuccessful=\(removeSuccessful)") //removeSuccessful=true let retrievedStringAfterDelete: String? = KeychainWrapper.stringForKey(StrUsernameKey) print("retrievedStringAfterDelete=\(retrievedStringAfterDelete)") //retrievedStringAfterDelete=nil 
+3
source

My solution seems to work:

 init(identifier: String) { super.init() genericPasswordQuery.setObject(kSecClassGenericPassword, forKey: kSecClass as String) genericPasswordQuery.setObject(identifier, forKey: kSecAttrGeneric as String) // Use the proper search constants, return only the attributes of the first match. genericPasswordQuery.setObject(kSecMatchLimitOne, forKey: kSecMatchLimit as String) genericPasswordQuery.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes as String) var tempQuery: NSDictionary = NSDictionary(dictionary: genericPasswordQuery) var outDictionary: Unmanaged<AnyObject>? = nil let status: OSStatus = SecItemCopyMatching(tempQuery as CFDictionaryRef, &outDictionary) var result: NSDictionary? = outDictionary?.takeRetainedValue() as NSDictionary? if (result == nil) { // Stick these default values into keychain item if nothing found. resetKeychainItem() // Add the generic attribute and the keychain access group. keychainItemData!.setObject(identifier, forKey: kSecAttrGeneric as String) } else { // load the saved data from Keychain. keychainItemData = secItemFormatToDictionary(result!) } } 

The only thing I did was deploy outDictionary right after receiving it.

0
source

Source: https://habr.com/ru/post/974392/


All Articles