博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用钥匙串,在应用里保存用户密码的方法
阅读量:6615 次
发布时间:2019-06-25

本文共 14832 字,大约阅读时间需要 49 分钟。

//
//
  SFHFKeychainUtils.h
//
//
  Created by Buzz Andersen on 10/20/08.
//
  Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
//
  Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
//
//
  Permission is hereby granted, free of charge, to any person
//
  obtaining a copy of this software and associated documentation
//
  files (the "Software"), to deal in the Software without
//
  restriction, including without limitation the rights to use,
//
  copy, modify, merge, publish, distribute, sublicense, and/or sell
//
  copies of the Software, and to permit persons to whom the
//
  Software is furnished to do so, subject to the following
//
  conditions:
//
//
  The above copyright notice and this permission notice shall be
//
  included in all copies or substantial portions of the Software.
//
//
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//
  OTHER DEALINGS IN THE SOFTWARE.
//
#import <UIKit/UIKit.h>
@interface SFHFKeychainUtils : NSObject {
  
}
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@end

 

 

//
//
  SFHFKeychainUtils.m
//
//
  Created by Buzz Andersen on 10/20/08.
//
  Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
//
  Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
//
//
  Permission is hereby granted, free of charge, to any person
//
  obtaining a copy of this software and associated documentation
//
  files (the "Software"), to deal in the Software without
//
  restriction, including without limitation the rights to use,
//
  copy, modify, merge, publish, distribute, sublicense, and/or sell
//
  copies of the Software, and to permit persons to whom the
//
  Software is furnished to do so, subject to the following
//
  conditions:
//
//
  The above copyright notice and this permission notice shall be
//
  included in all copies or substantial portions of the Software.
//
//
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//
  OTHER DEALINGS IN THE SOFTWARE.
//
#import 
"
SFHFKeychainUtils.h
"
#import <Security/Security.h>
static NSString *SFHFKeychainUtilsErrorDomain = 
@"
SFHFKeychainUtilsErrorDomain
";
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
@interface SFHFKeychainUtils (PrivateMethods)
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@end
#endif
@implementation SFHFKeychainUtils
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
    
if (!username || !serviceName) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
2000 userInfo: nil];
        
return nil;
    }
    
    SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
    
    
if (*error || !item) {
        
return nil;
    }
    
    
//
 from Advanced Mac OS X Programming, ch. 16
  UInt32 length;
  
char *password;
  SecKeychainAttribute attributes[
8];
  SecKeychainAttributeList list;
    
  attributes[
0].tag = kSecAccountItemAttr;
  attributes[
1].tag = kSecDescriptionItemAttr;
  attributes[
2].tag = kSecLabelItemAttr;
  attributes[
3].tag = kSecModDateItemAttr;
  
  list.count = 
4;
  list.attr = attributes;
  
  OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (
void **)&password);
    
    
if (status != noErr) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
        
return nil;
  }
  
    NSString *passwordString = nil;
    
    
if (password != NULL) {
        
char passwordBuffer[
1024];
        
        
if (length > 
1023) {
            length = 
1023;
        }
        strncpy(passwordBuffer, password, length);
        
        passwordBuffer[length] = 
'
\0
';
        passwordString = [NSString stringWithCString:passwordBuffer];
    }
    
    SecKeychainItemFreeContent(&list, password);
  
  CFRelease(item);
  
  
return passwordString;
}
+ (
void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {    
    
if (!username || !password || !serviceName) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
2000 userInfo: nil];
        
return;
    }
    
    OSStatus status = noErr;
    
    SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
    
    
if (*error && [*error code] != noErr) {
        
return;
    }
    
    *error = nil;
    
    
if (item) {
        status = SecKeychainItemModifyAttributesAndData(item,
                                                    NULL,
                                                    strlen([password UTF8String]),
                                                    [password UTF8String]);
        
        CFRelease(item);
    }
    
else {
        status = SecKeychainAddGenericPassword(NULL,                                     
                                           strlen([serviceName UTF8String]), 
                                           [serviceName UTF8String],
                                           strlen([username UTF8String]),                        
                                           [username UTF8String],
                                           strlen([password UTF8String]),
                                           [password UTF8String],
                                           NULL);
    }
    
    
if (status != noErr) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
    }
}
+ (
void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
    
if (!username || !serviceName) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 
2000 userInfo: nil];
        
return;
    }
    
    *error = nil;
    
    SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
    
    
if (*error && [*error code] != noErr) {
        
return;
    }
    
    OSStatus status;
    
    
if (item) {
        status = SecKeychainItemDelete(item);
        
        CFRelease(item);
    }
    
    
if (status != noErr) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
    }
}
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
    
if (!username || !serviceName) {
        *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
2000 userInfo: nil];
        
return nil;
    }
    
    *error = nil;
  
    SecKeychainItemRef item;
    
    OSStatus status = SecKeychainFindGenericPassword(NULL,
                                                   strlen([serviceName UTF8String]),
                                                   [serviceName UTF8String],
                                                   strlen([username UTF8String]),
                                                   [username UTF8String],
                                                   NULL,
                                                   NULL,
                                                   &item);
    
    
if (status != noErr) {
        
if (status != errSecItemNotFound) {
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
        }
        
        
return nil;        
    }
    
    
return item;
}
#else
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
    
if (!username || !serviceName) {
        
if (error != nil) {
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
2000 userInfo: nil];
        }
        
return nil;
    }
    
    
if (error != nil) {
        *error = nil;
    }
  
    
//
 Set up a query dictionary with the base query attributes: item type (generic), username, and service
    
    NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil] autorelease];
    NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil] autorelease];
    
    NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
    
    
//
 First do a query for attributes, in case we already have a Keychain item with no password data set.
    
//
 One likely way such an incorrect item could have come about is due to the previous (incorrect)
    
//
 version of this code (which set the password as a generic attribute instead of password data).
    
    NSDictionary *attributeResult = NULL;
    NSMutableDictionary *attributeQuery = [query mutableCopy];
    [attributeQuery setObject: (
id) kCFBooleanTrue forKey:(
id) kSecReturnAttributes];
    OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult);
    
    [attributeResult release];
    [attributeQuery release];
    
    
if (status != noErr) {
        
//
 No existing item found--simply return nil for the password
        
if (error != nil && status != errSecItemNotFound) {
            
//
Only return an error if a real exception happened--not simply for "not found."
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
        }
        
        
return nil;
    }
    
    
//
 We have an existing item, now query for the password data associated with it.
    
    NSData *resultData = nil;
    NSMutableDictionary *passwordQuery = [query mutableCopy];
    [passwordQuery setObject: (
id) kCFBooleanTrue forKey: (
id) kSecReturnData];
  
    status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData);
    
    [resultData autorelease];
    [passwordQuery release];
    
    
if (status != noErr) {
        
if (status == errSecItemNotFound) {
            
//
 We found attributes for the item previously, but no password now, so return a special error.
            
//
 Users of this API will probably want to detect this error and prompt the user to
            
//
 re-enter their credentials.  When you attempt to store the re-entered credentials
            
//
 using storeUsername:andPassword:forServiceName:updateExisting:error
            
//
 the old, incorrect entry will be deleted and a new one with a properly encrypted
            
//
 password will be added.
            
if (error != nil) {
                *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
1999 userInfo: nil];
            }
        }
        
else {
            
//
 Something else went wrong. Simply return the normal Keychain API error code.
            
if (error != nil) {
                *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
            }
        }
        
        
return nil;
    }
  
    NSString *password = nil;    
  
    
if (resultData) {
        password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
    }
    
else {
        
//
 There is an existing item, but we weren't able to get password data for it for some reason,
        
//
 Possibly as a result of an item being incorrectly entered by the previous code.
        
//
 Set the -1999 error so the code above us can prompt the user again.
        
if (error != nil) {
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
1999 userInfo: nil];
        }
    }
  
    
return [password autorelease];
}
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error 
{        
    
if (!username || !password || !serviceName) 
  {
        
if (error != nil) 
    {
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
2000 userInfo: nil];
        }
        
return NO;
    }
    
    
//
 See if we already have a password entered for these credentials.
    NSError *getError = nil;
    NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];
  
    
