I am recieving Bad Access when trying to create a signature with xcode SecKeyCreateSignature.
This flows with biometric enrollment in a webview. When the user hits the enrollment page the device sends to the webview the device Id and a public key.
To generate the key I have...
private let tag = "com.CustomTagName.private",
deviceId = UIDevice.current.identifierForVendor!.uuidString,
privateKeyAttr: [NSObject: NSObject] = [
kSecAttrIsPermanent:true as NSObject,
kSecAttrApplicationTag: "com.CustomTagName.private".data(using: .utf8)! as NSObject,
kSecClass: kSecClassKey,
kSecAttrType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits : 2048 as NSObject,
kSecReturnData: kCFBooleanTrue
],
privateKey : SecKey?,
privateKeyStr = ""
;
...
private func generateKeys() throws {
var err: Unmanaged<CFError>?
do {
guard let prk = SecKeyCreateRandomKey(privateKeyAttr as CFDictionary, &err) else {
throw err!.takeRetainedValue() as Error
}
// After creating a random private key It appears we have to unwrap it...?
guard let unWrappedKey = SecKeyCopyExternalRepresentation(prk, &err) as Data? else {
throw err!.takeRetainedValue() as Error
}
self.privateKeyStr = unWrappedKey.base64EncodedString()
self.privateKey = prk;
} catch {
}
}
From here I use
let publicKey = SecKeyCopyPublicKey(self.privateKey!);
If I was going to call SecKeyCreateSignature the I have no issue. But I dont call the signature until the user needs to login. So I retrieve the key using...
let message = "HereIAm";
let statusPrivateKey = SecItemCopyMatching(privateKeyAttr as CFDictionary, &resultPrivateKey)
if statusPrivateKey != noErr || resultPrivateKey == nil{
fatalError("Error getting private key")
}
self.privateKey = resultPrivateKey as! SecKey?;
self.privateKeyStr = (privateKey as! Data).base64EncodedString()
// Bad Access Error Here \\
guard let signFingerPrint = SecKeyCreateSignature(privateKey!, SecKeyAlgorithm.rsaSignatureMessagePKCS1v15SHA512, message.data(using: .utf8)! as CFData, &err) else {
fatalError("Signing error")
}
I do notice that the SecKey does not need to be unwrapped with SecKeyCopyExternalRepresentation.
I dont understand the difference in the to Sec Keys when the data is the same.
How do I retrieve the private key to that I can create the signature off it?
I found the answer... kinda, In my privateAttrs I have
kSecReturnData: kCFBooleanTrue
and should be
kSecReturnRef: kCFBooleanTrue
Im my case I was returning Data as the type but I needed to return the type of the original reference.
Related
I have created a private key in the Keychain with SecKeyCreateRandomKey. When I attempt to access the key to perform a signing operation, the Touch ID or FaceID dialog will never appear. I get the sign string but without TouchID or FaceID. I tried with BiometryAny and TouchIdAny but it doesn't work.
static func createKey(keyName:String){
DispatchQueue.main.async{
var error : Unmanaged<CFError>?
print("Key is generating for \(keyName)")
let tag = (keyName + "PrivateKey").data(using: .utf8)!
// private key parameters
var privateKeyParams: [String: Any] = [:]
let accessControlError:UnsafeMutablePointer<Unmanaged<CFError>?>? = nil
// ^ Already a 'pointer'
if #available(iOS 10 , *) {
let allocator:CFAllocator! = kCFAllocatorDefault
let protection:AnyObject! = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
let flags:SecAccessControlCreateFlags = SecAccessControlCreateFlags.userPresence
let accessControlRef = SecAccessControlCreateWithFlags(
allocator,
protection,
flags,
accessControlError // <- Notice the lack of '&'
)
privateKeyParams = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag,
kSecAttrAccessControl as String : accessControlRef!,
]
} else {
// Fallback on earlier versions
}
// global parameters for our key generation
let parameters: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPrivateKeyAttrs as String: privateKeyParams
]
if #available(iOS 10.0, *) {
do{
guard let privateKey = SecKeyCreateRandomKey(parameters as CFDictionary, nil) else {
print("\(keyName)PrivateKey generator Error!")
throw error!.takeRetainedValue() as Error
}
}
}
}
and signture function:
static func SigntureWithPrivateKey(keyName: String, message : String) -> String {
//print("sign started .........")
guard let messageData = message.data(using: String.Encoding.utf8) else {
print("bad message to sign")
return ""
}
if #available(iOS 10.0, *) {
guard let privateKeyLocal: SecKey = getPrivateKey("\(keyName)PrivateKey") else
{
return ""
}
guard let signData = SecKeyCreateSignature(privateKeyLocal,SecKeyAlgorithm.rsaSignatureDigestPKCS1v15SHA512,messageData as CFData, nil) else {
print("priv ECC error signing")
return ""
}
let convertedSignData = signData as Data
let convertedString = convertedSignData.base64EncodedString()
return convertedString
} else {
return ""
}
}
and getPrivateKey function :
fileprivate static func getPrivateKey(_ name: String) -> SecKey?
{
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrApplicationTag as String: name,
kSecReturnRef as String: true
]
var item: CFTypeRef? = nil
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else
{
if status == errSecUserCanceled
{
print("\tError: Accessing private key failed: The user cancelled (%#).", "\(status)")
}
else if status == errSecDuplicateItem
{
print("\tError: The specified item already exists in the keychain (%#).", "\(status)")
}
else if status == errSecItemNotFound
{
print("\tError: The specified item could not be found in the keychain (%#).", "\(status)")
}
else if status == errSecInvalidItemRef
{
print("\tError: The specified item is no longer valid. It may have been deleted from the keychain (%#).", "\(status)")
}
else
{
print("\tError: Accessing private key failed (%#).", "\(status)")
}
return nil
}
return (item as! SecKey)
}
Sorry your question is quite long so I thought I would give generic answer.
Make sure you have set NSFaceIDUsageDescription in your
info.plist
Without this key, the system won’t allow your app to use Face ID. The
value for this key is a string that the system presents to the user
the first time your app attempts to use Face ID. The string should
clearly explain why your app needs access to this authentication
mechanism. The system doesn’t require a comparable usage description
for Touch ID.
Make sure you have added both Security and LocalAuthentication
framework other than turning on keychain services
You have to specifically set Authentication param in
SecAccessControlCreateWithFlags class(Please go through this clearly it makes lots of difference)
Please find more information along with sample source code here
https://developer.apple.com/documentation/localauthentication/accessing_keychain_items_with_face_id_or_touch_id
Hope this helps.
Do not use the simulator, try it on a real device.
you need to set flag as
let flags:SecAccessControlCreateFlags =
[SecAccessControlCreateFlags.privateKeyUsage, SecAccessControlCreateFlags.touchIDCurrentSet]
Does iOS expose API for key generation, and secret key derivation using ECDH?
From what I see, apple are using it (and specifically x25519) internally but I don't see it exposed as public API by common crypto or otherwise.
Thanks,
Z
Done in playground with Xcode 8.3.3, generates a private/public key using EC for Alice, Bob, then calculating the shared secret for Alice using Alice's private and Bob's public, and share secret for Bob using Bob's private and Alice's public and finally asserting that they're equal.
import Security
import UIKit
let attributes: [String: Any] =
[kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: false]
]
var error: Unmanaged<CFError>?
if #available(iOS 10.0, *) {
// generate a key for alice
guard let privateKey1 = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey1 = SecKeyCopyPublicKey(privateKey1)
// generate a key for bob
guard let privateKey2 = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey2 = SecKeyCopyPublicKey(privateKey2)
let dict: [String: Any] = [:]
// alice is calculating the shared secret
guard let shared1 = SecKeyCopyKeyExchangeResult(privateKey1, SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256, publicKey2!, dict as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
// bob is calculating the shared secret
guard let shared2 = SecKeyCopyKeyExchangeResult(privateKey2, SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256, publicKey1!, dict as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
print(shared1==shared2)
} else {
// Fallback on earlier versions
print("unsupported")
}
Thanks #Mats for sending me in the right direction..3
Here is the latest code with swift 5 and changes in parameters.
import Security
import UIKit
var error: Unmanaged<CFError>?
let keyPairAttr:[String : Any] = [kSecAttrKeySizeInBits as String: 256,
SecKeyKeyExchangeParameter.requestedSize.rawValue as String: 32,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false],
kSecPublicKeyAttrs as String:[kSecAttrIsPermanent as String: false]]
let algorithm:SecKeyAlgorithm = SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256//ecdhKeyExchangeStandardX963SHA256
do {
guard let privateKey = SecKeyCreateRandomKey(keyPairAttr as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey = SecKeyCopyPublicKey(privateKey)
print("public ky1: \(String(describing: publicKey)),\n private key: \(privateKey)\n\n")
guard let privateKey2 = SecKeyCreateRandomKey(keyPairAttr as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey2 = SecKeyCopyPublicKey(privateKey2)
print("public ky2: \(String(describing: publicKey2)),\n private key2: \(privateKey2)\n\n")
let shared:CFData? = SecKeyCopyKeyExchangeResult(privateKey, algorithm, publicKey2!, keyPairAttr as CFDictionary, &error)
let sharedData:Data = shared! as Data
print("shared Secret key: \(sharedData.hexEncodedString())\n\n")
let shared2:CFData? = SecKeyCopyKeyExchangeResult(privateKey2, algorithm, publicKey!, keyPairAttr as CFDictionary, &error)
let sharedData2:Data = shared2! as Data
print("shared Secret key 2: \(sharedData2.hexEncodedString())\n\n")
// shared secret key and shared secret key 2 should be same
} catch let error as NSError {
print("error: \(error)")
} catch {
print("unknown error")
}
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}
Circa - 2020 and iOS13. In Zohar's code snippet below, also specify a key size in the dictionary before attempting to get the shared secrets.
let dict: [String: Any] = [SecKeyKeyExchangeParameter.requestedSize.rawValue as String: 32]
Or there would be an error saying this.
kSecKeyKeyExchangeParameterRequestedSize is missing
I am trying to sync up private asymmetric keys between my iOS app and its watchOS equivalent. I have tried using SecKeyCopyExternalRepresentation to export it out as CFData and then send it to the watch using WatchConnectivity. However when it gets to the watch I have no way of converting the Data back into a SecKey. I tried using SecKeyCreateWithData in an attempt to recreate it, but it seems that that only works with symmetric keys, for when I tried it it crashed the watch app. Any ideas?
iOS Code:
func sendSharedKeyPair(keyPair: (publicKey: SecKey, privateKey: SecKey)) {
var error: Unmanaged<CFError>?
let publicKeyData = SecKeyCopyExternalRepresentation(keyPair.publicKey, &error)
if let error = error {
return print("Error sending shared key: \(error)")
}
let privateKeyData = SecKeyCopyExternalRepresentation(keyPair.privateKey, &error)
if let error = error {
return print("Error sending shared key: \(error)")
}
if let publicKeyData = publicKeyData, let privateKeyData = privateKeyData {
session.sendMessage(["requestedCommand": WatchControllerCommands.sendSharedKeyPair.rawValue, "keyPair": ["publicKey": publicKeyData, "privateKey": privateKeyData]], replyHandler: nil, errorHandler: { error in
print(error)
})
}
}
watchOS Code:
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
guard let requestedCommand = (message["requestedCommand"] as? String).flatMap({ WatchControllerCommands(rawValue: $0) }), requestedCommand == .sendSharedKeyPair else { return }
guard let publicKeyData = (message["keyPair"] as? [String: Any])?["publicKey"].flatMap({ $0 as? Data }), let privateKeyData = (message["keyPair"] as? [String: Any])?["privateKey"].flatMap({ $0 as? Data }) else { return print("Couldn't parse keys") }
let publicTag = "myAppTag"
let privateTag = publicTag + ".private"
let privateAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): privateTag] as [String : Any]
let publicAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): publicTag] as [String : Any]
var error: Unmanaged<CFError>?
let publicCFData = publicKeyData as CFData
let privateCFData = privateKeyData as CFData
let publicCFDict = publicAttributes as CFDictionary
let privateCFDict = privateAttributes as CFDictionary
SecKeyCreateWithData(publicCFData, publicCFDict, &error)
if let error = error {
print(error)
}
SecKeyCreateWithData(privateCFData, privateCFDict, &error)
if let error = error {
print(error)
}
}
From headerdocs around SecKeyCreateWithData:
#param attributes Dictionary containing attributes describing the key
to be imported. The keys in this dictionary are kSecAttr* constants
from SecItem.h. Mandatory attributes are: * kSecAttrKeyType *
kSecAttrKeyClass * kSecAttrKeySizeInBits
Your code only defines kSecAttrIsPermanent and kSecAttrApplicationTag attributes.
This question already has answers here:
Save and retrieve value via KeyChain
(7 answers)
Closed 6 years ago.
How to simply store a String in Keychain and load when needed.
There are several SO solution which mostly refers to Git repo. But I need the smallest and the simplest solution on latest Swift. Certainly, I don't want to add git framework for simply storing a password in my project.
There are similar solution Save and retrieve value via KeyChain , which did not work for me. Tired with compiler errors.
Simplest Source
import Foundation
import Security
// Constant Identifiers
let userAccount = "AuthenticatedUser"
let accessGroup = "SecuritySerivice"
/**
* User defined keys for new entry
* Note: add new keys for new secure item and use them in load and save methods
*/
let passwordKey = "KeyForPassword"
// 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 {
/**
* Exposed methods to perform save and load queries.
*/
public class func savePassword(token: NSString) {
self.save(passwordKey, data: token)
}
public class func loadPassword() -> NSString? {
return self.load(passwordKey)
}
/**
* Internal methods for querying the keychain.
*/
private class func save(service: NSString, data: NSString) {
let dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
// Add the new keychain item
SecItemAdd(keychainQuery as CFDictionaryRef, nil)
}
private class func load(service: 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, userAccount, 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, encoding: NSUTF8StringEncoding)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
Example of Calling
KeychainService.savePassword("Pa55worD")
let password = KeychainService.loadPassword() // password = "Pa55worD"
SWIFT 4: VERSION WITH UPDATE AND REMOVE PASSWORD
import Cocoa
import Security
// see 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: String, account:String, data: String) {
if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue])
let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue:dataFromString] as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Read failed: \(err)")
}
}
}
}
class func removePassword(service: String, account:String) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue])
// Delete any existing items
let status = SecItemDelete(keychainQuery as CFDictionary)
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Remove failed: \(err)")
}
}
}
class func savePassword(service: String, account:String, data: String) {
if let dataFromString = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// 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
if let err = SecCopyErrorMessageString(status, nil) {
print("Write failed: \(err)")
}
}
}
}
class func loadPassword(service: String, account:String) -> String? {
// 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: String?
if status == errSecSuccess {
if let retrievedData = dataTypeRef as? Data {
contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8)
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
return contentsOfKeychain
}
}
You need to imagine the following wired up to a text input field and a label, then having four buttons wired up, one for each of the methods.
class ViewController: NSViewController {
#IBOutlet weak var enterPassword: NSTextField!
#IBOutlet weak var retrievedPassword: NSTextField!
let service = "myService"
let account = "myAccount"
// will only work after
#IBAction func updatePassword(_ sender: Any) {
KeychainService.updatePassword(service: service, account: account, data: enterPassword.stringValue)
}
#IBAction func removePassword(_ sender: Any) {
KeychainService.removePassword(service: service, account: account)
}
#IBAction func passwordSet(_ sender: Any) {
let password = enterPassword.stringValue
KeychainService.savePassword(service: service, account: account, data: password)
}
#IBAction func passwordGet(_ sender: Any) {
if let str = KeychainService.loadPassword(service: service, account: account) {
retrievedPassword.stringValue = str
}
else {retrievedPassword.stringValue = "Password does not exist" }
}
}
Swift 5
Kosuke's version for Swift 5
import Security
import UIKit
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) }
}
}
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)
}
I'm trying to use Apple's keychain service based on a tutorial I found (https://www.raywenderlich.com/85528/user-accounts-ios-ruby-rails-swift#next_section). However I am shown this error
Cannot convert value of type 'Unmanaged< AnyObject>'?' to expected argument type 'AnyObject?'
At this block of codes
var dataTypeRef : Unmanaged<AnyObject>?
let status = SecItemCopyMatching(queryAttributes, &dataTypeRef)
if dataTypeRef == nil {
return nil
}
The full code block is as shown below
public class func passwordForAccount(account: String, service: String = "keyChainDefaultService") -> String? {
let queryAttributes = NSDictionary(objects: [secClassGenericPassword(), service, account, true], forKeys: [secClass(), secAttrService(), secAttrAccount(), secReturnData()])
var dataTypeRef : Unmanaged<AnyObject>?
let status = SecItemCopyMatching(queryAttributes, &dataTypeRef)
if dataTypeRef == nil {
return nil
}
let retrievedData : NSData = dataTypeRef!.takeRetainedValue() as! NSData
let password = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
return (password as! String)
}
The entire code can be found here (https://codeshare.io/426px) at line 52- 57.