iOS -- keyChain

最近写新项目,想搞一点高大上的,用keyChain来存储用户信息

keyChain的好处是可以使用苹果的加密来保证信息的安全,而且可以在app被删除之后保留信息,有传言在iOS10中keychain中的数据会随app一起删除,但是我用iOS11测试的结果是依然保留数据的

keyChain的另一个特性是同一个开发者帐号下的应用可以共享数据,这个我目前用不到

 

keyChain虽然有很多优点,但是读写信息还是挺麻烦的,需要两个string的认证,这相对于plist文件中的key-value相对来说麻烦一点

而且现在的应用一般不保留用户密码,而是用token来验证用户身份,所以目前keychain对我来说没有什么用

 

 

但是我还是要写一下keychain的使用方法哎嘿嘿

 

 

keychain是用SQLite进行存储的。用苹果的话来说是一个专业的数据库,加密我们保存的数据,可以通过metadata(attributes)进行高效的搜索。keychain适合保存一些比较小的数据量的数据,如果要保存大的数据,可以考虑文件的形式存储在磁盘上,在keychain里面保存解密这个文件的密钥。

keychain的类型

  • kSecClassGenericPassword
  • kSecClassInternetPassword
  • kSecClassCertificate
  • kSecClassKey
  • kSecClassIdentity

不同类型对应的属性:

iOS -- keyChain

既然苹果是采用SQLite去存储的,那么以上这些不同item的attribute可以理解是数据库里面表的字段。那么对keychain的操作其实也就是普通数据库的增删改查了。这样也许就会觉得那些API也没那么难用了。

 

下面是我写的keychainmanager类

#import <Foundation/Foundation.h>

@interface KeyChainManager : NSObject

+(void)addInfoWith:(NSString *)info
           account:(NSString *)account
           service:(NSString *)service;

+(void)deleteInfoWithAccount:(NSString *)account
                     service:(NSString *)service;


+(void)changeInfoWith:(NSString *)info
              account:(NSString *)account
              service:(NSString *)service;

+(NSString *)getInfoWithAccount:(NSString *)account
                        service:(NSString *)service;

@end

 

#import "KeyChainManager.h"

@implementation KeyChainManager

/*

 向keychain中添加item

 info是需要存储的信息

 account,service是确认item的标识符,keychain通过这两个值来确定一个item,进行增删改查

 */

+(void)addInfoWith:(NSString *)info
           account:(NSString *)account
           service:(NSString *)service{
    
    if (!info) {
        info = @"";
    }
    if (!account || [account isEqualToString:@""]) {
        NSLog(@"向keychain中添加item失败原因是kSecAttrAccount不存在");
        return;
    }
    if (!service || [service isEqualToString:@""]) {
        NSLog(@"向keychain中添加item失败原因是kSecAttrService不存在");
        return;
    }
    
    
    NSDictionary *query = @{(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlocked,
                            (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecValueData : [info dataUsingEncoding:NSUTF8StringEncoding],
                            (__bridge id)kSecAttrAccount :account,
                            (__bridge id)kSecAttrService : service,
                            };
    
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, nil);
    if (status == errSecSuccess) {
        NSLog(@"向keychain中添加item成功");
    }else{
        NSLog(@"向keychain中添加item失败%d",status);
    };
    
}

//删除keychain中item
+(void)deleteInfoWithAccount:(NSString *)account
                     service:(NSString *)service{
    
    if (!account || [account isEqualToString:@""]) {
        NSLog(@"删除keychain中item失败原因是kSecAttrAccount不存在");
        return;
    }
    if (!service || [service isEqualToString:@""]) {
        NSLog(@"删除keychain中item失败原因是kSecAttrService不存在");
        return;
    }
    
    
    NSDictionary *query = @{
                            (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrAccount :account,
                            (__bridge id)kSecAttrService : service,
                            };
    
    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
    if (status == errSecSuccess) {
        NSLog(@"删除keychain中item成功");
    }else{
        NSLog(@"删除keychain中item失败%d",status);
    };
    
}

//修改keychain中item数据
+(void)changeInfoWith:(NSString *)info
              account:(NSString *)account
              service:(NSString *)service{
    
    if (!info) {
        info = @"";
    }
    if (!account || [account isEqualToString:@""]) {
        NSLog(@"修改keychain中item数据失败原因是kSecAttrAccount不存在");
        return;
    }
    if (!service || [service isEqualToString:@""]) {
        NSLog(@"修改keychain中item数据失败原因是kSecAttrService不存在");
        return;
    }
    
    NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrAccount :account,
                            (__bridge id)kSecAttrService : service,
                            };
    NSDictionary *update = @{
                             (__bridge id)kSecValueData : [info dataUsingEncoding:NSUTF8StringEncoding],
                             };
    
    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update);
    if (status == errSecSuccess) {
        NSLog(@"修改keychain中item数据成功");
    }else{
        NSLog(@"修改keychain中item数据失败%d",status);
    };
    
}

//获取keychain中item数据
+(NSString *)getInfoWithAccount:(NSString *)account
                        service:(NSString *)service{
    
    if (!account || [account isEqualToString:@""]) {
        NSLog(@"获取keychain中item数据失败原因是kSecAttrAccount不存在");
        return nil;
    }
    if (!service || [service isEqualToString:@""]) {
        NSLog(@"获取keychain中item数据失败原因是kSecAttrService不存在");
        return nil;
    }
    
    NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecReturnData : @YES,
                            (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne,
                            (__bridge id)kSecAttrAccount :account,
                            (__bridge id)kSecAttrService : service,
                            };
    
    CFTypeRef dataTypeRef = NULL;
    
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataTypeRef);
    
    if (status == errSecSuccess) {
        NSString *pwd = [[NSString alloc] initWithData:(__bridge NSData * _Nonnull)(dataTypeRef) encoding:NSUTF8StringEncoding];
        NSLog(@"获取keychain中item数据成功==result:%@", pwd);
        return pwd;
    }else{
        NSLog(@"获取keychain中item数据失败%d",status);
        return nil;
    };

    
}


@end

 

 

 keychain可以进行应用间的数据共享

同一个开发者帐号下的应用可以共享存在keychain中的数据

这里需要到capabilities>keychain sharing

打开keychain sharing

可以看到有一个group,添加你想要获取数据的应用A的identifer,就可以获取它在keychain中的数据

可以对应用A的数据进行增删改查,但是这对应用A来说不是很安全

 

 

iOS - keychain 详解及变化

https://www.cnblogs.com/junhuawang/p/8194484.html

 

聊聊iOS Keychain

https://www.cnblogs.com/xiongwj0910/p/7151258.html

 

iOS -- keyChain

上一篇:用shell脚本递归遍历某个目录下的所有文件并移动到某个指定的目录中


下一篇:IOS Carthage安装、使用