How to generate SecIdentityRef with SecCertificateRef and SecKeyRef on iOS

We generated key pair and imported associated cert into keychain. We stored value of kSecAttrPublicKeyHash and use it to find matching certificate and private key later. However we have some legacy code that requests to get SecIdentityRef. We can retrieve SecCertificateRef with matched value of kSecAttrPublicKeyHash and private key with kSecAttrApplicationLabel. But we don't know how to generate/query SecIdentityRef with SecCertificateRef and SecKeyRef. API SecIdentityCreateWithCertificate is only available on macOS. Is there any equivalent API on iOS?

Thanks, Ying

Replies

You do this by calling SecItemCopyMatching with kSecClass set to kSecClassIdentity and kSecReturnRef set to true. I generally start out by setting kSecMatchLimit to kSecMatchLimitAll to get back all the identities, which checks that identity formation is working correctly. After I start adding in other filter criteria to get back exactly the identity I want.

ps I have a bunch of hints and tips on the SecItem API in:

Share and Enjoy

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

Hi Quinn,

We have tried query with uniqueness constraint to get exactly matched identity, but no luck. Since we have cached public key hash, so we retrieved certificate data and use it to query as kSecClass set to kSecClassIdentity, kSecMatchItemList set to cert data array, kSecReturnRef set to true. It returns the identity, but not the one matched. We also tried to query with kSecClass set to kSecClassIdentity, kSecAttrPublicKeyHash set to public key hash, kSecReturnRef set to true, it returns -25291. We used to query exact identity with kSecValuePersistentRef in the other project. But for this project, we only cached public key hash, so not sure how to get matched identity. To figure out what we can use, we queried identities with attributes and got returned dictionary list. Below is the attributes dictionary for the identity that we want to get:

    UUID = "F970C2F0-2FB8-419B-924E-141BD593D700";
    accc = "<SecAccessControlRef: dk>";
    agrp = "JBF29L28EJ.com.example.keychainTest";
    asen = 0;
    atag = "";
    bsiz = 256;
    cdat = "2024-03-21 19:13:11 +0000";
    cenc = 3;
    certdata = {length = 1190, bytes = 0x308204a2 30820428 a0030201 02021466 ... fa248377 6a0d670c };
    class = idnt;
    crtr = 0;
    ctyp = 3;
    decr = 1;
    drve = 1;
    edat = "2001-01-01 00:00:00 +0000";
    encr = 0;
    esiz = 256;
    extr = 1;
    issr = {length = 73, bytes = 0x31243022 06035504 030c1b50 524f4420 ... 0a0c0543 6973636f };
    kcls = 1;
    klbl = {length = 20, bytes = 0x0d08990422f219443228a0149948b76477070a7a};
    labl = "urn::deviceid:0ace7954238d7747";
    mdat = "2024-03-21 19:13:11 +0000";
    modi = 1;
    musr = {length = 0, bytes = 0x};
    next = 0;
    pdmn = dk;
    perm = 1;
    pkhh = {length = 20, bytes = 0x0d08990422f219443228a0149948b76477070a7a};
    priv = 1;
    sdat = "2001-01-01 00:00:00 +0000";
    sens = 0;
    sha1 = {length = 20, bytes = 0xe10d689ebac24f5f90e64d94f83eef53e76281bb};
    sign = 1;
    skid = {length = 20, bytes = 0x0d08990422f219443228a0149948b76477070a7a};
    slnr = {length = 20, bytes = 0x660cd07546ddf3e095a1b0c1bbee50efe44eddb4};
    snrc = 0;
    subj = {length = 55, bytes = 0x31353033 06035504 030c2c75 726e3a63 ... 32333864 37373437 };
    sync = 0;
    tomb = 0;
    type = 73;
    unwp = 1;
    "v_Ref" = "<SecIdentityRef: 0x283484f80>";
    vrfy = 0;
    vyrc = 0;
    wrap = 0;

The labl = "urn::deviceid:0ace7954238d7747" is not unique for certificate, it is only unique for the device. Looks like the unique constraints are certdata and pkhh/public key hash, but we tried both with no luck. Any idea?

Thanks, Ying

Ok, we found the unique constraint to query SecIdentity. Although kSecAttrPublicKeyHash doesn't work, but kSecAttrApplicationLabel with public key hash gets exactly SecIdentity that we want.