Hi,
I'm working on a SSL client authentication scheme, schematically:
- generate a RSA (for now) keypair on the phone (tagging it with a applcation data)
- generating PKCS#10 data (public key and meta data), send it to a server to be signed
- getting a signed certificate back and importing into the keychain
When receiving client authentication chanlenge, I search for the identity, using the tag as the query, then create a URL credential.
It works on iOS 12
However, if I try on a iOS 13 device, I get -34018 (Missing entitlements) systematically when I use SecCopyItemMatching.
In development, the certificate is signed by a self-signed CA.
I have no need for keychain group, but tried to add the app-identifier as a keychain group in the entitlements, but the result is the same.
Could it be a iOS 13 bug or some change in Security?
On iOS13, I can get the private key and the cert separately (lookup using the public key hash) so I don't understand why I couldn't get the identity.
If anyone have some insight on this, I'll take it.
Thanks!
Jean-Alexis
Here are some excerpt from the code:
func generateCSR() -> Data? {
let keyParameters : [CFString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 2048,
kSecPublicKeyAttrs: [
kSecAttrLabel: "My Public Key" as NSString,
kSecAttrIsPermanent: true,
],
kSecPrivateKeyAttrs: [
kSecAttrLabel: "My Private Key" as NSString,
kSecAttrApplicationTag: Self.keyTag,
kSecAttrIsPermanent: true,
// kSecAttrAccessControl: controlAccess!,
]
]
var privateKey: SecKey! // on success, privateKey will not be nil
guard withCFErrorCheck(context: "Generate key pair", {
privateKey = SecKeyCreateRandomKey(keyParameters as CFDictionary, $0)
}) else {
return nil
}
return pkcs10(privateKey)
}
func handleCertResponse(data: Data?, response: URLResponse?, error: Error?) {
// pkcs1DER was generated by openssl, received and decoded from Data, certificate import seems to work
guard let signedKey = SecCertificateCreateWithData(nil, pkcs1DER as CFData) else {
print ("Invalid DER X.509 certificate")
return
}
let keyAttributes : [CFString: Any] = [kSecClass: kSecClassCertificate, kSecValueRef: signedKey,
kSecAttrLabel: Self.certificateLabel,
kSecReturnRef: true,
kSecReturnAttributes: true,
]
var returned: CFTypeRef?
let status = SecItemAdd(keyAttributes as CFDictionary, &returned)
guard isStatusOK(status, context: "Add certificate in keychain") else {
return
}
let certAttributes = returned as! CFDictionary as Dictionary
print ("Certificate values: \(certAttributes)")
}
//
func getIdentity() -> SecIdentity? {
let getquery: [CFString: Any] = [kSecClass: kSecClassIdentity,
kSecAttrApplicationTag: Self.keyTag,
kSecReturnRef: true,
]
var item: CFTypeRef?
let statusSearch = SecItemCopyMatching(getquery as CFDictionary, &item)
guard isStatusOK(statusSearch, context: "Searching identity") else {
return nil
}
let identity = item as! SecIdentity
print ("Identity is:\(identity)")
return identity
}