I'm trying to get sha384 with ECDSA keys. I'm following this article but I changed RSA to EC, of course.
The keys are successfully generated, but encryptMessageWithPublicKey()
doesn't work :(
Variable "status" in method encryptMessageWithPublicKey()
returns -50 ...
Here is my code:
import UIKit
import Security
private let _singletonInstance = AsymmetricCryptoManager()
private let kAsymmetricCryptoManagerApplicationTag = "com.AsymmetricCrypto.keypair"
private let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeEC//kSecAttrKeyTypeEC
private let kAsymmetricCryptoManagerKeySize = 384
// private key parameters
let privateKeyParams: [String: AnyObject] = [
kSecAttrIsPermanent as String: true as AnyObject,
kSecAttrApplicationTag as String: "com.AsymmetricCrypto.keypair" as AnyObject
]
// private key parameters
let publicKeyParams: [String: AnyObject] = [
kSecAttrIsPermanent as String: true as AnyObject,
kSecAttrApplicationTag as String: "com.AsymmetricCrypto.keypair" as AnyObject
]
enum AsymmetricCryptoException: Error {
case unknownError
case duplicateFoundWhileTryingToCreateKey
case keyNotFound
case authFailed
case unableToAddPublicKeyToKeyChain
case wrongInputDataFormat
case unableToEncrypt
case unableToDecrypt
case unableToSignData
case unableToVerifySignedData
case unableToPerformHashOfData
case unableToGenerateAccessControlWithGivenSecurity
case outOfMemory
}
class AsymmetricCryptoManager: NSObject {
/** Shared instance */
class var sharedInstance: AsymmetricCryptoManager {
return _singletonInstance
}
func createSecureKeyPair(_ completion: ((_ success: Bool, _ error: AsymmetricCryptoException?) -> Void)? = nil) {
// private key parameters
let privateKeyParams: [String: AnyObject] = [
kSecAttrIsPermanent as String: true as AnyObject,
kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag as AnyObject
]
// private key parameters
let publicKeyParams: [String: AnyObject] = [
kSecAttrIsPermanent as String: true as AnyObject,
kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag as AnyObject
]
// global parameters for our key generation
let parameters: [String: AnyObject] = [
kSecAttrKeyType as String: kAsymmetricCryptoManagerKeyType,
kSecAttrKeySizeInBits as String: kAsymmetricCryptoManagerKeySize as AnyObject,
kSecPublicKeyAttrs as String: publicKeyParams as AnyObject,
kSecPrivateKeyAttrs as String: privateKeyParams as AnyObject,
]
// asynchronously generate the key pair and call the completion block
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in
var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)
if status == errSecSuccess {
DispatchQueue.main.async(execute: { completion?(true, nil) })
} else {
var error = AsymmetricCryptoException.unknownError
switch (status) {
case errSecDuplicateItem: error = .duplicateFoundWhileTryingToCreateKey
case errSecItemNotFound: error = .keyNotFound
case errSecAuthFailed: error = .authFailed
default: break
}
DispatchQueue.main.async(execute: { completion?(false, error) })
}
}
}
private func getPublicKeyReference() -> SecKey? {
let parameters = [
kSecClass as String: kSecClassKey,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecReturnRef as String: true,
] as [String : Any]
var ref: AnyObject?
let status = SecItemCopyMatching(parameters as CFDictionary, &ref)
if status == errSecSuccess { return ref as! SecKey? }
else { return nil }
}
func encryptMessageWithPublicKey(_ message: String, completion: @escaping (_ success: Bool, _ data: Data?, _ error: AsymmetricCryptoException?) -> Void) {
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in
if let publicKeyRef = self.getPublicKeyReference() {
// prepare input input plain text
guard let messageData = message.data(using: String.Encoding.utf8) else {
completion(false, nil, .wrongInputDataFormat)
return
}
let plainText = (messageData as NSData).bytes.bindMemory(to: UInt8.self, capacity: messageData.count)
let plainTextLen = messageData.count
// prepare output data buffer
var cipherData = Data(count: SecKeyGetBlockSize(publicKeyRef))
let cipherText = cipherData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8> in
return bytes
})
var cipherTextLen = cipherData.count
let status = SecKeyEncrypt(publicKeyRef, .PKCS1SHA384, plainText, plainTextLen, cipherText, &cipherTextLen)
// analyze results and call the completion in main thread
DispatchQueue.main.async(execute: { () -> Void in
completion(status == errSecSuccess, cipherData, status == errSecSuccess ? nil : .unableToEncrypt)
cipherText.deinitialize()
})
return
} else {
DispatchQueue.main.async(execute: { completion(false, nil, .keyNotFound) }) }
}
}
func decryptMessageWithPrivateKey(_ encryptedData: Data, completion: @escaping (_ success: Bool, _ result: String?, _ error: AsymmetricCryptoException?) -> Void) {
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in
if let privateKeyRef = self.getPublicKeyReference() {
// prepare input input plain text
let encryptedText = (encryptedData as NSData).bytes.bindMemory(to: UInt8.self, capacity: encryptedData.count)
let encryptedTextLen = encryptedData.count
// prepare output data buffer
var plainData = Data(count: 1024)
let plainText = plainData.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8> in
return bytes
})
var plainTextLen = plainData.count
let status = SecKeyDecrypt(privateKeyRef, .PKCS1SHA384, encryptedText, encryptedTextLen, plainText, &plainTextLen)
// analyze results and call the completion in main thread
DispatchQueue.main.async(execute: { () -> Void in
if status == errSecSuccess {
// adjust NSData length
plainData.count = plainTextLen
// Generate and return result string
if let string = NSString(data: plainData as Data, encoding: String.Encoding.utf8.rawValue) as String? {
completion(true, string, nil)
} else { completion(false, nil, .unableToDecrypt) }
} else { completion(false, nil, .unableToDecrypt) }
plainText.deinitialize()
})
return
} else { DispatchQueue.main.async(execute: { completion(false, nil, .keyNotFound) }) }
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
AsymmetricCryptoManager.sharedInstance.createSecureKeyPair({ success, err in
if success {
AsymmetricCryptoManager.sharedInstance.encryptMessageWithPublicKey("thisShouldWork", completion: { (success, data, error) in
print(success)
})
}
})
}
}