Retrieving private-public-certificate triples from keychain

Hi there, I'm currently working on a compatibility feature for Apple that allows the user to manage their keys and certificates from within our internal API. For this I need to retrieve all the items contained within keychains. I am looking at the documentation for SecItem API but so far I have not really found an obvious way to link these items together. My best guess so far is to perform two queries, grabbing all SecKeys from the keychains, pairing them up with public keys through SecKeyCopyPublicKey, then downloading all CertItems and pairing them with public keys with SecCertificateCopyKey, and then join the two using public keys. This sounds however somewhat involved and I was wondering if there was a better way of going about the process?

Answered by DTS Engineer in 814638022

What platform are you targeting?

This matters because the keychain story is radically different on macOS than on our other platforms. See TN3137 On Mac keychain APIs and implementations.

the user to manage their keys and certificates from within our internal API.

By “user” do you mean a developer that calls your API?

It sounds like you’re trying to work with digital identities, that is, the combination of a certificate and the private key that matches the public key within that certificate. Is that right?

If so, the SecItem API has a specific class for that, kSecClassIdentity. This works in terms of SecIdentity objects.

Digital identities aren’t stored as such, by instead synthesised by combining key and certificate items. I talk about this in the Digital Identities Aren’t Real section of SecItem: Pitfalls and Best Practices.

Oh, and speaking of that you’ll probably want to real it and its platonic life partner, SecItem: Fundamentals.

If you have follow-up questions, feel free to post them here.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

What platform are you targeting?

This matters because the keychain story is radically different on macOS than on our other platforms. See TN3137 On Mac keychain APIs and implementations.

the user to manage their keys and certificates from within our internal API.

By “user” do you mean a developer that calls your API?

It sounds like you’re trying to work with digital identities, that is, the combination of a certificate and the private key that matches the public key within that certificate. Is that right?

If so, the SecItem API has a specific class for that, kSecClassIdentity. This works in terms of SecIdentity objects.

Digital identities aren’t stored as such, by instead synthesised by combining key and certificate items. I talk about this in the Digital Identities Aren’t Real section of SecItem: Pitfalls and Best Practices.

Oh, and speaking of that you’ll probably want to real it and its platonic life partner, SecItem: Fundamentals.

If you have follow-up questions, feel free to post them here.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

If so, the SecItem API has a specific class for that, kSecClassIdentity. This works in terms of SecIdentity objects.

Ah, thank you, that's exactly what I was looking for.

Okay I've made some progress, to better understand the structure and the data that is stored within the keychain I tried debugPrinting out all the attributes within the identity, however it seems like only the kSecClassCertificate related attributes are present, and none of the kSecClassKey, even though I can retrieve the private key:

let query: [String: Any] = [kSecClass as String: kSecClassIdentity,
                            kSecMatchLimit as String: kSecMatchLimitAll,
                            kSecReturnAttributes as String: true,
                            kSecReturnRef as String: true,
];
var item: CFTypeRef?;
let status = SecItemCopyMatching(query as CFDictionary, &item);
debugPrint(status);
let output = item as! [[String: Any]];
for entry in output
{
    do {
        debugPrint("Certificate attributes:");
        let label = entry[kSecAttrLabel as String];
        debugPrint("Label: ", label!);
        let certType: CSSM_CERT_TYPE = entry[kSecAttrCertificateType as String] as! CSSM_CERT_TYPE;
        debugPrint("Cert item type: ", certType);
        let issuer = entry[kSecAttrIssuer as String];
        debugPrint("Issuer: ", String(data: issuer! as! Data, encoding: .utf8)!);
        let serialNumber = entry[kSecAttrSerialNumber as String];
        debugPrint("Serial number: ", serialNumber!);
        var cert: SecCertificate?;
        SecIdentityCopyCertificate(entry[kSecValueRef as String] as! SecIdentity, &cert);
        debugPrint("Certificate: ", cert!);
        debugPrint();
    }
    do {
        debugPrint("Key attributes:");
        let keyClass = entry[kSecAttrKeyClass as String];
        debugPrint("Key class: ", keyClass);
        let keyType = entry[kSecAttrKeyType as String];
        debugPrint("Key type: ", keyType);
        let applicationLabel = entry[kSecAttrApplicationLabel as String];
        debugPrint("Application label: ", applicationLabel);
        let applicationTag = entry[kSecAttrApplicationTag as String];
        debugPrint("Application tag: ", applicationTag);
        let sizeInBits = entry[kSecAttrKeySizeInBits as String];
        debugPrint("Size in bits: ", sizeInBits);
        let effectiveKeySize = entry[kSecAttrEffectiveKeySize as String];
        debugPrint("Effective key size: ", effectiveKeySize);
        var pkey: SecKey?;
        SecIdentityCopyPrivateKey(entry[kSecValueRef as String] as! SecIdentity, &pkey);
        debugPrint("Private key: ", pkey!);
        debugPrint();
    }
}
Retrieving private-public-certificate triples from keychain
 
 
Q