I play with some Mac OS X Keychain / Security APIs using Swift. So far I have been able to successfully receive both the username and password of an existing element keychain (by setting both kSecReturnAttributesand kSecReturnAttributeson true).
This code has been compiled from several sources, including StackOverflow and Apple Dev Forums:
//
// main.swift
// go-osxkeychain
//
import Foundation
// Create an HTTPS Keychain item w/ this server name before running this code
let serverName = "api.myserver.com"
public func getPassword(serverName: NSString) -> NSString? {
// Instantiate a new default keychain query.
// Tell the query to return the password data, as well as item attributes.
// Limit our results to one item.
var keychainQuery = NSMutableDictionary(
objects: [
kSecClassInternetPassword,
serverName,
kSecAttrProtocolHTTPS,
true,
true,
kSecMatchLimitOne
], forKeys: [
kSecClass,
kSecAttrServer,
kSecAttrProtocol,
kSecReturnAttributes,
kSecReturnData,
kSecMatchLimit
])
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
if Int(errSecSuccess) != Int(status) {
println("error finding keychain item")
return "ERROR"
}
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedDictData = Unmanaged<NSDictionary>.fromOpaque(op).takeUnretainedValue()
let foundDict = NSDictionary(dictionary: retrievedDictData)
// let account = foundDict[kSecAccountItemAttr as NSNumber] as String // BROKEN
let account = foundDict["acct"] as String // WORKS
println("ACCOUNT: \(account)")
let foundSecret = NSString(data: foundDict[kSecValueData as NSString] as NSData, encoding: NSUTF8StringEncoding) as String
println("PASSWORD: \(foundSecret)")
return foundSecret
} else {
println("Nothing was retrieved from the keychain. Status code \(status)")
}
return ""
}
let contents = getPassword(serverName) as String
As far as I know, the ugliness of this code is mostly inevitable due to the nature of the Keychain APIs. When I run this code locally, I see the following output:
0
ACCOUNT: bgentry@fakedomain.com
PASSWORD: this.is.a.fake.password
Program ended with exit code: 0
, , . NSDictionary keychain, API ( foundDict), , SecItemAttr/FourCharCode, . , kSecAccountItemAttr keychain. Objective-C:
typedef FourCharCode SecItemAttr;
enum
{
...
kSecAccountItemAttr = 'acct',
...
}
Swift , :
let account = foundDict[kSecAccountItemAttr] as String
let account = foundDict[kSecAccountItemAttr as NSNumber] as String
let account = foundDict[kSecAccountItemAttr as FourCharCode] as String
let account = foundDict[kSecAccountItemAttr as String] as String
let account = foundDict["acct"] as String
, , Swift.
?