共享密钥链中的私有数据

时间:2014-02-24 13:45:47

标签: ios keychain

我正在努力使用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];

App 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 个答案:

答案 0 :(得分:1)

如果它是共享钥匙串,那么它就是共享的。其中任何可以访问钥匙串的应用程序都可以访问其中的所有数据。

你可以:

  • 创建2个钥匙串。一个共享和一个私人。共享的私有东西是私有的。
  • 加密您不想与他人分享的数据。

我可能会选择第一个。恕我直言,KeychainItemWrapper作为抓取和使用代码非常糟糕。它的旧代码几乎没有提供功能。我附上了一个快速而肮脏的代码,我编写了这些代码并使用不带KeychainItemWrapper的密钥链功能进行测试。在这种情况下,我正在玩“app”和“Security”中的项目来创建一些共享和非共享项目。你不能在这里真正说出来,因为它只是一些测试代码和共享是在Targets-> Capabilities-> Keychain Sharing。

- (void)viewDidLoad {
    [super viewDidLoad];

//    [self removeKeychainItem];
//    [self addKeychainItem];
    [self searchForKeychainItems];
}

- (void)searchForKeychainItems {
    [self log:@"\n\n  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:@"\n\n  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:@"\n\n  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"];
}

答案 1 :(得分:0)

要注意的一件事是,您需要同时添加一个私有访问组和一个共享访问组。私有访问组应与应用程序的捆绑软件ID匹配,并位于共享的ID之前。

<dict>
<key>keychain-access-groups</key>
    <array>
        <string>$(AppIdentifierPrefix)<APP BUNDLE ID></string>
        <string>$(AppIdentifierPrefix)com.mybusiness.shared</string>
    </array>
</dict>

如果您仅添加一个命名访问组,则这将是所有有效共享所有内容的所有钥匙串项目的默认设置。