4. 똑같은 비밀번호를 여러 곳에 사용합니까?
여러 개의 계정에 (모든 계정은 아니지만)
같은 비밀번호를 사용한다
모든 계정에 다 다른 비밀번호를 사용한다
모든 계정에 같은 비밀번호를 사용한다
Online Security Survey by Google / Harris Poll
11. KeyChain Item
KeyChain Item
Data
Item Class
Key / Value
Attributes
Item Attribute
Key / Value
query[kSecClass as String] = kSecClassGenericPassword
Class / Attribute - Key : Value
21. Access Group 가족 앱끼리 비밀 공유하기 🤫
[$(teamID).com.example.SharedItems,
$(teamID).com.example.AppOne]
[$(teamID).com.example.SharedItems,
$(teamID).com.example.AppTwo]
1. KeyChain Access Groups (Optional)
2. Application Identifier
3. Application Groups
31. func readPassword() throws -> String {
/*
Build a query to find the item that matches the service, account and
access group.
*/
var query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup:
essGroup)
query[kSecMatchLimit as String] = kSecMatchLimitOne
query[kSecReturnAttributes as String] = kCFBooleanTrue
query[kSecReturnData as String] = kCFBooleanTrue
// Try to fetch the existing keychain item that matches the query.
var queryResult: AnyObject?
let status = withUnsafeMutablePointer(to: &queryResult) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
// Check the return status and throw an error if appropriate.
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == noErr else { throw KeychainError.unhandledError(status: status) }
// Parse the password string from the query result.
guard let existingItem = queryResult as? [String : AnyObject],
let passwordData = existingItem[kSecValueData as String] as? Data,
let password = String(data: passwordData, encoding: String.Encoding.utf8)
else {
throw KeychainError.unexpectedPasswordData
}
return password
}
func savePassword(_ password: String) throws {
// Encode the password into an Data object.
let encodedPassword = password.data(using: String.Encoding.utf8)!
do {
// Check for an existing item in the keychain.
try _ = readPassword()
// Update the existing item with the new password.
var attributesToUpdate = [String : AnyObject]()
attributesToUpdate[kSecValueData as String] = encodedPassword as AnyObject?
let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup:
accessGroup)
let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
// Throw an error if an unexpected status was returned.
guard status == noErr else { throw KeychainError.unhandledError(status: status) }
}
static func passwordItems(forService service: String, accessGroup
[KeychainPasswordItem] {
// Build a query for all items that match the service and
var query = KeychainPasswordItem.keychainQuery(withService
query[kSecMatchLimit as String] = kSecMatchLimitAll
query[kSecReturnAttributes as String] = kCFBooleanTrue
query[kSecReturnData as String] = kCFBooleanFalse
// Fetch matching items from the keychain.
var queryResult: AnyObject?
let status = withUnsafeMutablePointer(to: &queryResult) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutab
}
// If no items were found, return an empty array.
guard status != errSecItemNotFound else { return [] }
// Throw an error if an unexpected status was returned.
guard status == noErr else { throw KeychainError.unhandled
// Cast the query result to an array of dictionaries.
guard let resultData = queryResult as? [[String : AnyObjec
KeychainError.unexpectedItemData }
// Create a `KeychainPasswordItem` for each dictionary in
var passwordItems = [KeychainPasswordItem]()
for result in resultData {
guard let account = result[kSecAttrAccount as String]
KeychainError.unexpectedItemData }
let passwordItem = KeychainPasswordItem(service: servi
accessGroup)
passwordItems.append(passwordItem)
Wrapper를 사용하는 이유.
var query = [String : AnyObject]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrService as String] = service as AnyObject?