1 Article|Adding a Password to the Keychain 文章|添加密码到钥匙链
Add network credentials to the keychain on behalf of the user.
为用户添加网络密码到钥匙链.
2 Overview 概述
Use keychain services to enable simple, secure storage for users’ passwords. In this way you avoid repeatedly asking the user for a password, and you don’t have to implement your own encryption, which can be error prone.
钥匙链服务可以让用户简单而安全地存储他们的密码.使用这种方式,我们不再需要频繁让用户提供密码,并且我们不用自己实现加密,自己加密往往很容易出错.
2.1 Get Set Up 开始配置
To get started, define a structure to hold the credentials as they move around your app in memory:
开始,我们定义一个结构体来保存用户名密码.
//swift:
struct Credentials {
var username: String
var password: String
}
//oc:
struct Credentials {
NSString * username;
NSString * password;
};
Next, define an error enumeration that you can use to communicate keychain access outcomes:
然后,定义一个错误枚举,你可以使用该枚举和钥匙链访问的返回值交流.
enum KeychainError: Error {
case noPassword
case unexpectedPasswordData
case unhandledError(status: OSStatus)
}
Then, identify the server that your app is working with:
然后,定义应用访问的服务器:
static let server = "www.example.com"
2.2 Create an Add Query创建一个添加查询
Using an instance of the credentials structure and the server constant, you can create an add query:
使用Credentials结构体实例和server常量,创建一个添加查询:
let account = credentials.username
let password = credentials.password.data(using: String.Encoding.utf8)!
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
kSecAttrAccount as String: account,
kSecAttrServer as String: server,
kSecValueData as String: password]
The query dictionary’s first key-value pair indicates that the item is an Internet password, from which keychain services infers that the data is secret and requires encryption. This also ensures that the item has attributes that distinguish it from other Internet passwords, such as the server and account to which it applies. Indeed, the next two key-value pairs in the query provide this information, attaching the user name acquired from the user as the account, along with a domain name appropriate to this password as the server.
查询字典的第一个键值对标识这个钥匙链项目的类型是一个网络密码,钥匙链服务根据该类型推断该数据是私密的,并且需要加密.并且确保该项目包含区别其他网络密码的属性,比如该密码要应用的服务器和账号.接下来的两个键值对的确也提供了这些信息,用户名作为账号,密码使用的域名作为服务器.
Note注意:
Keychain services also offers the related kSecClassGenericPassword item class. Generic passwords are similar in most respects to Internet passwords, but they lack certain attributes specific to remote access (for example, they don’t have a kSecAttrServer attribute). When you don’t need these extra attributes, use a generic password instead.
钥匙链服务还提供了kSecClassGenericPassword类型的钥匙链项目.通用密码和网络密码在很多方面相同,但是通用密码缺少特定表明远程访问的属性(比如,他们没有kSecAttrServer属性).如果你不需要这些属性,使用通用密码替代.
Although not necessary in this case, you could further characterize the password by specifying additional attributes, such as the port number or the network protocol. For example, if you needed to store distinct FTP and HTTP credentials for the same user working on the same server, you could add the kSecAttrProtocol attribute to distinguish between them.
尽管在该示例中不需要,但是你可以更进一步地描述这个密码,通过指定附加的属性,比如端口号或者网络协议.比如,如果你需要分别存储同一个用户在同一个服务器上的FTP密码和HTTP密码,你需要增加kSecAttrProtocol属性来区分他们.
Finally, the query contains the password from the user, encoded as a Data instance.
最后,该查询包含了用户的密码,该密码被编码为NSData类型.
2.3 Add the Item添加钥匙链项目
With the query complete, you simply feed it to the SecItemAdd function:
查询已经创建完毕,现在只需要将它传入SecItemAdd函数:
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
Although you typically ignore the return data supplied by reference in the second argument on an add operation (as demonstrated here), always check the function’s return status to ensure that the operation succeeds. The operation might fail, for example, if an item with the given attributes already exists.
尽管通常在添加操作中忽略第二个参数引用的返回数据,我们还是应该要检查函数的返回状态来确保操作成功.该操作可能失败,例如,一个具有相同属性的项目已经存在.
Note注意:
If you do want the return data, keep in mind that the SecItemAdd function behaves much like the SecItemCopyMatching function in this regard. See Searching for Keychain Items for details.
如果你需要返回的数据,需要留心SecItemAdd函数在这一点上和SecItemCopyMatching函数是很类似的.要了解详细信息,请参考Searching for Keychain Items查询钥匙链项目
2.4 Ensure Searchability确保可查询性
To be able to find the item later, you’re going to use your knowledge of its attributes. In this example, the server and the account are the item’s distinguishing characteristics. For constant attributes (here, the server), use the same value during lookup. In contrast, the account attribute is dynamic, because it holds a value provided by the user at runtime. As long as your app never adds similar items with varying attributes (such as passwords for different accounts on the same server), you can omit these dynamic attributes as search parameters and instead retrieve them along with the item. As a result, when you look up the password, you also get the corresponding username.
为了能够在之后找到该项目,你必须使用项目的属性.在这个例子中,服务器和账号是该项目用以区别其他项目的特征.对于常量属性(在这里是服务器属性),在查询时,使用一样的值.相反,账号属性是动态的,因为他是用户在运行时提供的.只要你的应用不填加不同属性的类似的项目(比如同一个服务器上不同账号的密码),你可以省略这些动态属性,并且将这些属性和钥匙链项目一起得到.这就导致了,当你查询密码时,你同时得到了对应的用户名.
If your app does add items with varying dynamic attributes, you’ll need a way to choose among them during retrieval. One option is to record information about the items in another way. For example, if you keep records of users in a Core Data model, you store the username there after using keychain services to store the password field. Later, you use the user name pulled from your data model to condition the search for the password.
如果你的应用添加了不同的动态属性的项目,你需要一种方式在检索中选择你需要的.一个选择是用另一种方式记录项目的信息.比如,如果你使用CoreData中保存用户记录,那么,你在使用钥匙链服务保存密码后,同事保存用户名.之后,你使用数据模型中得到的用户名来寻找密码.
In other cases, it may make sense to further characterize the item by adding more attributes. For example, you might include the kSecAttrLabel attribute in the original add query, providing a string that marks the item for the particular purpose. Then you’ll be able to use this attribute to narrow your search later.
在其他情况下,添加更多的属性进一步描述钥匙链项目是很有意义的.比如,你可以在原始添加查询中添加kSecAttrLabel属性,该属性提供了一个字符串来标记该项目的特定作用.之后,你可以通过该属性来缩小你的查询.
3 See Also参考
3.1 Adding Keychain Items添加钥匙链项目
3.1.1 SecItemAdd
Adds one or more items to a keychain.
保存一个或者多个钥匙链项目到钥匙链.
3.1.3 Item Class Keys and Values 钥匙链项目的类型的键与值
Specify the class of a keychain item.
指定钥匙链项目的类型.
3.1.3 Item Attribute Keys and Values 钥匙链项目的属性的键与值
Specify the attributes of keychain items.
指定钥匙链项目的属性.