【问题标题】:Enumerate all Keychain items in my iOS application枚举我的 iOS 应用程序中的所有钥匙串项
【发布时间】:2012-06-13 13:59:10
【问题描述】:

以编程方式(从我的应用程序中)获取存储在钥匙串中的所有项目的最简单方法是什么?

它可能与 SecItemCopyMatching() 有关,但该函数的文档不是很清楚(我未能在网络上找到合适的示例)。

【问题讨论】:

    标签: ios security keychain


    【解决方案1】:

    SecItemCopyMatching 是正确的选择。首先,我们构建查询字典,以便在字典中返回项目的属性,并返回所有项目:

    NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
        (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnAttributes,
        (__bridge id)kSecMatchLimitAll, (__bridge id)kSecMatchLimit,
        nil];
    

    由于SecItemCopyMatching 至少需要返回的SecItems 的类,我们创建一个包含所有类的数组...

    NSArray *secItemClasses = [NSArray arrayWithObjects:
                               (__bridge id)kSecClassGenericPassword,
                               (__bridge id)kSecClassInternetPassword,
                               (__bridge id)kSecClassCertificate,
                               (__bridge id)kSecClassKey,
                               (__bridge id)kSecClassIdentity,
                               nil];
    

    ...对于每个类,在我们的查询中设置类,调用SecItemCopyMatching,并记录结果。

    for (id secItemClass in secItemClasses) {
        [query setObject:secItemClass forKey:(__bridge id)kSecClass];
    
        CFTypeRef result = NULL;
        SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
        NSLog(@"%@", (__bridge id)result);
        if (result != NULL) CFRelease(result);
    }
    

    在生产代码中,您应该检查SecItemCopyMatching 返回的OSStatuserrSecItemNotFound(未找到项)还是errSecSuccess(至少找到一项)。

    【讨论】:

    • 谢谢!尚未测试,但看起来是正确的答案。
    • 我在 iPhone 和模拟器中都打印出了所有的空值。还有什么我应该做的吗?
    • 检查SecItemCopyMatching的返回值。如果是errSecItemNotFound,你的iOS应用中没有存储任何钥匙串项目,你将不会得到任何回报。
    • 我尝试添加嵌入式证书(包含多个证书的单个证书),但它只返回一个条目。有没有出路或“SecItemAdd”没有正确添加它们?任何想法。
    【解决方案2】:

    Swift 4 更新为 @Cosmin's Swift 3 answer

    open func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
        let query: [String: Any] = [
            kSecClass as String : secClass,
            kSecReturnData as String  : kCFBooleanTrue,
            kSecReturnAttributes as String : kCFBooleanTrue,
            kSecReturnRef as String : kCFBooleanTrue,
            kSecMatchLimit as String: kSecMatchLimitAll
        ]
                    
        var result: AnyObject?
                    
        let lastResultCode = withUnsafeMutablePointer(to: &result) {
            SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
        }
                    
        var values = [String:String]()
        if lastResultCode == noErr {
            let array = result as? Array<Dictionary<String, Any>>
                        
            for item in array! {
                if let key = item[kSecAttrAccount as String] as? String,
                   let value = item[kSecValueData as String] as? Data {
                       values[key] = String(data: value, encoding:.utf8)
                 }
             }
        }
                    
        return values
    }
    

    【讨论】:

      【解决方案3】:

      其他 Swift 代码 sn-ps 似乎都有些复杂。您实际上不必过多地处理 MutablePointers,并且您可能希望进行适当的错误管理。我只是通过调整Apple documentation 中的代码在 Swift 中实现了我的版本。这是为那些使用 Xcode 11 的人准备的。

      let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword, // change the kSecClass for your needs
                                  kSecMatchLimit as String: kSecMatchLimitAll,
                                  kSecReturnAttributes as String: true,
                                  kSecReturnRef as String: true]
      var items_ref: CFTypeRef?
      let status = SecItemCopyMatching(query as CFDictionary, &items_ref)
      guard status != errSecItemNotFound else { throw KeychainError.noPassword }
      guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
      let items = items_ref as! Array<Dictionary<String, Any>>
      
      // Now loop over the items and do something with each item
      for item in items {
          // Sample code: prints the account name
          print(item[kSecAttrAccount as String] as? String)
      }
      

      【讨论】:

        【解决方案4】:

        更新为在字典中包含 kSecClassIdentity 和 kSecClassCertificate 信息

        我也不认为调用 withUnsafeMutablePointer(to:_:) 是必要的。

        func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:AnyObject] {
        
            let query: [String: Any] = [
                kSecClass as String : secClass,
                kSecReturnData as String  : true,
                kSecReturnAttributes as String : true,
                kSecReturnRef as String : true,
                kSecMatchLimit as String: kSecMatchLimitAll
            ]
        
            var result: AnyObject?
        
            let lastResultCode = withUnsafeMutablePointer(to: &result) {
                SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
            }
        
        //  this also works, although I am not sure if it is as save as calling withUnsafeMutabePointer
        //  let lastResultCode = SecItemCopyMatching(query as CFDictionary, &result)
        
            var values = [String: AnyObject]()
            if lastResultCode == noErr {
                let array = result as? Array<Dictionary<String, Any>>
        
                for item in array! {
                    if let key = item[kSecAttrAccount as String] as? String,
                        let value = item[kSecValueData as String] as? Data {
                        values[key] = String(data: value, encoding:.utf8) as AnyObject?
                    }
                    // including identities and certificates in dictionary
                    else if let key = item[kSecAttrLabel as String] as? String,
                        let value = item[kSecValueRef as String] {
                        values[key] = value as AnyObject
                    }
                }
            }
        
            return values
        }
        

        【讨论】:

          【解决方案5】:

          带有 xcode 9.1 的 Swift 3 版本

          func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
          
              let query: [String: Any] = [
                  kSecClass as String : secClass,
                  kSecReturnData as String  : kCFBooleanTrue,
                  kSecReturnAttributes as String : kCFBooleanTrue,
                  kSecReturnRef as String : kCFBooleanTrue,
                  kSecMatchLimit as String : kSecMatchLimitAll
              ]
          
              var result: AnyObject?
          
              let lastResultCode = withUnsafeMutablePointer(to: &result) {
                  SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
              }
          
              var values = [String:String]()
              if lastResultCode == noErr {
                  let array = result as? Array<Dictionary<String, Any>>
          
                  for item in array! {
                      if let key = item[kSecAttrAccount as String] as? String,
                          let value = item[kSecValueData as String] as? Data {
                          values[key] = String(data: value, encoding:.utf8)
                      }
                  }
              }
          
              return values
          }
          

          可以这样称呼:

          debugPrint(getAllKeyChainItemsOfClass(kSecClassGenericPassword as String))
          

          【讨论】:

            【解决方案6】:

            还返回键的 Swift 3+ 版本 (kSecAttrAccount):

            open func getAllKeyChainItemsOfClass(_ secClass: String) -> [String:String] {
            
                    let query: [String: Any] = [
                        kSecClass : secClass,
                        kSecReturnData  : kCFBooleanTrue,
                        kSecReturnAttributes : kCFBooleanTrue,
                        kSecReturnRef : kCFBooleanTrue,
                        kSecMatchLimit : kSecMatchLimitAll
                    ]
            
                    var result: AnyObject?
            
                    let lastResultCode = withUnsafeMutablePointer(to: &result) {
                        SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
                    }
            
                    var values = [String:String]()
                    if lastResultCode == noErr {
                        let array = result as? Array<Dictionary<String, Any>>
            
                        for item in array! {
                            if let key = item[kSecAttrAccount] as? String, 
                               let value = item[kSecValueData] as? Data {
                               values[key] = String(data: value, encoding:.utf8) 
                            }
                        }
                    }
            
                    return values
                }
            

            【讨论】:

            • 在 Xcode 9.2 上使用 Swift 3 这在 let key : String = item[kSecAttrAccount] as! StringCould not cast value of type '__NSCFData' (0x109b30348) to 'NSString' (0x1069030d0). 处崩溃。我不知道为什么。
            • 更改设置键和值的行使崩溃不会发生,尽管它消除了几个我不知道如何拔出的对象:if let key = item[kSecAttrAccount] as? String, let value = item[kSecValueData] as? Data {
            猜你喜欢
            • 2015-03-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多