One of the requirements of a third-party SDK I am integrating with is to sign an identifier string with the identity from a p12 certificate. This code loads a p12 certificate from the application's bundle, opens the cert with the specified key, retrieves the key, signs an input string (the requestorID
) with the information in the certificate, and encodes the signed data as a base64 encoded String.
import Security
// ...
func signRequestorId(requestorID: String) -> String? {
guard let certificatePath = NSBundle.mainBundle().pathForResource("Certificate", ofType: "p12"), certificateData = NSData(contentsOfFile: certificatePath) else {
return nil
}
let certificateKey = "certificateKey"
var signedBytes: UnsafeMutablePointer<UInt8>
var privateKey: SecKeyRef?
var myReturnedCertificate: SecCertificateRef?
var signedRequestorId: String?
var status: OSStatus
let options = [kSecImportExportPassphrase as String : certificateKey]
var items: CFArrayRef?
var securityStatus = SecPKCS12Import(certificateData, options, &items)
if securityStatus == errSecSuccess {
// Unwrap optional CFArrayRef of items from the certificate
if let items = items {
// Cast CFArrayRef to Swift Array
let itemsArray = items as [AnyObject]
// Cast CFDictionaryRef as Swift Dictionary
if let myIdentityAndTrust = itemsArray.first as? [String : AnyObject] {
// Get our SecIdentityRef from the PKCS #12 blob
// TODO: Figure out how to avoid force unwrap here
// unsafeBitCast is just as bad
let outIdentity = myIdentityAndTrust[kSecImportItemIdentity as String] as! SecIdentityRef
status = SecIdentityCopyCertificate(outIdentity, &myReturnedCertificate)
if status != errSecSuccess {
print("Failed to retrieve the certificate associated with the requested identity.")
} else {
// Get the private key associated with our identity
status = SecIdentityCopyPrivateKey(outIdentity, &privateKey)
if status != errSecSuccess {
print("Failed to extract the private key from the keystore.")
} else {
// Retrieve the digital signature and sign the requestor
// Unwrap privateKey from optional SecKeyRef
if let privateKey = privateKey {
// Get the maximum size of the digital signature
var signedBytesSize: size_t = SecKeyGetBlockSize(privateKey)
// alloc a buffer to hold the signature
// TODO: See if we can use a Swift Array here instead of C types
signedBytes = UnsafeMutablePointer<UInt8>.alloc(signedBytesSize * strideof(UInt8.self))
memset(signedBytes, 0x0, signedBytesSize)
// We're calling alloc here, so we need to destroy and deinit
defer {
signedBytes.destroy()
signedBytes.dealloc(signedBytesSize * strideof(UInt8.self))
}
// Sign data
if let requestorData = requestorID.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
// Generate a digital signature for our requestor from our cert
securityStatus = SecKeyRawSign(privateKey, SecPadding.PKCS1, UnsafePointer(requestorData.bytes), requestorData.length, signedBytes, &signedBytesSize)
if securityStatus == errSecSuccess {
let encryptedBytes = NSData(bytes: signedBytes, length: signedBytesSize)
signedRequestorId = encryptedBytes.base64EncodedStringWithOptions([])
} else {
print("Cannot sign the device id info: failed obtaining the signed bytes.")
}
}
}
}
}
}
}
} else {
print("Cannot sign the device id info: failed importing keystore.")
}
return signedRequestorId
}
I put in a few comments to walk through what was going on for my team. They are not familiar with Swift and even less familiar with low level programming with CoreFoundation. I struggled to wrap my head on exactly what is going on when I first wrote this code, so please let me know if the comments can be improved for clarity.
I'm able to use Swift types in place of CoreFoundation types (
CFDictionaryRef
,CFArrayRef
, etc.), but how do I retrieve a CoreFoundation type from a Swift dictionary without force-unwrapping? SpecificallymyIdentityAndTrust[kSecImportItemIdentity as String] as! SecIdentityRef
.I
alloc
a buffer to hold the signed bytes, but I'm sure I can use a Swift array instead thanks to Martin's great answer on a different question of mine about AES-128 bit encryption.I'm not too happy with the
if-else
structure of this code. Is it bad practice to reuse theOSStatus
variable like this? What other options are there to structure my code?