Last active
September 18, 2023 17:22
-
-
Save lukaskukacka/79994d0dff966b5733a80b49af9a8bca to your computer and use it in GitHub Desktop.
Keychain dumper (Obj-C). Prints all values from iOS Keychain.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@interface KeychainDumper: NSObject | |
- (void)dumpVerbose:(BOOL)verbose; | |
@end | |
@implementation KeychainDumper | |
- (void)dumpVerbose:(BOOL)verbose { | |
[self printString:@"##################################################################"]; | |
[self printString:@"### KEYCHAIN DUMP ################################################"]; | |
[self printString:@"##################################################################"]; | |
NSMutableDictionary *query = [NSMutableDictionary new]; | |
query[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; | |
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; | |
NSArray *allSecItemClasses = [NSArray arrayWithObjects: | |
(__bridge id)kSecClassGenericPassword, | |
(__bridge id)kSecClassInternetPassword, | |
(__bridge id)kSecClassCertificate, | |
(__bridge id)kSecClassKey, | |
(__bridge id)kSecClassIdentity, | |
nil]; | |
for (id secItemClass in allSecItemClasses) { | |
[self printString:@"### Item class: %@", secItemClass]; | |
[query setObject:secItemClass forKey:(__bridge id)kSecClass]; | |
CFTypeRef result = NULL; | |
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); | |
if (status == errSecItemNotFound) { | |
[self printString:@" No items for the class."]; | |
continue; | |
} | |
if (status != errSecSuccess) { | |
[self printString:@" Failed to read from keychain"]; | |
continue; | |
} | |
if (result == NULL) { | |
[self printString:@" No data the class"]; | |
continue; | |
} | |
NSArray *resultsArray = (__bridge NSArray *)result; | |
for (NSDictionary *itemDictionary in resultsArray) { | |
[self dumpData:itemDictionary verbose:verbose]; | |
} | |
if (result != NULL) { | |
CFRelease(result); | |
} | |
} | |
[self printString:@"##################################################################"]; | |
} | |
- (void)dumpData:(NSDictionary *)data verbose:(BOOL)verbose { | |
if (verbose) { | |
[self printString:[data description]]; | |
return; | |
} | |
NSString *key = data[@"svce"]; | |
if (key.length > 0) { | |
NSString *value = [self readKeychainValueForKey:key]; | |
[self printString:@" %@: '%@'", key, value]; | |
} else { | |
[self printString:@" <no non-verbose description, value with %ld keys in dictionary>", (unsigned long)data.count]; | |
} | |
} | |
- (void)printString:(NSString *)format, ... { | |
va_list args; | |
va_start(args, format); | |
NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; | |
va_end(args); | |
printf("%s\n", [string UTF8String]); | |
} | |
- (nullable NSString *)readKeychainValueForKey:(NSString *)key { | |
NSParameterAssert(key); | |
NSMutableDictionary *query = [NSMutableDictionary new]; | |
query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; | |
query[(__bridge id)kSecReturnData] = @YES; | |
query[(__bridge id)kSecAttrService] = key; | |
CFTypeRef result = NULL; | |
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); | |
if (status == errSecItemNotFound) { | |
return nil; | |
} | |
if (status != errSecSuccess) { | |
NSLog(@"Failed reading value for key: '%@'", key); | |
return nil; | |
} | |
NSData *resultData = (__bridge_transfer NSData *)result; | |
return [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment