Hi,
I'm building on macOS Ventura 13.0.1, usingApple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51) and targetting .macOS(.v10_15).
I'm experiencing a crash / exception (illegal hardware instruction) when trying to cast a SecKeychainItem into a SecCertificate. This should be safe according to the docs:
A SecKeychainItem object for a certificate that is stored in a keychain can be safely cast to a SecCertificate for use with Certificate, Key, and Trust Services.
I'm trying to load PKCS12 (PEM-wrapped) data to obtain a SecIdentity from the contained certificate.
To do this I'm loading in an array of SecKeychainItems via SecItemImport.
I can see the resulting output CFArray populated with 2 items as expected. The following is the resulting dump:
▿ Optional(<__NSArrayM 0x600001270210>(
<cert(0x7f804f906fa0) s: Just a test certificate>,
<SecCDSAKeyRef 0x6000012792f0: algorithm id: 1, class=1, algorithm=2a, usage=80000000 attrs=38>
)
)
I extract the SecKeychainItem whose type is a certifcate by finding the item where CFGetTypeID($0) == SecCertificateGetTypeID().
The following is my code:
private static func protection(
_ passphrase: String
) -> SecItemImportExportKeyParameters {
SecItemImportExportKeyParameters(
version: UInt32(bitPattern: SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION),
flags: .noAccessControl,
passphrase: Unmanaged<AnyObject>.passUnretained(passphrase as AnyObject),
alertTitle: nil,
alertPrompt: nil,
accessRef: nil,
keyUsage: nil,
keyAttributes: nil
)
}
static func load(pkcs12Data: Data, passphrase: String = "") throws -> SecIdentity {
// setup the import parameters
let data = pkcs12Data as CFData
var protection = Self.protection(passphrase)
var externalFormat: SecExternalFormat = .formatPEMSequence
var externalItemType: SecExternalItemType = .itemTypeAggregate
var result: CFArray?
// import the results
SecItemImport(
data,
nil,
&externalFormat,
&externalItemType,
.pemArmour,
&protection,
nil,
&result
)
// cast results to an array of SecKeychainItems
guard let items = result as? [SecKeychainItem] else {
print("Failed to cast result to [SecKeychainItem]")
fatalError()
}
// extract the certificate result
guard let certItem = items.first(where: { CFGetTypeID($0) == SecCertificateGetTypeID() }) else {
print("Failed to extract certItem from items")
fatalError()
}
// force cast because SecKeychainItem cast will always succeed according to documentation
// the next line crashes with the exception "illegal hardware instruction"
print(certItem)
let certificate = certItem as! SecCertificate
print("Loading identity from cert:")
var identity: SecIdentity?
let identityImportStatus = SecIdentityCreateWithCertificate(nil, certificate, &identity)
guard identityImportStatus == errSecSuccess,
let identity = identity else {
print("Failed to create identity. Status: \(identityImportStatus)")
fatalError()
}
print("Loaded identity: \(identity)")
return identity
}
I'd really appreciate any insight or help understanding why the cast (let certificate = certItem as! SecCertificate) causes an exception / crash?
Am I doing something wrong here? How should I obtain the SecCertificate?
Many thanks for reading this :)
Selecting any option will automatically load the page