Adding Items to and Querying the iOS Keychain with Swift

前端 未结 5 1790
遇见更好的自我
遇见更好的自我 2020-12-25 14:59

I\'m having trouble converting all of the Objective C code samples that are available for adding data and querying data from the iOS Keychain into

相关标签:
5条回答
  • 2020-12-25 15:05

    I wrote a demo app and helper functions for this simple task: writing/reading a text string for a given key in Keychain.

    https://github.com/marketplacer/keychain-swift

    let keychain = KeychainSwift()
    keychain.set("hello world", forKey: "my key")
    keychain.get("my key")
    keychain.delete("my key")
    
    0 讨论(0)
  • 2020-12-25 15:08

    For Swift users

    Single line code to add/retrieve/update fields in Keychain:
    https://github.com/jrendel/SwiftKeychainWrapper

    Usage

    Add a string value to keychain:

    let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey")  
    

    Retrieve a string value from keychain:

    let retrievedString: String? = KeychainWrapper.stringForKey("myKey")
    

    Remove a string value from keychain:

    let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey")
    
    0 讨论(0)
  • 2020-12-25 15:14

    My interpretation on how to add, get, delete passwords (for those who are lazy to use libraries presented in this thread):

    // Saving password associated with the login and service
    let userAccount = "user's login"
    let service = "service name"
    let passwordData: NSData = self.textfield_password.text!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    
    let keychainQuery: [NSString: NSObject] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: userAccount,
            kSecAttrService: service,
            kSecValueData: passwordData]    
    
    SecItemDelete(keychainQuery as CFDictionaryRef) //Deletes the item just in case it already exists
    let keychain_save_status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
    print("Keychain saving code is: \(keychain_save_status)")
    
    ...
    
    // Getting the password associated with the login and service
    let userAccount = "user's login"
    let service = "service name"
    let keychainQuery: [NSString: NSObject] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrService: service,
        kSecAttrAccount: userAccount,
        kSecReturnData: kCFBooleanTrue,
        kSecMatchLimit: kSecMatchLimitOne]
    
    var rawResult: AnyObject?
    let keychain_get_status: OSStatus = SecItemCopyMatching(keychainQuery, &rawResult)
    print("Keychain getting code is: \(keychain_get_status)")
    
    if (keychain_get_status == errSecSuccess) {
        let retrievedData = rawResult as? NSData
        let pass = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding)
        print("Username: \(userAccount), password: \(pass!)")
        // Do your work with the retrieved password here
    } else {
        print("No login data found in Keychain.")
    
    ...
    
    //Deleting user's credentials from Keychain. Password is optional for the query when you delete, in most cases you won't know it after all.
    let userAccount = "user's login"
    let service = "service name"
    
    let keychainQuery: [NSString: NSObject] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: userAccount,
            kSecAttrService: service]
    let keychain_delete_status: OSStatus = SecItemDelete(keychainQuery as CFDictionaryRef)
    print("Keychain deleting code is: \(keychain_delete_status)")
    

    The result codes and other useful info can be found in the official documentation: https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/

    0 讨论(0)
  • 2020-12-25 15:22

    I think I've worked out the solution. I've edited my post above to include the code that works (at least for me). I've also blogged about it here: using the iOS Keychain with Swift (example code).

    Update 11 Aug: I've updated the code in the blog post based on rshelby's comments. Take a look.

    Update: I turned this into a keychain library written in Swift called Locksmith.


    Save

    class func save(service: NSString, data: NSString) {
      var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
      // Instantiate a new default keychain query
      var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
    
      // Delete any existing items
      SecItemDelete(keychainQuery as CFDictionaryRef)
    
      // Add the new keychain item
      var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
    
      // Check that it worked ok
      println("Saving status code is: \(status)")
    }
    

    Load

      class func load(service: NSString) -> AnyObject? {
        // Instantiate a new default keychain query
        // Tell the query to return a result
        // Limit our results to one item
        var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, kCFBooleanTrue, kSecMatchLimitOne], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit])
    
    
    
        // I'm not too sure what's happening here...
        var dataTypeRef :Unmanaged<AnyObject>?
    
        // Search for the keychain items
        let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
    
    
        println("Loading status code is: \(status)")
    
        // I'm not too sure what's happening here...
        let opaque = dataTypeRef?.toOpaque()
    
        if let op = opaque? {
          let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
          println("Retrieved the following data from the keychain: \(retrievedData)")
          var str = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
          println("The decoded string is \(str)")
        } else {
          println("Nothing was retrieved from the keychain.")
        }
    
        return nil
      }
    

    Usage (view controller)

    KeychainService.saveToken("sometoken")
    KeychainService.loadToken()
    

    which uses these convenience methods

    class func saveToken(token: NSString) {
        self.save("service", data: token)
      }
    
    class func loadToken() {
        var token = self.load("service")
        if let t = token {
          println("The token is: \(t)")
        }
      }
    

    This leads to the output in the console:

    Saving status code is: 0
    Loading status code is: 0
    Retrieved the following data from the keychain: <736f6d65 746f6b65 6e>
    The decoded string is sometoken
    
    0 讨论(0)
  • 2020-12-25 15:29

    In order to get this to work, you will need to retrieve the retained values of the keychain constants and store then first like so:

    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
    

    You can then reference the values in the NSMutableDictionary like so:

    var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
    

    I wrote a blog post about it at: http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/

    Hope this helps!

    rshelby

    0 讨论(0)
提交回复
热议问题