我正在使用 Swift 玩一些 Mac OS X 钥匙串/安全 API。到目前为止,我已经能够从现有的钥匙串项中成功地获取用户名和密码(通过将kSecReturnAttributes
和设置kSecReturnAttributes
为true
)。
这段代码由多个来源拼凑而成,包括 StackOverflow 和 Apple 开发论坛:
//
// 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
据我所知,由于 Keychain API 的性质,这段代码的丑陋基本上是不可避免的。当我在本地运行此代码时,我看到以下输出:
0
ACCOUNT: bgentry@fakedomain.com
PASSWORD: this.is.a.fake.password
Program ended with exit code: 0
话虽这么说,但有一件事特别难住了我。我从成功的NSDictionary
APIfoundDict
调用SecItemAttr
(FourCharCode
名为 例如,常量应该允许我从钥匙串项中检索用户帐户。这是它的 Objective-C 定义:kSecAccountItemAttr
typedef FourCharCode SecItemAttr;
enum
{
...
kSecAccountItemAttr = 'acct',
...
}
在 Swift 中,我一直无法找到通过引用传递这些常量的任何方法,而是使用字符串来访问字典中的属性:
// Runtime error, "fatal error: unexpectedly found nil while unwrapping an Optional value"
let account = foundDict[kSecAccountItemAttr] as String
// Runtime error, "fatal error: unexpectedly found nil while unwrapping an Optional value"
let account = foundDict[kSecAccountItemAttr as NSNumber] as String
// Compiler error, "type 'FourCharCode' does not conform to protocol 'NSCopying'"
let account = foundDict[kSecAccountItemAttr as FourCharCode] as String
// Compiler error, "'Int' is not convertible to 'String'"
let account = foundDict[kSecAccountItemAttr as String] as String
// This works:
let account = foundDict["acct"] as String
显然,我想避免在我的代码中为属性键溅出字符串,并且我还想避免在我的 Swift 代码中重新声明这些常量。
我怎样才能做到这一点?