zoukankan      html  css  js  c++  java
  • 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

    不同类型对应的属性:

    既然苹果是采用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

  • 相关阅读:
    BZOJ_2002_[Hnoi2010]Bounce 弹飞绵羊_LCT
    BZOJ_4154_[Ipsc2015]Generating Synergy_KDTree
    BZOJ_2801_[Poi2012]Minimalist Security_dfs树+特判+乱搞
    BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并
    2019集训队作业做题实况[1](1-30):
    牛客挑战赛33 F 淳平的形态形成场(无向图计数,EGF,多项式求逆)
    【NOIP2019模拟2019.10.07】果实摘取 (约瑟夫环、Mobius反演、类欧、Stern-Brocot Tree)
    CodeChef Max-digit Tree(动态规划)
    骚操作:c++如何用goto便捷地写人工栈?
    Comet OJ
  • 原文地址:https://www.cnblogs.com/chebaodaren/p/9523555.html
Copyright © 2011-2022 走看看