我正在努力使用iOS钥匙串,似乎找不到任何好的文档。
无论如何,我有两个应用程序,基本上我想做的是在钥匙串中共享一些数据并保持一些数据的私密性,以便其他应用程序无法访问它。
我试图实现Apple提供的KeychainItemWrapper,但这根本不起作用。我在共享数据时没有问题,但如果我未设置访问权限组,数据仍会共享。我使用的设备而不是模拟器,这可能会导致同样的问题。
这是我的代码
应用 1 :
KeychainItemWrapper *item = [[KeychainItemWrapper alloc] initWithIdentifier:@"SharedKeyChainApp" accessGroup:nil];
[item setObject:@"MyAccount" forKey:(__bridge id)kSecAttrAccount];
[item setObject:@"SecureValue" forKey:(__bridge id)kSecValueData];
应用 2 :
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"SharedKeyChainApp" accessGroup:nil];
NSString *data = [keychain objectForKey:(__bridge id)kSecValueData];
NSLog(@"data is : %@",data); //Prints "data is : SecureValue"
如果我在一个或另一个应用程序中删除项目属性中的钥匙串组,它不会打印任何内容。但显然我无法再在这两个应用程序之间共享数据。
谢谢
如果是共享钥匙串,则它是共享的。任何其他可以访问钥匙串的应用程序都可以访问其中的所有数据。
您可以:
- 创建 2 个钥匙串。一个共享,一个私有。可共享的东西是共享的,私人的东西是私有的。
- 加密您不想与他人共享的数据。
我可能会选择第一个。恕我直言,KeychainItemWrapper
作为抓取和使用代码非常糟糕。这是旧代码,几乎没有提供功能。我正在附加一段我编写的快速而脏的代码,以便在没有KeychainItemWrapper
的情况下使用钥匙链功能进行播放和测试。在这种情况下,我同时使用"应用程序"和"安全"中的项目来创建一些共享和非共享项目。你在这里真的看不出来,因为它只是一些测试代码,共享是在目标->功能->钥匙串共享下。
- (void)viewDidLoad {
[super viewDidLoad];
// [self removeKeychainItem];
// [self addKeychainItem];
[self searchForKeychainItems];
}
- (void)searchForKeychainItems {
[self log:@"nn EXISTING KEYCHAIN ITEM(S)"];
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue, // returns password
(__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue, // returns rest of data
// (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.Security"
// (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.app"
};
OSStatus resultCode;
CFArrayRef *searchResults = nil;
resultCode = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&searchResults);
NSArray *foo = CFBridgingRelease(searchResults);
[self log:[NSString stringWithFormat:@"Search result code: %d", (int)resultCode]];
[self log:[NSString stringWithFormat:@"Search Results: %@", foo]];
NSDictionary *keychainItem = foo[0];
NSString *password = [[NSString alloc] initWithData:[keychainItem objectForKey:(__bridge id)kSecValueData] encoding:NSUTF8StringEncoding];
[self log:[NSString stringWithFormat:@"password is `%@`", password]];
}
- (void)addKeychainItem {
[self log:@"nn ADDING KEYCHAIN ITEM"];
NSDictionary *genericDataDictionary = @{@"authState": @"1",
@"lastAuthDate": @"2/11/2014",
@"otherCrap": @"poo"};
NSData *encodedGenericData = [NSKeyedArchiver archivedDataWithRootObject:genericDataDictionary];
NSData *encodedPassword = [@"secret" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrCreator: @"MyCom",
(__bridge id)kSecAttrComment: @"keychain tests",
(__bridge id)kSecAttrService: @"Credentials",
(__bridge id)kSecAttrAccount: @"username",
(__bridge id)kSecValueData: encodedPassword,
(__bridge id)kSecAttrGeneric: encodedGenericData,
(__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.Security"
};
OSStatus result;
result = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
NSLog(@"Add status code: %d", (int)result);
}
- (void)removeKeychainItem {
[self log:@"nn REMOVING KEYCHAIN ITEM"];
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
// (__bridge id)kSecAttrCreator: @"MyCom",
// (__bridge id)kSecAttrService: @"Credentials",
(__bridge id)kSecAttrComment: @"New Keychain standards Test Item",
// (__bridge id)kSecAttrAccount: @"username",
// (__bridge id)kSecValueData: [@"password" dataUsingEncoding:NSUTF8StringEncoding],
// (__bridge id)kSecAttrGeneric: encodedGenericData
// (__bridge id)kSecAttrAccessGroup: @"AAAAAAAAAA.com.foo.Security"
};
OSStatus resultsCode;
resultsCode = SecItemDelete((__bridge CFDictionaryRef)query);
NSLog(@"Delete results code: %d", (int)resultsCode);
}
- (void)log:(NSString *)text {
self.textView.text = [[self.textView.text stringByAppendingString:text] stringByAppendingString:@"n"];
}
需要注意的一件事是,需要同时添加专用和共享访问组。专用访问组应与应用的捆绑 ID 匹配,并位于共享的捆绑 ID 之前。
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)<APP BUNDLE ID></string>
<string>$(AppIdentifierPrefix)com.mybusiness.shared</string>
</array>
</dict>
如果您只添加单个命名访问组,那么这将是所有钥匙串项目的默认选项,可以有效地共享所有内容。