खोज…
वाक्य - विन्यास
- kSecClassGenericPassword // एक गैर-इंटरनेट पासवर्ड का प्रतिनिधित्व करने वाली एक मूल्य कुंजी
- kSecClassInternetPassword // एक इंटरनेट पासवर्ड का प्रतिनिधित्व करने वाली एक मूल्य कुंजी
- kSecClassCert सर्टिफिकेट // सर्टिफिकेट का प्रतिनिधित्व करने वाली एक मूल्य कुंजी
- kSecClassCert सर्टिफिकेट // एक कुंजी का प्रतिनिधित्व एक कुंजी
- kSecClassIdentity // एक पहचान का प्रतिनिधित्व करने वाली एक मूल्य कुंजी, जो एक प्रमाणपत्र प्लस एक कुंजी है
टिप्पणियों
iOS एक सुरक्षित भंडारण क्षेत्र में पासवर्ड, एन्क्रिप्शन कुंजी, प्रमाणपत्र और पहचान जैसी निजी जानकारी संग्रहीत करता है, जिसे किचेन कहा जाता है। यह भंडारण क्षेत्र पूरी तरह से सिक्योर एन्क्लेव नामक सह-प्रोसेसर द्वारा प्रबंधित किया जाता है, जो एप्लिकेशन प्रोसेसर के अंदर एम्बेडेड होता है। क्योंकि किचेन iOS पर सैंडबॉक्स किया गया है, किचेन आइटम केवल उस एप्लिकेशन द्वारा प्राप्त किए जा सकते हैं जो उन्हें पहले स्थान पर रखते हैं।
कुछ मामलों में आपको त्रुटियों से बचने के लिए Xcode क्षमताओं में किचेन शेयरिंग को चालू करना होगा।
किचेन के साथ बातचीत करने के लिए, हम किचेन सर्विसेज नामक एसी फ्रेमवर्क का उपयोग करते हैं। अधिक जानकारी के लिए, Apple की किचेन सर्विसेज प्रोग्रामिंग गाइड देखें ।
क्योंकि किचेन सर्विसेज Foundation
स्तर से नीचे है, इसलिए यह CoreFoundation
प्रकारों का उपयोग करने के लिए प्रतिबंधित है। नतीजतन, अधिकांश वस्तुओं को आंतरिक रूप से CFDictionary
रूप में प्रस्तुत किया जाता है, जो CFString
कुंजी के रूप में और उनके मूल्यों के रूप में कई प्रकार के CoreFoundation
प्रकार के होते हैं।
जबकि किचेन सर्विसेज को Security
ढांचे के एक भाग के रूप में शामिल किया गया है, Foundation
का आयात आमतौर पर एक अच्छा विकल्प है क्योंकि इसमें बैकएंड में कुछ सहायक कार्य शामिल हैं।
इसके अतिरिक्त, यदि आप सीधे किचेन सेवाओं से निपटना नहीं चाहते हैं, तो Apple जेनेरिक किचेन स्विफ्ट नमूना परियोजना प्रदान करता है जो स्विफ्ट प्रकार प्रदान करता है जो पर्दे के पीछे किचेन सेवाओं का उपयोग करता है।
किचेन में एक पासवर्ड जोड़ना
हर किचेन आइटम को अक्सर CFDictionary
रूप में CFDictionary
। हालांकि, आप बस का उपयोग कर सकते NSDictionary
ऑब्जेक्टिव-सी में और ब्रिजिंग का लाभ लेने के लिए, या स्विफ्ट में आप उपयोग कर सकते Dictionary
और स्पष्ट रूप से करने के लिए डाली CFDictionary
।
आप निम्नलिखित शब्दकोश के साथ एक पासवर्ड का निर्माण कर सकते हैं:
तीव्र
var dict = [String : AnyObject]()
सबसे पहले, आपको एक कुंजी / मान जोड़ी की आवश्यकता है जो कि किचेन को बताए कि यह एक पासवर्ड है। ध्यान दें कि क्योंकि हमारे dict कुंजी एक है String
हम किसी भी कास्ट करना होगा CFString
एक करने के लिए String
क्योंकि यह Hashable नहीं है स्पष्ट रूप से स्विफ्ट 3. CFString में एक स्विफ्ट शब्दकोश की कुंजी के रूप में इस्तेमाल नहीं किया जा सकता।
तीव्र
dict[kSecClass as String] = kSecClassGenericPassword
अगला, हमारे पासवर्ड में इसे वर्णित करने और बाद में इसे खोजने में मदद करने के लिए विशेषताओं की एक श्रृंखला हो सकती है। यहां जेनेरिक पासवर्ड के लिए विशेषताओं की एक सूची दी गई है ।
तीव्र
// The password will only be accessible when the device is unlocked
dict[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlocked
// Label may help you find it later
dict[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
// Username
dict[kSecAttrAccount as String] = "My Name" as CFString
// Service name
dict[kSecAttrService as String] = "MyService" as CFString
अंत में, हमें अपने वास्तविक निजी डेटा की आवश्यकता है। याद रखें कि इसे लंबे समय तक स्मृति में न रखें। यह CFData
होना चाहिए।
तीव्र
dict[kSecValueData as String] = "my_password!!".data(using: .utf8) as! CFData
अंत में, किचेन सर्विसेज ऐड फंक्शन यह जानना चाहता है कि नवनिर्मित किचेन आइटम को कैसे वापस करना चाहिए। चूँकि आपको डेटा को मेमोरी में बहुत लंबे समय तक नहीं रखना चाहिए, यहाँ बताया गया है कि आप केवल विशेषताओं को कैसे लौटा सकते हैं:
तीव्र
dict[kSecReturnAttributes as String] = kCFBooleanTrue
अब हमने अपनी वस्तु का निर्माण कर लिया है। आइए इसे जोड़ते हैं:
तीव्र
var result: AnyObject?
let status = withUnsafeMutablePointer(to: &result) {
SecItemAdd(dict as CFDictionary, UnsafeMutablePointer($0))
}
let newAttributes = result as! Dictionary<String, AnyObject>
यह नई विशेषताओं को result
अंदर निर्धारित करता है। SecItemAdd
हमारे द्वारा बनाए गए शब्दकोश में, साथ ही साथ एक पॉइंटर में ले जाता है जहां हम अपना परिणाम पसंद करेंगे। फ़ंक्शन तब सफलता या एक त्रुटि कोड का संकेत देता है एक OSStatus
देता है। परिणाम कोड यहाँ वर्णित हैं ।
किचेन में पासवर्ड ढूंढना
एक क्वेरी का निर्माण करने के लिए, हमें इसे CFDictionary
रूप में CFDictionary
। तुम भी उपयोग कर सकते हैं NSDictionary
ऑब्जेक्टिव-सी या में Dictionary
स्विफ्ट में और करने के लिए डाली CFDictionary
।
हमें एक वर्ग कुंजी की आवश्यकता है:
तीव्र
var dict = [String : AnyObject]()
dict[kSecClass as String] = kSecClassGenericPassword
आगे, हम अपनी खोज को कम करने के लिए विशेषताएँ निर्दिष्ट कर सकते हैं:
तीव्र
// Label
dict[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
// Username
dict[kSecAttrAccount as String] = "My Name" as CFString
// Service name
dict[kSecAttrService as String] = "MyService" as CFString
हम यहां वर्णित विशेष खोज संशोधक कुंजियों को भी निर्दिष्ट कर सकते हैं ।
अंत में, हमें यह कहने की आवश्यकता है कि हम अपना डेटा कैसे लौटाएंगे। नीचे, हम निवेदन करेंगे कि केवल निजी पासवर्ड को ही CFData
ऑब्जेक्ट के रूप में CFData
:
तीव्र
dict[kSecReturnData as String] = kCFBooleanTrue
अब, आइए खोजें:
तीव्र
var queryResult: AnyObject?
let status = withUnsafeMutablePointer(to: &queryResult) {
SecItemCopyMatching(dict as CFDictionary, UnsafeMutablePointer($0))
}
// Don't keep this in memory for long!!
let password = String(data: queryResult as! Data, encoding: .utf8)!
यहाँ, SecItemCopyMatching
एक क्वेरी डिक्शनरी और एक पॉइंटर में ले जाता है जहाँ आप जाना चाहते हैं। यह परिणाम कोड के साथ एक OSStatus
देता है। यहां संभावनाएं हैं।
किचेन में पासवर्ड अपडेट करना
हमेशा की तरह, हमें पहले उस आइटम का प्रतिनिधित्व करने के लिए एक CFDictionary
आवश्यकता है CFDictionary
हम अपडेट करना चाहते हैं। इसमें आइटम के सभी पुराने मूल्य शामिल होने चाहिए, जिसमें पुराने निजी डेटा भी शामिल हैं। फिर यह किसी भी विशेषता या डेटा को बदलने के लिए जो आप बदलना चाहते हैं, का एक CFDictionary
लेता है।
तो पहले, चलो एक वर्ग कुंजी और विशेषताओं की एक सूची का निर्माण करते हैं। ये विशेषताएँ हमारी खोज को कम कर सकती हैं, लेकिन आपको कोई भी विशेषताएँ और पुराने मान शामिल करने होंगे यदि आप उन्हें बदल रहे हैं।
तीव्र
var dict = [String : AnyObject]()
dict[kSecClass as String] = kSecClassGenericPassword
// Label
dict[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
// Username
dict[kSecAttrAccount as String] = "My Name" as CFString
अब हमें पुराना डेटा जोड़ना होगा:
तीव्र
dict[kSecValueData as String] = "my_password!!".data(using: .utf8) as! CFData
चलिए अब उसी गुण को बनाते हैं लेकिन एक अलग पासवर्ड:
तीव्र
var newDict = [String : AnyObject]()
newDict[kSecClass as String] = kSecClassGenericPassword
// Label
newDict[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
// Username
newDict[kSecAttrAccount as String] = "My Name" as CFString
// New password
newDict[kSecValueData as String] = "new_password!!".data(using: .utf8) as! CFData
अब, हम इसे किचेन सर्विसेज में भेजते हैं:
तीव्र
let status = SecItemUpdate(dict as CFDictionary, newDict as CFDictionary)
SecItemUpdate
एक स्थिति कोड देता है। परिणाम यहाँ वर्णित हैं ।
किचेन से पासवर्ड हटाना
किचेन से किसी आइटम को हटाने के लिए हमें केवल एक चीज की आवश्यकता होती है: एक CFDictionary
विशेषताओं के साथ जो आइटम को हटाने का वर्णन करता है। क्वेरी डिक्शनरी से मेल खाने वाली कोई भी वस्तु स्थायी रूप से हटा दी जाएगी, इसलिए यदि आप केवल एक आइटम को हटाना चाहते हैं तो सुनिश्चित करें कि आपकी क्वेरी के लिए विशिष्ट हो। हमेशा की तरह, हम एक का उपयोग कर सकते NSDictionary
ऑब्जेक्टिव-सी में या स्विफ्ट में हम एक का उपयोग कर सकते Dictionary
और उसके बाद के लिए डाली CFDictionary
।
एक क्वेरी शब्दकोश, इस संदर्भ में विशेष रूप से यह बताने के लिए एक वर्ग कुंजी शामिल है कि आइटम क्या है और आइटम के बारे में जानकारी का वर्णन करने के लिए विशेषताएँ। खोज प्रतिबंध जैसे kSecMatchCaseInsensitive
शामिल होने की अनुमति नहीं है।
तीव्र
var dict = [String : AnyObject]()
dict[kSecClass as String] = kSecClassGenericPassword
// Label
dict[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
// Username
dict[kSecAttrAccount as String] = "My Name" as CFString
और अब हम इसे हटा सकते हैं:
तीव्र
let status = SecItemDelete(dict as CFDictionary)
SecItemDelete
एक रिटर्न OSStatus
। परिणाम कोड यहाँ वर्णित हैं ।
किचेन ऐड, अपडेट, निकालें और एक फाइल का उपयोग करके ऑपरेशन खोजें।
Keychain.h
#import <Foundation/Foundation.h>
typedef void (^KeychainOperationBlock)(BOOL successfulOperation, NSData *data, OSStatus status);
@interface Keychain : NSObject
-(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_;
-(void)insertKey:(NSString *)key withData:(NSData *)data withCompletion:(KeychainOperationBlock)completionBlock;
-(void)updateKey:(NSString*)key withData:(NSData*) data withCompletion:(KeychainOperationBlock)completionBlock;
-(void)removeDataForKey:(NSString*)key withCompletionBlock:(KeychainOperationBlock)completionBlock;
-(void)findDataForKey:(NSString*)key withCompletionBlock:(KeychainOperationBlock)completionBlock;
@end
Keychain.m
#import "Keychain.h"
#import <Security/Security.h>
@implementation Keychain
{
NSString * keychainService;
NSString * keychainGroup;
}
-(id) initWithService:(NSString *)service withGroup:(NSString*)group
{
self =[super init];
if(self) {
keychainService = [NSString stringWithString:service];
if(group) {
keychainGroup = [NSString stringWithString:group];
}
}
return self;
}
-(void)insertKey:(NSString *)key
withData:(NSData *)data
withCompletion:(KeychainOperationBlock)completionBlock
{
NSMutableDictionary * dict =[self prepareDict:key];
[dict setObject:data forKey:(__bridge id)kSecValueData];
[dict setObject:keychainService forKey:(id)kSecAttrService];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
if(errSecSuccess != status) {
DLog(@"Unable add item with key =%@ error:%d",key,(int)status);
if (completionBlock) {
completionBlock(errSecSuccess == status, nil, status);
}
}
if (status == errSecDuplicateItem) {
[self updateKey:key withData:data withCompletion:^(BOOL successfulOperation, NSData *updateData, OSStatus updateStatus) {
if (completionBlock) {
completionBlock(successfulOperation, updateData, updateStatus);
}
DLog(@"Found duplication item -- updating key with data");
}];
}
}
-(void)findDataForKey:(NSString *)key
withCompletionBlock:(KeychainOperationBlock)completionBlock
{
NSMutableDictionary *dict = [self prepareDict:key];
[dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[dict setObject:keychainService forKey:(id)kSecAttrService];
[dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);
if( status != errSecSuccess) {
DLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
if (completionBlock) {
completionBlock(errSecSuccess == status, nil, status);
}
} else {
if (completionBlock) {
completionBlock(errSecSuccess == status, (__bridge NSData *)result, status);
}
}
}
-(void)updateKey:(NSString *)key
withData:(NSData *)data
withCompletion:(KeychainOperationBlock)completionBlock
{
NSMutableDictionary * dictKey =[self prepareDict:key];
NSMutableDictionary * dictUpdate =[[NSMutableDictionary alloc] init];
[dictUpdate setObject:data forKey:(__bridge id)kSecValueData];
[dictUpdate setObject:keychainService forKey:(id)kSecAttrService];
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)dictKey, (__bridge CFDictionaryRef)dictUpdate);
if( status != errSecSuccess) {
DLog(@"Unable to remove item for key %@ with error:%d",key,(int)status);
}
if (completionBlock) {
completionBlock(errSecSuccess == status, nil, status);
}
}
-(void)removeDataForKey:(NSString *)key
withCompletionBlock:(KeychainOperationBlock)completionBlock {
NSMutableDictionary *dict = [self prepareDict:key];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict);
if( status != errSecSuccess) {
DLog(@"Unable to remove item for key %@ with error:%d",key,(int)status);
}
if (completionBlock) {
completionBlock(errSecSuccess == status, nil, status);
}
}
#pragma mark Internal methods
-(NSMutableDictionary*) prepareDict:(NSString *) key {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:keychainService forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
//This is for sharing data across apps
if(keychainGroup != nil) {
[dict setObject:keychainGroup forKey:(__bridge id)kSecAttrAccessGroup];
}
return dict;
}
@end
किचेन एक्सेस कंट्रोल (पासवर्ड कमबैक वाला टचआईडी)
किचेन विशेष SecAccessControl विशेषता के साथ आइटम को सहेजने की अनुमति देता है जो कि किचेन से आइटम प्राप्त करने की अनुमति देगा केवल उपयोगकर्ता को टच आईडी (या इस तरह के फ़ॉलबैक की अनुमति होने पर पासकोड) के साथ प्रमाणित किया जाएगा। एप्लिकेशन को केवल यह सूचित किया जाता है कि प्रमाणीकरण सफल था या नहीं, पूरे UI को iOS द्वारा प्रबंधित किया गया है।
सबसे पहले, SecAccessControl ऑब्जेक्ट बनाया जाना चाहिए:
तीव्र
let error: Unmanaged<CFError>?
guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .userPresence, &error) else {
fatalError("Something went wrong")
}
इसके बाद, इसे kSecAttrAccessControl कुंजी के साथ शब्दकोश में जोड़ें (जो अन्य उदाहरणों में आपके द्वारा उपयोग किए जा रहे kSecAttrAccessible कुंजी के साथ पारस्परिक रूप से अनन्य है):
तीव्र
var dictionary = [String : Any]()
dictionary[kSecClass as String] = kSecClassGenericPassword
dictionary[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
dictionary[kSecAttrAccount as String] = "My Name" as CFString
dictionary[kSecValueData as String] = "new_password!!".data(using: .utf8) as! CFData
dictionary[kSecAttrAccessControl as String] = accessControl
और इसे पहले की तरह सहेज कर रखें:
तीव्र
let lastResultCode = SecItemAdd(query as CFDictionary, nil)
संग्रहीत डेटा तक पहुंचने के लिए, बस एक चाबी के लिए किचेन को क्वेरी करें। किचेन सर्विसेज उपयोगकर्ता के लिए प्रमाणीकरण संवाद प्रस्तुत करेंगी और उपयुक्त फिंगरप्रिंट प्रदान किए गए या पासकोड के मिलान के आधार पर डेटा या एनआईएल लौटाएंगी।
वैकल्पिक रूप से, शीघ्र स्ट्रिंग निर्दिष्ट किया जा सकता है:
तीव्र
var query = [String: Any]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecReturnData as String] = kCFBooleanTrue
query[kSecAttrAccount as String] = "My Name" as CFString
query[kSecAttrLabel as String] = "com.me.myapp.myaccountpassword" as CFString
query[kSecUseOperationPrompt as String] = "Please put your fingers on that button" as CFString
var queryResult: AnyObject?
let status = withUnsafeMutablePointer(to: &queryResult) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}
ध्यान दें कि उपयोगकर्ता द्वारा अस्वीकृत, रद्द या विफल होने पर status
err
होगी।
तीव्र
if status == noErr {
let password = String(data: queryResult as! Data, encoding: .utf8)!
print("Password: \(password)")
} else {
print("Authorization not passed")
}