1. Get keys from keychain if available. (SecItemCopyMatching)
2. Remove keys in keychain. (SecItemDelete)
3. Generate an RSA private/public key pair and store in keychain. (SecKeyGeneratePair)
5. Encrypt and decrypt a message with a key pair. (SecKeyEncrypt/SecKeyDecrypt)
The code below is developed with Xcode 7.2.1 (Swift 2.1.1) and tested with an iPhone and the simulator.
Just create a new Single View Application project in Xcode and modify ViewController.swift as:
import UIKit
class ViewController: UIViewController {
var publicKey, privateKey: SecKey?
let tagPrivate = "com.mycompany.tagPrivate"
let tagPublic = "com.mycompany.tagPublic"
var keySourceStr = ""
override func viewDidLoad() {
super.viewDidLoad()
if (GetKeysFromKeychain()) {
keySourceStr = "from keychain"
print("Key pair retrieved successfully!")
} else {
print("No valid key pair")
deleteAllKeysInKeyChain()
GenerateKeyPair()
keySourceStr = "newly generated"
}
//deleteAllKeysInKeyChain() //Used to clear the keychain
EncryptDecryptMessage()
}
//Check Keychain and get keys
func GetKeysFromKeychain() -> Bool {
privateKey = GetKeyTypeInKeyChain(tagPrivate)
publicKey = GetKeyTypeInKeyChain(tagPublic)
return ((privateKey != nil)&&(publicKey != nil))
}
func GetKeyTypeInKeyChain(tag : String) -> SecKey? {
let query: [String: AnyObject] = [
String(kSecClass) : kSecClassKey,
String(kSecAttrKeyType) : kSecAttrKeyTypeRSA,
String(kSecAttrApplicationTag): tag,
String(kSecReturnRef) : true
]
var result : AnyObject?
let status = SecItemCopyMatching(query, &result)
if status == errSecSuccess {
print("\(keyTypeStr(tag)) Key existed!")
return result as! SecKey?
}
print("no \(keyTypeStr(tag)) key")
return nil
}
//Generate private and public keys
func GenerateKeyPair() {
let privateKeyAttr: [NSString: AnyObject] = [
kSecAttrIsPermanent: true,
kSecAttrApplicationTag: tagPrivate
]
let publicKeyAttr: [NSString: AnyObject] = [
kSecAttrIsPermanent: true,
kSecAttrApplicationTag: tagPublic
]
let parameters: [String: AnyObject] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 1024,
kSecPrivateKeyAttrs as String: privateKeyAttr,
kSecPublicKeyAttrs as String: publicKeyAttr
]
let status = SecKeyGeneratePair(parameters, &publicKey, &privateKey)
if status != noErr {
print("SecKeyGeneratePair Error! \(status.description)")
return
}
print("Keys generated and added to keychain")
//StoreInKeychain(tagPublic, key: publicKey!)
//StoreInKeychain(tagPrivate, key: privateKey!)
}
//Store private and public keys in keychain
/*
/*
func StoreInKeychain(tag: String, key: SecKeyRef) {
let attribute = [
String(kSecClass) : kSecClassKey,
String(kSecAttrKeyType) : kSecAttrKeyTypeRSA,
String(kSecValueRef) : key,
String(kSecReturnPersistentRef): true
]
let status = SecItemAdd(attribute, nil)
if status != noErr {
print("SecItemAdd Error!")
return
}
print("\(keyTypeStr(tag)) key added to keychain")
}*/
//Delete keys when required.
func deleteAllKeysInKeyChain() {
let query : [String: AnyObject] = [
String(kSecClass) : kSecClassKey
]
let status = SecItemDelete(query)
switch status {
case errSecItemNotFound:
print("No key in keychain")
case noErr:
print("All Keys Deleted!")
default:
print("SecItemDelete error! \(status.description)")
}
}
//key type string
func keyTypeStr(tag: String) -> String {
return tag.stringByReplacingOccurrencesOfString("com.mycompany.tag", withString: "")
}
//Encrypt and decrypt message
func EncryptDecryptMessage() {
if ((publicKey == nil)||(privateKey == nil)) {
print("Key pair is invalid. Encrypting/Decrypting message not allowed!")
return
}
let message = "Raspberry Pi 3 is with Bluetooth & Wi-Fi and at US$35."
let blockSize = SecKeyGetBlockSize(publicKey!)
var messageEncrypted = [UInt8](count: blockSize, repeatedValue: 0)
var messageEncryptedSize = blockSize
var status: OSStatus!
status = SecKeyEncrypt(publicKey!, SecPadding.PKCS1, message, message.characters.count, &messageEncrypted, &messageEncryptedSize)
if status != noErr {
print("Encryption Error!")
return
}
//Decrypt the entrypted string with the private key
var messageDecrypted = [UInt8](count: blockSize, repeatedValue: 0)
var messageDecryptedSize = messageEncryptedSize
status = SecKeyDecrypt(privateKey!, SecPadding.PKCS1, &messageEncrypted, messageEncryptedSize, &messageDecrypted, &messageDecryptedSize)
if status != noErr {
print("Decryption Error!")
return
}
print("Encrypt and decrypt message successfully with keys \(keySourceStr). Message is:")
print(NSString(bytes: &messageDecrypted, length: messageDecryptedSize, encoding: NSUTF8StringEncoding)!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Results
When no keys stored in keychain:
no Private key
no Public key
No valid key pair
No key in keychain
Keys generated and added to keychain
Encrypt and decrypt message successfully with keys newly generated. Message is:
Raspberry Pi 3 is with Bluetooth & Wi-Fi and at US$35.
When keys already stored in keychain:
Private Key existed!
Public Key existed!
Key pair retrieved successfully!
Encrypt and decrypt message successfully with keys from keychain. Message is:
Raspberry Pi 3 is with Bluetooth & Wi-Fi and at US$35.
Encrypt/decrypt a string with public/private keys imported from PEM files (Swift)
OpenSSL RSA commands to encrypt/decrypt a message in terminal (Mac)
Go back to Communication between iOS device (Client) and Raspberry Pi (Server)
Hi, how i can use this code to generate EC instead of RSA ???
ReplyDeletecan i do that by replace kSecAttrKeyTypeRSA to kSecAttrKeyTypeEC
and kSecAttrKeySizeInBits as String: 1024 to kSecAttrKeySizeInBits as String: 192 ???
i search on the internet for any tutorial talking about EC but i found nothing
please help me with that ??
I hope I could answer you, but unfortunately I have no experience with EC. Can you ask in Stackoverflow or Apple Developer Forums?
DeleteHi when we create a pair keychain, the keys are store automatically in the keychain? is for it that you commented the method StoreInKeychain?
ReplyDeleteThanks
Yes. SecKeyGeneratePair() automatically stores generated keys in keychain when I called it. So SecItemAdd() is not required.
DeleteAnd Can i encrypt and decrypt a message much bigger the key size?
ReplyDeleteThanks
Yes, just try it :)
DeleteThis comment has been removed by the author.
DeleteYes I have try encrypt a messaga >= key size, but return always errSecParam
DeleteDo you have any idea why?
Thanks
Perhaps the problem is with the block size, which should be the size of the encrypted message, but not be the size of the public key:
Deletelet blockSize = SecKeyGetBlockSize(publicKey!)
var messageEncrypted = [UInt8](count: blockSize, repeatedValue: 0)
Please try define blockSize as a larger value.
Thx Enoch, very helpful. Any code sample on how to format the public Key in order to share it with a server (written in node.js) ?
ReplyDeleteHello. Please see if this helps:
DeleteCommunication between iOS device (Client) and Raspberry Pi (Server)
Hi, delete function remove all keys? Even if these keys doesn't from my app?
ReplyDeleteIt should delete keys of this app only.
DeleteThis comment has been removed by the author.
ReplyDelete