JTKeychain

//
// SSKeychain.h
// SSToolkit
//
// Created by Sam Soffes on 5/19/10.
// Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
//

import <Foundation/Foundation.h>

import <Security/Security.h>

/** Error codes that can be returned in NSError objects. /
typedef enum {
/
* No error. */
SSKeychainErrorNone = noErr,

/** Some of the arguments were invalid. */
SSKeychainErrorBadArguments = -1001,

/** There was no password. */
SSKeychainErrorNoPassword = -1002,

/** One or more parameters passed internally were not valid. */
SSKeychainErrorInvalidParameter = errSecParam,

/** Failed to allocate memory. */
SSKeychainErrorFailedToAllocated = errSecAllocate,

/** No trust results are available. */
SSKeychainErrorNotAvailable = errSecNotAvailable,

/** Authorization/Authentication failed. */
SSKeychainErrorAuthorizationFailed = errSecAuthFailed,

/** The item already exists. */
SSKeychainErrorDuplicatedItem = errSecDuplicateItem,

/** The item cannot be found.*/
SSKeychainErrorNotFound = errSecItemNotFound,

/** Interaction with the Security Server is not allowed. */
SSKeychainErrorInteractionNotAllowed = errSecInteractionNotAllowed,

/** Unable to decode the provided data. */
SSKeychainErrorFailedToDecode = errSecDecode

} SSKeychainErrorCode;

extern NSString *const kSSKeychainErrorDomain;

/** Account name. */
extern NSString *const kSSKeychainAccountKey;

/**
Time the item was created.

The value will be a string.
*/
extern NSString *const kSSKeychainCreatedAtKey;

/** Item class. */
extern NSString *const kSSKeychainClassKey;

/** Item description. */
extern NSString *const kSSKeychainDescriptionKey;

/** Item label. */
extern NSString *const kSSKeychainLabelKey;

/** Time the item was last modified.

The value will be a string.
*/
extern NSString *const kSSKeychainLastModifiedKey;

/** Where the item was created. */
extern NSString *const kSSKeychainWhereKey;

/**
Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system
Keychain on Mac OS X and iOS.

This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors.
SSKeychain has since switched to a simpler implementation that was abstracted from SSToolkit.
*/
@interface JTKeychain : NSObject

///-----------------------
/// @name Getting Accounts
///-----------------------

/**
Returns an array containing the Keychain's accounts, or nil if the Keychain has no accounts.

See the NSString constants declared in SSKeychain.h for a list of keys that can be used when accessing the
dictionaries returned by this method.

@return An array of dictionaries containing the Keychain's accounts, or nil if the Keychain doesn't have any
accounts. The order of the objects in the array isn't defined.

@see allAccounts:
*/

  • (NSArray *)allAccounts;

/**
Returns an array containing the Keychain's accounts, or nil if the Keychain doesn't have any
accounts.

See the NSString constants declared in SSKeychain.h for a list of keys that can be used when accessing the
dictionaries returned by this method.

@param error If accessing the accounts fails, upon return contains an error that describes the problem.

@return An array of dictionaries containing the Keychain's accounts, or nil if the Keychain doesn't have any
accounts. The order of the objects in the array isn't defined.

@see allAccounts
*/

  • (NSArray *)allAccounts:(NSError **)error;

/**
Returns an array containing the Keychain's accounts for a given service, or nil if the Keychain doesn't have any
accounts for the given service.

See the NSString constants declared in SSKeychain.h for a list of keys that can be used when accessing the
dictionaries returned by this method.

@param serviceName The service for which to return the corresponding accounts.

@return An array of dictionaries containing the Keychain's accountsfor a given serviceName, or nil if the Keychain
doesn't have any accounts for the given serviceName. The order of the objects in the array isn't defined.

@see accountsForService:error:
*/

  • (NSArray *)accountsForService:(NSString *)serviceName;

