Search code examples
swiftsecuritycertificatekeychain

How to extract or compare kSecPropertyKeyValue from SecCertificate


Need to parse and compare certificate values, but some of them are returned as Data which doesn't parse as a string. Most properties are returned as numbers or strings, but kSecPropertyKeyValue is an array of CFData. Need this for similar certificate information display as Keychain Access does.

import Foundation
import Security

let query: [CFString: Any] = [
    kSecClass: kSecClassCertificate,
    kSecReturnAttributes: kCFBooleanTrue,
    kSecReturnRef: kCFBooleanTrue,
    kSecReturnData: kCFBooleanTrue,
    kSecMatchLimit: kSecMatchLimitAll
]

var result: AnyObject?
SecItemCopyMatching(query as CFDictionary, &result)
let certs: [SecCertificate] = (result as? [[CFString: Any]])?.map({ $0[kSecValueRef] as! SecCertificate }) ?? []

for cert in certs {
    if let values: [CFString:Any] = (SecCertificateCopyValues(cert, [kSecOIDExtendedKeyUsage] as CFArray, nil) as? [CFString:Any])?[kSecOIDExtendedKeyUsage] as? [CFString:Any] {

        // Expect to find `kSecOIDExtendedUseCodeSigning` value or something else
        // meaningful. How do I do that?

        print(
            "type:", values[kSecPropertyKeyType]!,
            "||| data values:", values[kSecPropertyKeyValue] as! [Data],
            "||| cString:", (values[kSecPropertyKeyValue] as! [CFData]).map({ String(cString: CFDataGetBytePtr($0)) }),
            "||| string:", (values[kSecPropertyKeyValue] as! [Data]).map({ String(data: $0, encoding: .utf8) })
        )
    }
}

Paste in a playground, prints:

type: array ||| data values: [8 bytes] ||| cString: ["+\u{06}\u{01}\u{05}\u{05}\u{07}\u{03}\u{04}"] ||| string: [Optional("+\u{06}\u{01}\u{05}\u{05}\u{07}\u{03}\u{04}")]
type: array ||| data values: [9 bytes] ||| cString: ["*�H��cd\u{04}\t"] ||| string: [nil]

Some look like unicode escapes, some appear like invalid strings. Any help appreciated.


Solution

  • Apparently OID values don't have readable string representations, but are constants, which can be found in google/der-ascii.

    For example, the above output of +\u{06}\u{01}\u{05}\u{05}\u{07}\u{03}\u{04} matches 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, which is email protection (+ is 0x2b in bytes).