iOS mTLS Client Certificate Authentication Fails in TestFlight with Error -25303

iOS mTLS Client Certificate Authentication Fails in TestFlight with Error -25303

Problem

I'm building an iOS app that uses mTLS (client certificates received from server at runtime). Storing SecCertificate to keychain fails with error -25303 in both development and TestFlight builds, preventing SecIdentity creation needed for URLSession authentication.

Environment: iOS 18.2, iPad Pro, TestFlight internal testing, keychain-access-groups properly configured

Diagnostic Results

Testing keychain operations shows an interesting pattern:

✅ Generic Password - Works:

let addQuery: [CFString: Any] = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: "test",
    kSecValueData: "password".data(using: .utf8)!
]
SecItemAdd(addQuery as CFDictionary, nil) // Returns: 0 (success)

✅ SecKey - Works:

let addKeyQuery: [CFString: Any] = [
    kSecClass: kSecClassKey,
    kSecValueRef: privateKey,
    kSecAttrApplicationTag: tag
]
SecItemAdd(addKeyQuery as CFDictionary, nil) // Returns: 0 (success)

❌ SecCertificate - Fails:

let addCertQuery: [CFString: Any] = [
    kSecClass: kSecClassCertificate,
    kSecValueRef: certificate,  // Created from server-provided PEM
    kSecAttrApplicationTag: tag
]
SecItemAdd(addCertQuery as CFDictionary, nil) // Returns: -25303

Code Context

Attempting to create SecIdentity for mTLS:

private func createIdentity(fromCert certPEM: String, key keyPEM: String) throws -> SecIdentity {
    // 1. Parse PEM to DER and create SecCertificate - succeeds
    guard let certData = extractPEMData(from: certPEM, type: "CERTIFICATE"),
          let certificate = SecCertificateCreateWithData(nil, certData as CFData) else {
        throw CertificateError.invalidCertificate
    }

    // 2. Parse PEM key and create SecKey - succeeds
    guard let keyData = extractPEMData(from: keyPEM, type: "PRIVATE KEY"),
          let privateKey = SecKeyCreateWithData(keyData as CFData, attrs as CFDictionary, &error) else {
        throw CertificateError.invalidKey
    }

    // 3. Add key to keychain - SUCCEEDS (errSecSuccess)
    let tempTag = UUID().uuidString.data(using: .utf8)!
    SecItemAdd([
        kSecClass: kSecClassKey,
        kSecValueRef: privateKey,
        kSecAttrApplicationTag: tempTag
    ] as CFDictionary, nil)  // ✅ Works

    // 4. Add certificate to keychain - FAILS (-25303)
    let status = SecItemAdd([
        kSecClass: kSecClassCertificate,
        kSecValueRef: certificate,
        kSecAttrApplicationTag: tempTag
    ] as CFDictionary, nil)  // ❌ Fails with -25303

    guard status == errSecSuccess else {
        throw CertificateError.keychainError(status)
    }

    // 5. Would query for SecIdentity (never reached)
    // ...
}

Network Behavior

When mTLS fails, console shows:

Connection: asked for TLS Client Certificates
Connection: received response for client certificates (-1 elements)
Connection: providing TLS Client Identity (-1 elements)
Task received response, status 403

The -1 elements indicates no certificates were provided.

Entitlements

<key>keychain-access-groups</key>
<array>
    <string>$(AppIdentifierPrefix)com.ellin.tshios</string>
</array>

Keychain Sharing capability is enabled.

What I've Tried

  1. Both kSecValueRef and kSecValueData approaches - same error
  2. Various kSecAttrAccessible values - same error
  3. Different keychain access groups - same error
  4. TestFlight build (vs dev build) - same error
  5. PKCS#12 creation - requires complex ASN.1/DER encoding, no iOS API

Questions

  1. Is error -25303 expected when adding SecCertificate in development/TestFlight builds?
  2. Will App Store distribution resolve this? Or is there a fundamental limitation?
  3. Why does SecKey succeed but SecCertificate fails with identical entitlements?
  4. Is there an alternative to create SecIdentity without keychain access?

Constraints

  • Certificates come from server at runtime (cannot bundle)
  • Need SecIdentity for URLSession client certificate authentication
  • Server provides PEM format certificates
  • Tested on: Simulator (dev), iPad Pro (dev), iPad Pro (TestFlight) - all fail

Any insights appreciated - specifically whether this is a provisioning profile limitation that App Store distribution would resolve.

Answered by DTS Engineer in 875929022

The error here, -25303, or errSecNoSuchAttr, suggests a problem with your attributes. And the only non-required attribute you’re supplying is kSecAttrApplicationTag. Which is, in fact, the cause of your issue. kSecAttrApplicationTag is only supported on keys.

I recommend that you have a read of SecItem: Fundamentals [1], and specifically the The Four Freedoms^H^H^H^H^H^H^H^H Functions section, which explains how you can use SQL to model the expected behaviour of the SecItem API. In this case, the SQL table for certificate items has no kSecAttrApplicationTag, and thus this error.

It also has links to the doc that explains which keychain item class supports which attributes.

Is there an alternative to create SecIdentity without keychain access?

We finally (finally finally!) have this. See the docs for the SecIdentityCreate function.

IMPORTANT This was added in Xcode 26 but backdeploys to much older systems. Or at least that’s the plan (-: This backdeployment was a bit of a challenge, and I’ve not actually tested it myself.

However, that may not be the right option here. An in-memory identity like this makes sense if this credential is ephemeral. My experience with setups like yours is that the credential tends to be long-lived, and thus storing it in the keychain is the correct choice.

Share and Enjoy

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

[1] I also recommend SecItem: Pitfalls and Best Practices, but it’s not directly relevant to this issue,

The error here, -25303, or errSecNoSuchAttr, suggests a problem with your attributes. And the only non-required attribute you’re supplying is kSecAttrApplicationTag. Which is, in fact, the cause of your issue. kSecAttrApplicationTag is only supported on keys.

I recommend that you have a read of SecItem: Fundamentals [1], and specifically the The Four Freedoms^H^H^H^H^H^H^H^H Functions section, which explains how you can use SQL to model the expected behaviour of the SecItem API. In this case, the SQL table for certificate items has no kSecAttrApplicationTag, and thus this error.

It also has links to the doc that explains which keychain item class supports which attributes.

Is there an alternative to create SecIdentity without keychain access?

We finally (finally finally!) have this. See the docs for the SecIdentityCreate function.

IMPORTANT This was added in Xcode 26 but backdeploys to much older systems. Or at least that’s the plan (-: This backdeployment was a bit of a challenge, and I’ve not actually tested it myself.

However, that may not be the right option here. An in-memory identity like this makes sense if this credential is ephemeral. My experience with setups like yours is that the credential tends to be long-lived, and thus storing it in the keychain is the correct choice.

Share and Enjoy

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

[1] I also recommend SecItem: Pitfalls and Best Practices, but it’s not directly relevant to this issue,

iOS mTLS Client Certificate Authentication Fails in TestFlight with Error -25303
 
 
Q