Most applications need to use Keychain Services only to add a new password to a keychain or retrieve a password when needed. Keychain Services provides the following pairs of functions for accomplishing these tasks:
SecKeychainAddInternetPassword and SecKeychainFindInternetPassword (for Internet passwords)
SecKeychainAddGenericPassword and SecKeychainFindGenericPassword (for generic passwords
You use Internet passwords for accessing servers and websites over the Internet, and generic passwords for any other password-protected service (such as a database or scheduling application). AppleShare passwords (that is, keychain items with a class code of kSecAppleSharePasswordItemClass) are stored as generic passwords.
Note: AppleShare passwords created with the legacy Keychain Manager function KCAddAppleSharePassword are stored as Internet password items. You can use the SecKeychainAddInternetPassword function to store an AppleShare password, but it will appear in the Keychain Access utility as an Internet password. To create a keychain item with a class code of kSecAppleSharePasswordItemClass, you must use the lower-level API described in Keychain Services Reference.
The “find” functions retrieve information (attributes or secure data) from an item in the keychain. The “add” functions add an item to a keychain. These functions call other Keychain Services functions to accomplish their tasks. Because Keychain Services allocates the buffers in which the item data and attributes are returned, you must call SecKeychainItemFreeContent to free these buffers after using one of the find functions to retrieve attributes or secure data from a keychain item. (If Keychain Services does not find the item or fails to return any data for some other reason, it does not allocate any buffers and you should not call the SecKeychainItemFreeContent function.)
Figure 2-1 shows a flowchart of how an application might use these functions to gain access to an Internet FTP server.
The user starts by selecting a File Transfer Protocol (FTP) server. The application calls SecKeychainFindInternetPassword, passing it attributes that identify the service and the user to seek. If the password is on the keychain, the function returns the password to the application, which sends it to the FTP server to authenticate the user. The application then calls SecKeychainItemFreeContent to free the data buffer allocated for the password (note that you should not call this function if no data is returned). If the authentication succeeds, the routine is finished. If the authentication fails, the application displays a dialog to request the user name and password.
If the password is not on the keychain, then SecKeychainFindInternetPassword returns the errSecItemNotFound result code. In this case as well, the application displays a dialog to request the user name and password. (This dialog should also include a Cancel button, but that choice was omitted from the figure to keep the flowchart from becoming overly complex.)
Having obtained the password from the user, the application proceeds to authenticate the user. When the authentication has succeeded, the application can assume that the information entered by the user was valid. The application then displays another dialog asking the user whether to save the password on the keychain. If the user selects No, then the routine is finished. If the user selects Yes, then the application calls the SecKeychainAddInternetPassword function (if this is a new keychain item), or the SecKeychainItemModifyAttributesAndData function (to update an existing keychain item) before ending the routine.
If there is no keychain, the SecKeychainFindInternetPassword or SecKeychainAddInternetPassword function displays a dialog allowing the user to “reset to defaults” (see Figure 1-5), which creates a new keychain named login.keychain with the user’s login account password. If the keychain is locked, the function displays a dialog requesting the user to enter a password to unlock the keychain (Figure 1-6). The user can cancel the operation at this time as well.
“Getting and setting passwords” shows how a typical application might use Keychain Services functions to get and set passwords for generic items. You can get and set keychain item attributes (such as user name or service name) using these same functions; see Listing 2-2 for an example.
Listing 2-1 Getting and setting passwords
#include <CoreFoundation/CoreFoundation.h> |
#include <Security/Security.h> |
#include <CoreServices/CoreServices.h> |
//Call SecKeychainAddGenericPassword to add a new password to the keychain: |
OSStatus StorePasswordKeychain (void* password,UInt32 passwordLength) |
{ |
OSStatus status; |
status = SecKeychainAddGenericPassword ( |
NULL, // default keychain |
10, // length of service name |
"SurfWriter", // service name |
10, // length of account name |
"MyUserAcct", // account name |
passwordLength, // length of password |
password, // pointer to password data |
NULL // the item reference |
); |
return (status); |
} |
//Call SecKeychainFindGenericPassword to get a password from the keychain: |
OSStatus GetPasswordKeychain (void *passwordData,UInt32 *passwordLength, |
SecKeychainItemRef *itemRef) |
{ |
OSStatus status1 ; |
status1 = SecKeychainFindGenericPassword ( |
NULL, // default keychain |
10, // length of service name |
"SurfWriter", // service name |
10, // length of account name |
"MyUserAcct", // account name |
passwordLength, // length of password |
passwordData, // pointer to password data |
itemRef // the item reference |
); |
return (status1); |
} |
//Call SecKeychainItemModifyAttributesAndData to change the password for |
// an item already in the keychain: |
OSStatus ChangePasswordKeychain (SecKeychainItemRef itemRef) |
{ |
OSStatus status; |
void * password = "myNewP4sSw0rD"; |
UInt32 passwordLength = strlen(password); |
status = SecKeychainItemModifyAttributesAndData ( |
itemRef, // the item reference |
NULL, // no change to attributes |
passwordLength, // length of password |
password // pointer to password data |
); |
return (status); |
} |
/* ********************************************************************** */ |
int main (int argc, const char * argv[]) { |
OSStatus status; |
OSStatus status1; |
void * myPassword = "myP4sSw0rD"; |
UInt32 myPasswordLength = strlen(myPassword); |
void *passwordData = nil; // will be allocated and filled in by |
//SecKeychainFindGenericPassword |
SecKeychainItemRef itemRef = nil; |
UInt32 passwordLength = nil; |
status1 = GetPasswordKeychain (&passwordData,&passwordLength,&itemRef); //Call |
//SecKeychainFindGenericPassword |
if (status1 == noErr) //If call was successful, authenticate user |
//and continue. |
{ |
//Free the data allocated by SecKeychainFindGenericPassword: |
status = SecKeychainItemFreeContent ( |
NULL, //No attribute data to release |
passwordData //Release data buffer allocated by |
//SecKeychainFindGenericPassword |
); |
} |
if (status1 == errSecItemNotFound) { //Is password on keychain? |
/* |
If password is not on keychain, display dialog to prompt user for |
name and password. |
Authenticate user. If unsuccessful, prompt user again for name and password. |
If successful, ask user whether to store new password on keychain; if no, return. |
If yes, store password: |
*/ |
status = StorePasswordKeychain (myPassword,myPasswordLength); //Call |
// SecKeychainAddGenericPassword |
return (status); |
} |
/* |
If password is on keychain, authenticate user. |
If authentication succeeds, return. |
If authentication fails, prompt user for new user name and password and |
authenticate again. |
If unsuccessful, prompt again. |
If successful, ask whether to update keychain with new information. If no, return. |
If yes, store new information: |
*/ |
status = ChangePasswordKeychain (itemRef); //Call |
// SecKeychainItemModifyAttributesAndData |
if (itemRef) CFRelease(itemRef); |
return (status); |
} |
This example follows the same general sequence that was shown in Figure 2-1; however, unlike the figure, the example illustrates the use of generic passwords rather than Internet passwords.
Important: You should not cache passwords, because the user can change them using Keychain Access or another program and the data may no longer be valid. In addition, the long-term storage of passwords by applications negates the value of the keychain.
Although the example in “Getting and setting passwords” is written in procedural C, you can call these same functions using Objective C. Listing 2-2 shows how you might create an Internet password item if you wanted some custom attribute values. Note that attribute strings should all be encoded in UTF-8 format.
Last updated: 2007-01-08