Create an SecIdentityRef from a certificate and private key

Hi, I am working on a react native module used for tis connection and I am trying to implement the possibility to use a custom certificate/Private key. I have already implemented on android but on iOS I am getting hard times, we cannot find lots of resources, api is different on macOS and iOS with subtle differences so after having tested SO, chatgpt, ... I am trying here: I even tried to use an internal api since it seems ffmpeg uses it but with no success.

I have attached my current code because it does not fit here.

to sump up after having inserted cert and private key I try to get a SecIdentityRef but it fails. I assume that it's not enough to simply add certain and private key...

// Query for the identity with correct attributes
    NSDictionary *identityQuery = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassIdentity,
        (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
        (__bridge id)kSecReturnRef: @YES,
        (__bridge id)kSecReturnData: @YES,
        (__bridge id)kSecAttrLabel: @"My Certificate",
        //(__bridge id)kSecUseDataProtectionKeychain: @YES
    };
    
    SecIdentityRef identity = NULL;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)identityQuery, (CFTypeRef *)&identity);

SecItemCopyMatching with kSecClassIdentity fails, SecIdentityCreate return NULL... So please help and indicates what I am doing wrong and how I am supposed getting a SecIdentityRef. Thanks

Answered by DTS Engineer in 823700022

Honestly, I’m not sure why this is failing for you. I’m struggling to think of ways I can debug this remotely, so instead I decided to write up an end-to-end example that shows how to do this. See Importing a PEM-based RSA Private Key and its Certificate.

I suggest you do the following:

  1. Put that code into a simple test app and confirm that it works. If it does, that rules out any environmental issues.

  2. Then modify that test app to use your private key and certificate PEMs. If it continues to work, that rules out any issues with your credentials.

  3. Finally, try adapting this code for your project. If you’re gonna convert this to Objective-C, do that one routine at a time. That way you can test your new code within your test app that you know works in general.

Good luck!

Share and Enjoy

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

Cert Attributes: { accc = "<SecAccessControlRef: dk>"; agrp = "D27Q6535KW.com.lotalogic.testandroidtvremoteapp"; cdat = "2025-02-01 16:18:58 +0000"; cenc = 3; ctyp = 3; issr = {length = 89, bytes = 0x310b3009 06035504 06130246 52310c30 ... 69636520 4e616d65 }; labl = cert; mdat = "2025-02-01 16:18:58 +0000"; musr = {length = 0, bytes = 0x}; pdmn = dk; pkhh = {length = 20, bytes = 0x312ede000df0366b3b5fb559a48177ca23215516}; sha1 = {length = 20, bytes = 0x0d8661f2c1cf641dade7d7ed53475beed6c1e654}; skid = {length = 20, bytes = 0x312ede000df0366b3b5fb559a48177ca23215516}; slnr = {length = 20, bytes = 0x5c2576d44745619b3cab62ebcc402d19741150d4}; subj = {length = 89, bytes = 0x310b3009 06035504 06130246 52310c30 ... 69636520 4e616d65 }; sync = 0; tomb = 0; }

Key Attributes: { accc = "<SecAccessControlRef: ak>"; agrp = "D27Q6535KW.com.lotalogic.testandroidtvremoteapp"; atag = ""; bsiz = 0; cdat = "2025-02-01 16:19:01 +0000"; crtr = 0; edat = "2001-01-01 00:00:00 +0000"; esiz = 0; kcls = 1; klbl = {length = 20, bytes = 0x312ede000df0366b3b5fb559a48177ca23215516}; labl = cert; mdat = "2025-02-01 16:19:01 +0000"; musr = {length = 0, bytes = 0x}; pdmn = ak; sdat = "2001-01-01 00:00:00 +0000"; sha1 = {length = 20, bytes = 0x66164ac9fd9272542996070684f5346e2e5b86da}; sync = 0; tomb = 0; type = 42; }

So this time klbl and pkhh are identical, so what is the next step of this escape game ?
I am creating cert (SecCertificateCreateWithData), adding it to keystore, retrieving attributes to get pkhh creating private key (SecKeyCreateWithData), adding it to keystore with the kSecAttrApplicationLabel filled with the cert's pkhh...

CODE

Glad to hear you’re making progress.

So this time klbl and pkhh are identical

Cool.

so what is the next step of this escape game ?

I’m not sure I understand this. Once the relevant attributes line up, identity formation should work. Is it failing? If so, what are the symptoms?

Share and Enjoy

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

NSDictionary *identityQuery = @{
            (__bridge id)kSecClass : (__bridge id)kSecClassIdentity,
            (__bridge id)kSecReturnRef : @YES,
        };
        status = SecItemCopyMatching((__bridge CFDictionaryRef)identityQuery,
                                     (CFTypeRef *)&identity);

I get status = -25300 (errSecItemNotFound).

