iOS Question How to delete all keychain entries for an app, even if they are unknown?

JordiCP

Expert
Licensed User
Longtime User
My app uses a 3rd party authorization framework which stores some data in the keychain. For this, the app provides the needed entitlements.

The framework stores account ids, tokens and other user data, which I don't want to be available when the app is deleted and reinstalled.

If I knew the keys, I guess it would be possible with phone.keyChainRemove(..) but in this case I don't know which keys were stored, just want to bulk erase all the entries so that the framework behaves as with a fresh install.

Hope it makes sense. Is it possible?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    Dim p As Phone
    p.KeyChainPut("aaa", "bbb")
    Log(p.KeyChainGet("aaa"))
    Me.As(NativeObject).RunMethod("deleteAllKeychainItems", Null)
    Log(p.KeyChainGet("aaa"))
    
End Sub

#if objc
- (void)deleteAllKeychainItems {
    NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword,
                           (__bridge id)kSecClassInternetPassword,
                           (__bridge id)kSecClassCertificate,
                           (__bridge id)kSecClassKey,
                           (__bridge id)kSecClassIdentity];
    for (id secItemClass in secItemClasses) {
        NSDictionary *spec = @{(__bridge id)kSecClass: secItemClass};
        SecItemDelete((__bridge CFDictionaryRef)spec);
    }
}
#End If
Based on: https://stackoverflow.com/questions...-items-accessible-to-an-app?noredirect=1&lq=1
 
Upvote 0

Mr.Coder

Member
Licensed User
Longtime User
- This method deletes keys whose name contains any item of an array passed to the method :
B4X:
- (NSString *)deleteKeychainItemsContainingArray:(NSArray *)texts {

    if (!texts || texts.count == 0) {
        return @"EmptyArray";
    }
    
    NSDictionary *query = @{(__bridge id)kSecClass:
                            (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecReturnAttributes: @YES,
                            (__bridge id)kSecMatchLimit:
                            (__bridge id)kSecMatchLimitAll};

    CFTypeRef result = NULL;

    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query,&result);

    if (status != errSecSuccess) {
        return @"Failed";
    }
    
    NSArray *items = (__bridge NSArray *)result;

    for (NSDictionary *item in items) {

        NSString *account = item[(__bridge id)kSecAttrAccount];

        if (!account) {
            continue;
        }
        
        BOOL nameMatch = NO;

        for (NSString *text in texts) {

            if ([account containsString:text]) {
                nameMatch = YES;
                break;
            }
            
        }

        if (nameMatch) {

            NSDictionary *deleteQuery = @{
                (__bridge id)kSecClass:
                    (__bridge id)kSecClassGenericPassword,
                (__bridge id)kSecAttrAccount:
                    account};

            SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
            
        }
        
    }
    
    return @"Success";
    
}

- How to use it :
B4X:
    Dim lst As List
    
    lst.Initialize
    
    lst.Add("aaa")
    lst.Add("bbb")
    lst.Add("ccc")
    
    Dim Result As String = Me.As(NativeObject).RunMethod("deleteKeychainItemsContainingArray:", Array(lst)).AsString.Trim
    
    If Result = "Success" Then
        
    End If

- This method deletes keys whose name contains the text passed to the method and also passes character sensitivity :
B4X:
- (NSString *)deleteKeychainItemsContaining:(NSString *)text :(BOOL)caseSensitive {
    
    if (!text || text.length == 0) {
        return @"EmptyText";
    }

    NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword];

    for (id secItemClass in secItemClasses) {

        NSDictionary *query = @{(__bridge id)kSecClass: secItemClass,
                                (__bridge id)kSecReturnAttributes: @YES,
                                (__bridge id)kSecMatchLimit:
                                (__bridge id)kSecMatchLimitAll};

        CFTypeRef result = NULL;

        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);

        if (status != errSecSuccess) {
            continue;
        }
        
        NSArray *items = (__bridge NSArray *)result;

        for (NSDictionary *item in items) {

            NSString *account = item[(__bridge id)kSecAttrAccount];
            
            if (!account) {
                continue;
            }
            
            BOOL nameMatch = NO;

            if (caseSensitive) {

                nameMatch = [account containsString:text];

            } else {

                NSRange r = [account rangeOfString:text options:NSCaseInsensitiveSearch];

                nameMatch = (r.location != NSNotFound);
                
            }

            if (nameMatch) {

                NSMutableDictionary *deleteQuery = [NSMutableDictionary dictionary];

                deleteQuery[(__bridge id)kSecClass] = secItemClass;

                deleteQuery[(__bridge id)kSecAttrAccount] = account;

                SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
                
            }
            
        }
        
    }
    
    return @"Success";
        
}

- How to use it :
B4X:
    Dim Result As String = Me.As(NativeObject).RunMethod("deleteKeychainItemsContaining::", Array("aaa", False)).AsString.Trim

    If Result = "Success" Then
        
    End If
 
Upvote 0
Top