if ([getError code] == -
1999
  {
        
//
 There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
        
//
 Delete the existing item before moving on entering a correct one.
    
        getError = nil;
        
        [self deleteItemForUsername: username andServiceName: serviceName error: &getError];
    
        
if ([getError code] != noErr) 
    {
            
if (error != nil) 
      {
                *error = getError;
            }
            
return NO;
        }
    }
    
else 
if ([getError code] != noErr) 
  {
        
if (error != nil) 
    {
            *error = getError;
        }
        
return NO;
    }
    
    
if (error != nil) 
  {
        *error = nil;
    }
    
    OSStatus status = noErr;
  
    
if (existingPassword) 
  {
        
//
 We have an existing, properly entered item with a password.
        
//
 Update the existing item.
        
        
if (![existingPassword isEqualToString:password] && updateExisting) 
    {
            
//
Only update if we're allowed to update existing.  If not, simply do nothing.
            
            NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, 
                        kSecAttrService, 
                        kSecAttrLabel, 
                        kSecAttrAccount, 
                        nil] autorelease];
            
            NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, 
                           serviceName,
                           serviceName,
                           username,
                           nil] autorelease];
            
            NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];            
            
            status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *) kSecValueData]);
        }
    }
    
else 
  {
        
//
 No existing entry (or an existing, improperly entered, and therefore now
        
//
 deleted, entry).  Create a new entry.
        
        NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, 
                      kSecAttrService, 
                      kSecAttrLabel, 
                      kSecAttrAccount, 
                      kSecValueData, 
                      nil] autorelease];
        
        NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, 
                         serviceName,
                         serviceName,
                         username,
                         [password dataUsingEncoding: NSUTF8StringEncoding],
                         nil] autorelease];
        
        NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];            
    
        status = SecItemAdd((CFDictionaryRef) query, NULL);
    }
    
    
if (status != noErr) 
  {
        
//
 Something went wrong with adding the new item. Return the Keychain error code.
        
if (error != nil) {
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
        }
    
    
return NO;
    }
  
  
return YES;
}
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error 
{
    
if (!username || !serviceName) 
  {
        
if (error != nil) 
    {
            *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -
2000 userInfo: nil];
        }
        
return NO;
    }
    
    
if (error != nil) 
  {
        *error = nil;
    }
  
    NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil] autorelease];
    NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil] autorelease];
    
    NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
    
    OSStatus status = SecItemDelete((CFDictionaryRef) query);
    
    
if (status != noErr) 
  {
      
if (error != nil) {
          *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
      }
    
    
return NO;
    }
  
  
return YES;
}
#endif
@end

 

转载于:https://www.cnblogs.com/hxwj/p/4555661.html

你可能感兴趣的文章
linux 内核配置 fatfs,微软发布exFAT文件系统规范:推动嵌入Linux内核 支持开箱即用...
查看>>
linux程序中文乱码转换,Linux下汉字编码的转换(gbk转换为utf8)
查看>>
linux tty终端 录屏,在Linux上录制终端的操作
查看>>
linux i3 桌面,Linux 桌面平铺管理器 - i3wm
查看>>
linux文件内容转置,给定一个文件 file.txt,转置它的内容。你可以假设每行列数相同,并且每个字段由 ' ' 分隔....
查看>>
linux下mysql一般安装在哪里,MySQL在Linux下的安装
查看>>
linux malloc 多线程,c-malloc如何在多线程环境中工作?
查看>>
linux系统串口使用,linux使用串口说明
查看>>
linux实验串口网络通信编程仪,Linux串口通信编程
查看>>
linux下怎么绑定arp,LINUX 下进行arp 绑定.doc
查看>>
sys_brk分析 linux1.2.0版本,linux内存管理之sys_brk实现分析(续)
查看>>
c语言14年春机考答案,计算机二级《C语言》机考试题与答案
查看>>
c语言括号匹配问题不用栈,括号匹配问题(不用栈,用数组)
查看>>
c语言指针课堂教学设计,“C语言程序设计”课程指针的教学设计
查看>>
gradle是否可以编译c语言,使用CPP插件在gradle中编译C++代码
查看>>
c语言程实训报告,C语言程设计工程实训报告.doc
查看>>
C语言高斯消元课程设计报告,c高斯消元法解方程-课程设计报告.doc
查看>>
三坐标DMIS语言是C语言吗,三坐标测量软件PC-DMIS常见技巧
查看>>
空间域图像增强c语言,OpenCV 图像增强(空间域)
查看>>
android 窗口分割线,android RecyclerView 万能分隔线
查看>>