I have also added attributes (__bridge id)kSecAttrIsPermanent : @"YES", when adding key but does not help.

Honestly, I’m not sure why this is failing for you. I’m struggling to think of ways I can debug this remotely, so instead I decided to write up an end-to-end example that shows how to do this. See Importing a PEM-based RSA Private Key and its Certificate.

I suggest you do the following:

  1. Put that code into a simple test app and confirm that it works. If it does, that rules out any environmental issues.

  2. Then modify that test app to use your private key and certificate PEMs. If it continues to work, that rules out any issues with your credentials.

  3. Finally, try adapting this code for your project. If you’re gonna convert this to Objective-C, do that one routine at a time. That way you can test your new code within your test app that you know works in general.

Good luck!

Share and Enjoy

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

I first created a swift app with your code and it worked then I translated to objc and it worked too... To retrieve the identity I added kSecAttrLabel : certAlias attribute to avoid to list all identities.

Thanks a lot. I won't bother you anymore xD

Did I say I won't bother you xD, one question when I restart my app are the cert and private key still there ? Because just after identity creation I can call hasIdentity:

+ (BOOL)hasIdentity:(NSDictionary *)aliases {
    NSString *certAlias = aliases[@"certAlias"];
    if (!certAlias) {
        return NO;
    }
    OSStatus status = -1;
    NSDictionary *identityQuery = @{
        (__bridge id)kSecClass : (__bridge id)kSecClassIdentity,
        (__bridge id)kSecAttrLabel : certAlias
    };
    status = SecItemCopyMatching((__bridge CFDictionaryRef)identityQuery, (CFTypeRef *)NULL);
    
//    NSDictionary *certQuery = @{
//        (__bridge id)kSecClass : (__bridge id)kSecClassCertificate,
//        (__bridge id)kSecAttrLabel : certAlias
//    };
//    status = SecItemCopyMatching((__bridge CFDictionaryRef)certQuery, (CFTypeRef *)NULL);
   
    return status == errSecSuccess;
}

return true but once I restart my app it's false. So I know that identity is not stored but is a kind of runtime creation but even when I try with certAlias it returns item not found. So is there a magic parameter to pass to persist them ? When Adding key/cert I added kSecAttrAccessibleAfterFirstUnlock but don't seem to work.

It’s hard to say what’s going on here without more context. However, I ran a basic test here in my office and everything seems to as expected. Here’s what I did:

  1. I took the code and credentials from Importing a PEM-based RSA Private Key and its Certificate and added them to a test project.

  2. I added three buttons to the UI, named Import, Dump, and Find.

  3. I wired them up to the code shown at the end of this post.

  4. I ran the app in the iOS simulator.

  5. I tapped Import. It imported the identity. I didn’t expect a problem here because addRSA2048DigitalIdentityPEMToKeychain(…) came from my working example.

  6. I tapped Dump. This printed:

    will dump
    0
      …
      labl: Benjy
      …
    did dump
    

    This confirms that the identity was imported and has a label (kSecAttrLabel) of Benjy.

  7. I tapped Find. It printed:

    will find
    did find, identity: <SecIdentityRef: 0x60000021a680>
    

    This confirms that the identity is findable by that label.

  8. I stopped the app, then ran it again, then tapped Find again. It continues to work.

I recommend that you repeat my steps to confirm that they work in your environment. If so, you can compare my code to your code to see where things are going wrong for you.

Share and Enjoy

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


func importTest() {
    do {
        print("will import")
        let identity = try addRSA2048DigitalIdentityPEMToKeychain(certificate: benjyCertificatePEM, privateKey: benjyPrivateKeyPEM)
        print("will import, identity: \(identity)")
    } catch {
        print("did not import, error: \(error)")
    }
}

func dumpTest() {
    do {
        print("will dump")
        let attrsForIdentities = try secCall { SecItemCopyMatching([
            kSecClass: kSecClassIdentity,
            kSecMatchLimit: kSecMatchLimitAll,
            kSecReturnAttributes: true,
        ] as NSDictionary, $0) } as! [[String: Any]]
        for (i, attrs) in zip(0..., attrsForIdentities) {
            print(i)
            let printable = attrs.map { key, value in
                "  \(key): \(value)"
            }
            .joined(separator: "\n")
            print(printable)
        }
        print("did dump")
    } catch {
        print("did not dump, error: \(error)")
    }
}

func findTest() {
    do {
        print("will find")
        let identity = try secCall { SecItemCopyMatching([
            kSecClass: kSecClassIdentity,
            kSecAttrLabel: "Benjy",
            kSecReturnRef: true,
        ] as NSDictionary, $0) } as! SecIdentity
        print("did find, identity: \(identity)")
    } catch {
        print("did not find, error: \(error)")
    }
}
Create an SecIdentityRef from a certificate and private key
 
 
Q