• 利用钥匙串,在应用里保存用户密码的方法


    https://github.com/ldandersen/scifihifi-iphone/tree/master/security

    //
    //  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] = '';
            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

  • 相关阅读:
    paper 113:Bhattacharyya distance
    (ZT)算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)
    (ZT)算法杂货铺——分类算法之贝叶斯网络(Bayesian networks)
    (ZT)算法杂货铺——分类算法之决策树(Decision tree)
    (ZT)算法杂货铺——k均值聚类(K-means)
    超详细的遗传算法(Genetic Algorithm)解析
    Ontology理论研究和应用建模
    观察者模式(Observer)和发布(Publish/订阅模式(Subscribe)的区别
    程序员常用字体(vs2008字体修改方案)
    雾计算和边缘计算的区别
  • 原文地址:https://www.cnblogs.com/hxwj/p/4555661.html
Copyright © 2020-2023  润新知