使用 Swift 查询 iOS 钥匙串

Posted

技术标签:

【中文标题】使用 Swift 查询 iOS 钥匙串【英文标题】:Querying iOS Keychain using Swift 【发布时间】:2014-06-10 16:08:58 【问题描述】:

我无法使用 Swift 转换钥匙串查询结果。

我的请求似乎奏效了:

let queryAttributes = NSDictionary(objects: [kSecClassGenericPassword, "MyService",     "MyAccount",       true],
                                   forKeys: [kSecClass,                kSecAttrService, kSecAttrAccount, kSecReturnData])


dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 
    var dataTypeRef : Unmanaged<AnyObject>?
    let status = SecItemCopyMatching(queryAttributes, &dataTypeRef);

    let retrievedData : NSData = dataTypeRef!.takeRetainedValue() as NSData
    *** ^^^^can't compile this line^^^^
)

我的问题是,代码无法编译:

Bitcast requires both operands to be pointer or neither
  %114 = bitcast %objc_object* %113 to %PSs9AnyObject_, !dbg !487
Bitcast requires both operands to be pointer or neither
  %115 = bitcast %PSs9AnyObject_ %114 to i8*, !dbg !487
LLVM ERROR: Broken function found, compilation aborted!

我不知道如何将Unmanaged&lt;AnyObject&gt; 转换为NSData

有什么想法吗?

【问题讨论】:

我也在尝试访问 ios 钥匙串,我看到了你的帖子。我不知道如何创建查询字典。我什至将您上面的第一行复制到我的应用程序中,它也说了同样的话。 “找不到接受提供的参数的 'init' 的重载”。我错过了什么吗? 与 2015 年 3 月 11 日最新 XCode 相同的问题 如果你正在寻找一个简单的插入式钥匙串包装器,你可以试试这个:github.com/ashleymills/Keychain.swift 【参考方案1】:

使用withUnsafeMutablePointer函数和UnsafeMutablePointer结构来检索数据,例如:

var result: AnyObject?
var status = withUnsafeMutablePointer(&result)  SecItemCopyMatching(queryAttributes, UnsafeMutablePointer($0)) 

if status == errSecSuccess 
    if let data = result as NSData? 
        if let string = NSString(data: data, encoding: NSUTF8StringEncoding) 
            // ...
        
    

它适用于发布(最快 [-O])构建。

【讨论】:

这是发布应用程序时唯一对我有用的解决方案(其他解决方案在 xcode 中运行良好,但在分发应用程序的 beta 版本时不行)。 这应该是正确的答案。其他的将无法在发布版本中工作。 你是明星,岸川。不幸的是,无法使用您的包装器,因为它需要 8.0,但这让我很满意。 就我而言,这也是发布版本中唯一可行的解​​决方案。另外,我很想知道为什么其他实现在发布版本中不起作用。 +1 这对我也有用。非常感谢!另一种方法仅适用于 iPhone 5S+。在 iPhone 5 上,它会崩溃。【参考方案2】:

您似乎遇到了编译器错误,您应该报告。您可以采用不同的路径来检索该值,例如:

    var dataTypeRef :Unmanaged<AnyObject>?
    let status = SecItemCopyMatching(queryAttributes, &dataTypeRef);

    let opaque = dataTypeRef?.toOpaque()

    if let op = opaque? 
        let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()

    

AnyObject 用作Unmanaged&lt;T&gt; 的类型参数T 时会出现错误。以下代码 sn-p 编译没有问题,它使用更具体的类型,CFError

    let url = NSURL(string:"dummy")
    var errorRef: Unmanaged<CFError>?
    let succeeded = CTFontManagerRegisterFontsForURL(url, .Process, &errorRef)

    if errorRef 
        let error = errorRef!.takeRetainedValue()
    

由于 Keychain API 根据查询属性返回不同的结果,因此需要使用AnyObject

【讨论】:

非常感谢您的回答。然后我会报告它,也许会等着看是否在下一个测试版中修复。如果没有,我将使用此代码。再次感谢! 当您将 Swift 编译器 - 优化级别设置为 -Onone 时,这似乎可以正常工作。但是当你将它设置为 -O 时。不透明将为零。使用 Xcode 6.1(6A1052d) 构建 我在let opaque = dataTypeRef?.toOpaque() (Xcode 6.1 & 6.2b) 线上遇到了错误的访问异常【参考方案3】:

要使其工作,您需要首先访问每个钥匙串常量的保留值。例如:

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 

然后您需要像这样引用您在钥匙串字典对象中创建的常量。

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/

希望这会有所帮助!

谢尔比

【讨论】:

以上是关于使用 Swift 查询 iOS 钥匙串的主要内容,如果未能解决你的问题,请参考以下文章

升级到 iOS 13 后,钥匙串查询总是返回 errSecItemNotFound

“访问钥匙串时出错”

NE***Manager 中使用的 Swift 中的钥匙串引用

使用 swift 2.0 生成钥匙串密钥

在苹果钥匙串中存储和读取密码的简单方法?

重新安装应用程序后,钥匙串存储的密码仍然可用