KKKeychain.m 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. //
  2. // Copyright 2011-2012 Kosher Penguin LLC
  3. // Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. #import "KKKeychain.h"
  18. #import <Security/Security.h>
  19. #import "BundleUtil.h"
  20. @implementation KKKeychain
  21. + (NSString*)appName
  22. {
  23. NSBundle *bundle = [BundleUtil mainBundle];
  24. NSString *appName = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
  25. if (!appName) {
  26. appName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
  27. }
  28. return appName;
  29. }
  30. + (BOOL)setString:(NSString*)string forKey:(NSString*)key
  31. {
  32. if (string == nil || key == nil) {
  33. return NO;
  34. }
  35. key = [NSString stringWithFormat:@"%@ - %@", [KKKeychain appName], key];
  36. // First check if it already exists, by creating a search dictionary and requesting that
  37. // nothing be returned, and performing the search anyway.
  38. NSMutableDictionary *existsQueryDictionary = [NSMutableDictionary dictionary];
  39. NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
  40. [existsQueryDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
  41. // Add the keys to the search dict
  42. [existsQueryDictionary setObject:@"service" forKey:(__bridge id)kSecAttrService];
  43. [existsQueryDictionary setObject:key forKey:(__bridge id)kSecAttrAccount];
  44. // Have SecItemCopyMatching return the data even though we're not interested in it (to work around a Citrix Worx bug)
  45. CFTypeRef dataDummy = nil;
  46. [existsQueryDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
  47. OSStatus res = SecItemCopyMatching((__bridge CFDictionaryRef)existsQueryDictionary, &dataDummy);
  48. if (dataDummy)
  49. CFRelease(dataDummy);
  50. [existsQueryDictionary removeObjectForKey:(__bridge id)kSecReturnData];
  51. if (res == errSecItemNotFound) {
  52. if (string != nil) {
  53. NSMutableDictionary *addDict = existsQueryDictionary;
  54. [addDict setObject:data forKey:(__bridge id)kSecValueData];
  55. [addDict setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
  56. res = SecItemAdd((__bridge CFDictionaryRef)addDict, NULL);
  57. NSAssert1(res == errSecSuccess, @"Recieved %ld from SecItemAdd!", (long)res);
  58. }
  59. } else if (res == errSecSuccess) {
  60. // Modify an existing one
  61. // Actually pull it now of the keychain at this point.
  62. NSDictionary *attributeDict = @{
  63. (__bridge id)kSecValueData: data,
  64. (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAfterFirstUnlock
  65. };
  66. res = SecItemUpdate((__bridge CFDictionaryRef)existsQueryDictionary, (__bridge CFDictionaryRef)attributeDict);
  67. NSAssert1(res == errSecSuccess, @"SecItemUpdated returned %ld!", (long)res);
  68. } else {
  69. NSAssert1(NO, @"Received %ld from SecItemCopyMatching!", (long)res);
  70. }
  71. return YES;
  72. }
  73. + (NSString*)getStringForKey:(NSString*)key
  74. {
  75. key = [NSString stringWithFormat:@"%@ - %@", [KKKeychain appName], key];
  76. NSMutableDictionary *existsQueryDictionary = [NSMutableDictionary dictionary];
  77. [existsQueryDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
  78. // Add the keys to the search dict
  79. [existsQueryDictionary setObject:@"service" forKey:(__bridge id)kSecAttrService];
  80. [existsQueryDictionary setObject:key forKey:(__bridge id)kSecAttrAccount];
  81. // We want the data back!
  82. CFTypeRef data = nil;
  83. [existsQueryDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
  84. OSStatus res = SecItemCopyMatching((__bridge CFDictionaryRef)existsQueryDictionary, &data);
  85. if (res == errSecSuccess) {
  86. NSString *string = [[NSString alloc] initWithData:(__bridge NSData*)data encoding:NSUTF8StringEncoding];
  87. CFRelease(data);
  88. return string;
  89. } else {
  90. if (data)
  91. CFRelease(data);
  92. NSAssert1(res == errSecItemNotFound, @"SecItemCopyMatching returned %ld!", (long)res);
  93. }
  94. return nil;
  95. }
  96. + (void)upgradeAccessibilityForKey:(NSString*)key {
  97. // Check if the accessibility attributes need to be upgraded
  98. NSString *derivedKey = [NSString stringWithFormat:@"%@ - %@", [KKKeychain appName], key];
  99. NSMutableDictionary *queryDictionary = [NSMutableDictionary dictionary];
  100. [queryDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
  101. // Add the keys to the search dict
  102. [queryDictionary setObject:@"service" forKey:(__bridge id)kSecAttrService];
  103. [queryDictionary setObject:derivedKey forKey:(__bridge id)kSecAttrAccount];
  104. CFTypeRef attrs = nil;
  105. [queryDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
  106. [queryDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
  107. OSStatus res = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, &attrs);
  108. if (res == errSecSuccess) {
  109. NSDictionary *attrsDict = (__bridge NSDictionary*)attrs;
  110. if (![[attrsDict objectForKey:(__bridge id)kSecAttrAccessible] isEqualToString:(__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock]) {
  111. // Need to upgrade the accessibility on this key by setting it again
  112. [KKKeychain setString:[[NSString alloc] initWithData:[attrsDict objectForKey:(__bridge id)kSecValueData] encoding:NSUTF8StringEncoding] forKey:key];
  113. }
  114. }
  115. if (attrs)
  116. CFRelease(attrs);
  117. }
  118. @end