/**
Returns an array containing the Keychain's accounts for a given service, or nil if the Keychain doesn't have any
accounts for the given service.

@param serviceName The service for which to return the corresponding accounts.

@param error If accessing the accounts fails, upon return contains an error that describes the problem.

@return An array of dictionaries containing the Keychain's accountsfor a given serviceName, or nil if the Keychain
doesn't have any accounts for the given serviceName. The order of the objects in the array isn't defined.

@see accountsForService:
*/

  • (NSArray *)accountsForService:(NSString *)serviceName error:(NSError **)error;

///------------------------
/// @name Getting Passwords
///------------------------

/**
Returns a string containing the password for a given account and service, or nil if the Keychain doesn't have a
password for the given parameters.

@param serviceName The service for which to return the corresponding password.

@param account The account for which to return the corresponding password.

@return Returns a string containing the password for a given account and service, or nil if the Keychain doesn't
have a password for the given parameters.

@see passwordForService:account:error:
*/

  • (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;

/**
Returns a string containing the password for a given account and service, or nil if the Keychain doesn't have a
password for the given parameters.

@param serviceName The service for which to return the corresponding password.

@param account The account for which to return the corresponding password.

@param error If accessing the password fails, upon return contains an error that describes the problem.

@return Returns a string containing the password for a given account and service, or nil if the Keychain doesn't
have a password for the given parameters.

@see passwordForService:account:
*/

  • (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

/**
Returns the password data for a given account and service, or nil if the Keychain doesn't have data
for the given parameters.

@param serviceName The service for which to return the corresponding password.

@param account The account for which to return the corresponding password.

@param error If accessing the password fails, upon return contains an error that describes the problem.

@return Returns a the password data for the given account and service, or nil if the Keychain doesn't
have data for the given parameters.

@see passwordDataForService:account:error:
*/

  • (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;

/**
Returns the password data for a given account and service, or nil if the Keychain doesn't have data
for the given parameters.

@param serviceName The service for which to return the corresponding password.

@param account The account for which to return the corresponding password.

@param error If accessing the password fails, upon return contains an error that describes the problem.

@return Returns a the password data for the given account and service, or nil if the Keychain doesn't
have a password for the given parameters.

@see passwordDataForService:account:
*/

  • (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

///-------------------------
/// @name Deleting Passwords
///-------------------------

/**
Deletes a password from the Keychain.

@param serviceName The service for which to delete the corresponding password.

@param account The account for which to delete the corresponding password.

@return Returns YES on success, or NO on failure.

@see deletePasswordForService:account:error:
*/

  • (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;

/**
Deletes a password from the Keychain.

@param serviceName The service for which to delete the corresponding password.

@param account The account for which to delete the corresponding password.

@param error If deleting the password fails, upon return contains an error that describes the problem.

@return Returns YES on success, or NO on failure.

@see deletePasswordForService:account:
*/

  • (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

///------------------------
/// @name Setting Passwords
///------------------------

/**
Sets a password in the Keychain.

@param password The password to store in the Keychain.

@param serviceName The service for which to set the corresponding password.

@param account The account for which to set the corresponding password.

@return Returns YES on success, or NO on failure.

@see setPassword:forService:account:error:
*/

  • (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;

/**
Sets a password in the Keychain.

@param password The password to store in the Keychain.

@param serviceName The service for which to set the corresponding password.

@param account The account for which to set the corresponding password.

@param error If setting the password fails, upon return contains an error that describes the problem.

@return Returns YES on success, or NO on failure.

@see setPassword:forService:account:
*/

  • (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

/**
Sets arbirary data in the Keychain.

@param password The data to store in the Keychain.

@param serviceName The service for which to set the corresponding password.

@param account The account for which to set the corresponding password.

@param error If setting the password fails, upon return contains an error that describes the problem.

@return Returns YES on success, or NO on failure.

@see setPasswordData:forService:account:error:
*/

  • (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;

/**
Sets arbirary data in the Keychain.

@param password The data to store in the Keychain.

@param serviceName The service for which to set the corresponding password.

@param account The account for which to set the corresponding password.

@param error If setting the password fails, upon return contains an error that describes the problem.

@return Returns YES on success, or NO on failure.

@see setPasswordData:forService:account:
*/

  • (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;

///--------------------
/// @name Configuration
///--------------------

if __IPHONE_4_0 && TARGET_OS_IPHONE

/**
Returns the accessibility type for all future passwords saved to the Keychain.

@return Returns the accessibility type.

The return value will be NULL or one of the "Keychain Item Accessibility Constants" used for determining when a
keychain item should be readable.

@see accessibilityType
*/

  • (CFTypeRef)accessibilityType;

/**
Sets the accessibility type for all future passwords saved to the Keychain.

@param accessibilityType One of the "Keychain Item Accessibility Constants" used for determining when a keychain item
should be readable.

If the value is NULL (the default), the Keychain default will be used.

@see accessibilityType
*/

  • (void)setAccessibilityType:(CFTypeRef)accessibilityType;

endif

@end

//
// SSKeychain.m
// SSToolkit
//
// Created by Sam Soffes on 5/19/10.
// Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
//

import "JTKeychain.h"

NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain";

NSString *const kSSKeychainAccountKey = @"acct";
NSString *const kSSKeychainCreatedAtKey = @"cdat";
NSString *const kSSKeychainClassKey = @"labl";
NSString *const kSSKeychainDescriptionKey = @"desc";
NSString *const kSSKeychainLabelKey = @"labl";
NSString *const kSSKeychainLastModifiedKey = @"mdat";
NSString *const kSSKeychainWhereKey = @"svce";

if __IPHONE_4_0 && TARGET_OS_IPHONE

CFTypeRef SSKeychainAccessibilityType = NULL;

endif

@interface JTKeychain ()

  • (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account;
    @end

@implementation JTKeychain

pragma mark - Getting Accounts

  • (NSArray *)allAccounts {
    return [self accountsForService:nil error:nil];
    }
  • (NSArray *)allAccounts:(NSError **)error {
    return [self accountsForService:nil error:error];
    }
  • (NSArray *)accountsForService:(NSString *)service {
    return [self accountsForService:service error:nil];
    }
  • (NSArray *)accountsForService:(NSString *)service error:(NSError **)error {
    OSStatus status = SSKeychainErrorBadArguments;
    NSMutableDictionary *query = [self _queryForService:service account:nil];

if __has_feature(objc_arc)

[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
[query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];

else

[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
[query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit];

endif

CFTypeRef result = NULL;

if __has_feature(objc_arc)

status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);

else

status = SecItemCopyMatching((CFDictionaryRef)query, &result);

endif

if (status != noErr && error != NULL) {
    *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
    return nil;
}

if __has_feature(objc_arc)

return (__bridge_transfer NSArray *)result;

else

return [(NSArray *)result autorelease];

endif

}

pragma mark - Getting Passwords

  • (NSString *)passwordForService:(NSString *)service account:(NSString *)account {
    return [self passwordForService:service account:account error:nil];
    }
  • (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
    NSData *data = [self passwordDataForService:service account:account error:error];
    if (data.length > 0) {
    NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding];

if !__has_feature(objc_arc)

    [string autorelease];

endif

    return string;
}

return nil;

}

  • (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account {
    return [self passwordDataForService:service account:account error:nil];
    }
  • (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
    OSStatus status = SSKeychainErrorBadArguments;
    if (!service || !account) {
    if (error) {
    *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
    }
    return nil;
    }

    CFTypeRef result = NULL;
    NSMutableDictionary *query = [self _queryForService:service account:account];

if __has_feature(objc_arc)

[query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);

else

[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
status = SecItemCopyMatching((CFDictionaryRef)query, &result);

endif

if (status != noErr && error != NULL) {
    *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
    return nil;
}

if __has_feature(objc_arc)

return (__bridge_transfer NSData *)result;

else

return [(NSData *)result autorelease];

endif

}

pragma mark - Deleting Passwords

  • (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account {
    return [self deletePasswordForService:service account:account error:nil];
    }
  • (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
    OSStatus status = SSKeychainErrorBadArguments;
    if (service && account) {
    NSMutableDictionary *query = [self _queryForService:service account:account];

if __has_feature(objc_arc)

    status = SecItemDelete((__bridge CFDictionaryRef)query);

else

    status = SecItemDelete((CFDictionaryRef)query);

endif

}
if (status != noErr && error != NULL) {
    *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
}
return (status == noErr);

}

pragma mark - Setting Passwords

  • (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account {
    return [self setPassword:password forService:service account:account error:nil];
    }
  • (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
    NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding];
    return [self setPasswordData:data forService:service account:account error:error];
    }
  • (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account {
    return [self setPasswordData:password forService:service account:account error:nil];
    }
  • (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
    OSStatus status = SSKeychainErrorBadArguments;
    if (password && service && account) {
    [self deletePasswordForService:service account:account];
    NSMutableDictionary *query = [self _queryForService:service account:account];

if __has_feature(objc_arc)

    [query setObject:password forKey:(__bridge id)kSecValueData];

else

    [query setObject:password forKey:(id)kSecValueData];

endif

if __IPHONE_4_0 && TARGET_OS_IPHONE

    if (SSKeychainAccessibilityType) {

if __has_feature(objc_arc)

        [query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible];

else

        [query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible];

endif

    }

endif

if __has_feature(objc_arc)

    status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);

else

    status = SecItemAdd((CFDictionaryRef)query, NULL);

endif

}
if (status != noErr && error != NULL) {
    *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
}
return (status == noErr);

}

pragma mark - Configuration

if __IPHONE_4_0 && TARGET_OS_IPHONE

  • (CFTypeRef)accessibilityType {
    return SSKeychainAccessibilityType;
    }
  • (void)setAccessibilityType:(CFTypeRef)accessibilityType {
    CFRetain(accessibilityType);
    if (SSKeychainAccessibilityType) {
    CFRelease(SSKeychainAccessibilityType);
    }
    SSKeychainAccessibilityType = accessibilityType;
    }

endif

pragma mark - Private

  • (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account {
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];

if __has_feature(objc_arc)

[dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

else

[dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

endif

if (service) {

if __has_feature(objc_arc)

    [dictionary setObject:service forKey:(__bridge id)kSecAttrService];

else

    [dictionary setObject:service forKey:(id)kSecAttrService];

endif

}

if (account) {

if __has_feature(objc_arc)

    [dictionary setObject:account forKey:(__bridge id)kSecAttrAccount];

else

    [dictionary setObject:account forKey:(id)kSecAttrAccount];

endif

}

return dictionary;

}

@end

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,728评论 6 526
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,623评论 3 411
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,371评论 0 372
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,174评论 1 306
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,014评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,532评论 1 319
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,641评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,793评论 0 283
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,294评论 1 329
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,276评论 3 352
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,423评论 1 364
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,980评论 5 354
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,678评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,087评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,307评论 1 279
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,997评论 3 385
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,404评论 2 370

推荐阅读更多精彩内容

  • 目前获取唯一标识一般采用UUID+keychain来使用,实测可以做到,不过还没上架AppStore不确定能不能通...
    天明依旧阅读 836评论 0 0
  • 最近搞环信聊天,需求是游客身份也可以进行聊天,当用户注册了我们的APP后也需要把游客身份切换过来进行聊天,首先我们...
    EncourageMan阅读 1,978评论 2 3
  • 发现写博客想写明白也是一件不容易的事情。 这次拿YYKIt 源码 分析分析。希望这次能写的更好些。 YYKit 系...
    充满活力的早晨阅读 6,606评论 4 16
  • 这些都是转载的! iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个io...
    ANTI_JAM阅读 837评论 0 0
  • /**ios常见的几种加密方法: 普通的加密方法是讲密码进行加密后保存到用户偏好设置( [NSUserDefaul...
    彬至睢阳阅读 2,949评论 0 7