Search code examples
swiftmacossecuritykeychain

Keychain API is looking only into system keychain not in login keychain Mac OS Swift


I need some help in keychain API. I am searching a certificate in keychain using keychain API in a mac os application. However, it is searching only in system.keychain not in login.keychain. I didn't find any document where i can put or specify keychain in keychain search query. The current code is as below.

func isKeychainhasCert(for sNumber:Data) -> Bool {
      
        var query = [String: AnyObject]()
        query[kSecClass as String] = kSecClassCertificate
        query[kSecAttrSerialNumber as String] = sNumber as CFData
        query[kSecReturnRef as String] = true as CFBoolean
        var queryResult: AnyObject?
        let status = withUnsafeMutablePointer(to: &queryResult, {
            SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
        })
        guard status != errSecItemNotFound else {
            return false
        }
        
        return true
    }

Any help or suggestion. Based on above code the method return only true if cert is present system.keychain. If present in login.keychain but not in system.keychain return false.


Solution

  • This is not an answer, but might shed some light on how to get the certs:

    import Foundation
    import Security
    
    var result: OSStatus
    
    // Usually, we operate in the "user" domain, check this:
    var domain: SecPreferencesDomain = .system
    result = SecKeychainGetPreferenceDomain(&domain)
    if result == errSecSuccess {
        assert(domain == .user)
    }
    
    // Get the list of searched keychains of the current domain:
    // (it should include the "default" keychain for the "user" domain)
    var searchList: CFArray? = nil
    result = SecKeychainCopyDomainSearchList(domain, &searchList)
    guard result == errSecSuccess, let searchedKeyChains = searchList as? [SecKeychain] else {
        fatalError("SecKeychainCopyDomainSearchList failed")
    }
    
    print("Keychain search list:")
    searchedKeyChains.forEach { keyChain in
        print(keyChain)
    }
    print("")
    
    // Get the default keychain for the current domain:
    var defaultKeyChain: SecKeychain? = nil
    result = SecKeychainCopyDefault(&defaultKeyChain)
    guard result == errSecSuccess, defaultKeyChain != nil else {
        fatalError("SecKeychainCopyDefault failed")
    }
    print("Default Keychain: \(defaultKeyChain!)")
    
    

    Up until here, we only did some checks and tests.

    Now, search the all certificates in the keychain(s):

    // Here, search all "searchable" keychains, by not specifying a
    // kSecMatchSearchList value.
    var copyResult: CFTypeRef? = nil
    result = SecItemCopyMatching([
        kSecClass: kSecClassCertificate,
        kSecMatchLimit: kSecMatchLimitAll,
        kSecReturnRef: true
    ] as NSDictionary, &copyResult)
    
    guard result == errSecSuccess, let certificates1 = copyResult as? [SecCertificate] else {
        fatalError("unexpected result type: \(copyResult)")
    }
    
    

    Print all found certificates:

    certificates1.forEach { cert in
        print(cert)
    }
    

    Search only in the "default" keychain for the current domain (which should be .user). We do this, by specifying the kSecMatchSearchList parameter:

    // Here, search only the default keychain:
    result = SecItemCopyMatching([
        kSecClass: kSecClassCertificate,
        kSecMatchLimit: kSecMatchLimitAll,
        kSecMatchSearchList: [defaultKeyChain] as CFArray,
        kSecReturnRef: true
    ] as NSDictionary, &copyResult)
    
    guard result == errSecSuccess, let certificates2 = copyResult as? [SecCertificate] else {
        fatalError("unexpected result type: \(copyResult)")
    }
    
    

    Now, lets see what additional certs have been found in any other keychain than the default one:

    
    let diff = Array(Set(certificates1).symmetricDifference(Set(certificates2)))
    print("Difference:")
    
    diff.forEach { cert in
        print(cert)
    }
    print("")