Post

Replies

Boosts

Views

Activity

Reply to mTLS : Guidance on Generating SecIdentity with Existing Private Key and Certificate
Step 6 - Get a digital identity from the keychain: private func getClientIdentity() -> SecIdentity? { let query: [String: Any] = [ kSecClass as String: kSecClassIdentity, kSecAttrLabel as String: MTLSTag.clientcert, // <-- match by certificate tag kSecReturnRef as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var identityRef: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &identityRef) if status == errSecSuccess, let identity = identityRef as! SecIdentity? { print("Retrieved client identity from Keychain") return identity } else { print("Could not retrieve client identity, status=\(status)") return nil } } Usages : func updateCertificates() { // Load CA certificates (if needed for server trust validation) self.trustedCACerts = CertificateUtils.getCACertificates() self.storeCertificatesInKeychain() if let useridentity = self.getClientIdentity() { self.clientCredential = URLCredential(identity: useridentity, certificates: nil, persistence: .forSession) } }
Aug ’25
Reply to mTLS : Guidance on Generating SecIdentity with Existing Private Key and Certificate
Hello, Thank you for sharing the provided solution. I followed all the steps and used the same API set. However, while retrieving the SecIdentity using the certificate fetch API SecItemCopyMatching, I am encountering the following error: Error: Could not retrieve client identity, status = -25300 Please find the relevant code snippets below: Step 1 to 3 : Generate the private key in the keychain. Derive the public key from the private key. Export the public key bits and send that your certificate issuing infrastructure. func generateCSR(_ deviceId: String? = UserProfile.deviceId) -> String? { do { // Define subject DN for CSR let subject = try DistinguishedName([ .init(type: .NameAttributes.countryName, printableString: "US"), .init(type: .NameAttributes.stateOrProvinceName, printableString: "stateProvince_Name"), .init(type: .NameAttributes.localityName, printableString: "locality_Name"), .init(type: .NameAttributes.organizationName, printableString: "organization_Name"), .init(type: .NameAttributes.organizationalUnitName, printableString: "Engineering"), .init(type: .NameAttributes.commonName, utf8String: deviceId ?? "NA"), ]) // Application tag for persistent key storage in Keychain let tagData = MTLSTag.clientkey.data(using: .utf8)! as NSData // Generate EC keypair (private key in Keychain, permanent) let keyAttributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits as String: 256, kSecAttrIsPermanent as String: true, // retain key in Keychain kSecAttrApplicationTag as String: tagData // allow lookup by tag ] // In step 1, use SecKeyCreateRandomKey. var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(keyAttributes as CFDictionary, &error) else { print("Failed to create private key: \(error!.takeRetainedValue())") return nil } // In step 2, use SecKeyCopyPublicKey. // Extract public key (needed for CSR building) let publicKey = SecKeyCopyPublicKey(privateKey)! // Wrap private key so Swift `Certificate.PrivateKey` can use it let wrappedPrivateKey = try Certificate.PrivateKey(privateKey) // In step 3, use SecKeyCopyExternalRepresentation. // (Optional) Validate we can export the public key (for debugging or backend raw SPKI cases) if let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? { print("Public key exported, length: \(publicKeyData.count) bytes") } // Extensions to request in CSR let extensions = try Certificate.Extensions { BasicConstraints.notCertificateAuthority KeyUsage(digitalSignature: true, keyCertSign: true) } let extensionRequest = ExtensionRequest(extensions: extensions) let csrAttributes = try CertificateSigningRequest.Attributes([.init(extensionRequest)]) // Build CSR object with subject & keypair let csr = try CertificateSigningRequest( version: .v1, subject: subject, privateKey: wrappedPrivateKey, attributes: csrAttributes, signatureAlgorithm: .ecdsaWithSHA256 ) // Double-check CSR is signed correctly guard csr.publicKey.isValidSignature(csr.signature, for: csr) else { print("Invalid CSR signature") return nil } // Return PEM string return try csr.serializeAsPEM().pemString } catch { print("CSR generation failed: \(error)") return nil } } Step 4 : The server sends you back a PEM, convert into DER class CertificateUtils { static func getCRTCertificate() -> SecCertificate? { if let certificateString = UserAccount.getUserCRT() { return Certificates.convertStringToCertificate(certificateString: certificateString) } return nil } static func getCACertificates() -> [SecCertificate]? { if let certificateString = UserAccount.getUserCA() { return Certificates.convertMultiCertStringToCertificates(certificatesString: certificateString) } return nil } } Step 5 : Add that to your keychain. private func storeCertificatesInKeychain() { // Get client cert guard let clientCert = CertificateUtils.getCRTCertificate() else { print("No client certificate found") return } // Add client certificate to Keychain let clientQuery: [String: Any] = [ kSecClass as String: kSecClassCertificate, kSecValueRef as String: clientCert, kSecAttrLabel as String: MTLSTag.clientcert ] let status = SecItemAdd(clientQuery as CFDictionary, nil) if status == errSecDuplicateItem { print("Client certificate already in Keychain") } else if status != errSecSuccess { print("Failed to add client certificate: \(status)") } else { print("Client certificate added to Keychain") } // Add intermediate CA certs (optional) if let caCerts = CertificateUtils.getCACertificates() { for (index, cert) in caCerts.enumerated() { let caQuery: [String: Any] = [ kSecClass as String: kSecClassCertificate, kSecValueRef as String: cert, kSecAttrLabel as String: "\(MTLSTag.clientcert).\(index)" ] let caStatus = SecItemAdd(caQuery as CFDictionary, nil) if caStatus == errSecDuplicateItem { print("CA certificate \(index) already in Keychain") } else if caStatus != errSecSuccess { print("Failed to add CA certificate \(index): \(caStatus)") } else { print("Added CA certificate \(index)") } } } }
Aug ’25