Hello, fellow tech developers, Leo here. Do you ever consider studying or using Cryptography Asymmetric Keys in Swift? My hope is that this article helps you on solving your cryptography problems. Having cryptographic problems is not easy, so let’s try to make that a little bit more intuitive today.

This week we’ll explore some old apple frameworks to create and export asymmetric keys. This is particularly important because eventually you will need to secure communication with some servers and asymmetric keys are a common and safe way to do that.

If you don’t know what asymmetric keys you need to check this first, and then continue reading.

No more talking, let’s code.

The problem – Cryptography Asymmetric Keys in Swift

I need to receive some information from other server that only my app could read it.

So in general terms, we will:

  1. Create a private key
  2. Add that private key to the keychain
  3. Generate a public key
  4. Cryptograph some data with the public key
  5. Decrypt with the private key

Check the steps below and how to do that in Swift.

If you're a mid/senior iOS developer looking to improve your skills and salary level, join this 100% free online crash course. It's available only until April 28th, so click to get it now!

 

Create Private Key

First thing is to create a private key. Check the code below:

let attributes: CFDictionary =
[kSecAttrKeyType as String: kSecAttrKeyTypeRSA, // 1
 kSecAttrKeySizeInBits as String: 2048, // 2
 kSecPrivateKeyAttrs as String:
    [kSecAttrIsPermanent as String: true, // 3
     kSecAttrApplicationTag as String: tag ] // 4
] as CFDictionary

var error: Unmanaged<CFError>?

do {
    guard SecKeyCreateRandomKey(attributes, &error) != nil else { // 5
        throw error!.takeRetainedValue() as Error
    }
    print("key created")
    return true
} catch {
    print(error.localizedDescription)
    return false
}

All the code above is counterintuitive so let’s explain what’s going on.

At (1) we are specifying the key type, in this case, is a [RSA](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) key and the (2) is just the key size, it’s important to remember that the key size shouldn’t be small because this weakens the whole purpose of cryptography things. With the actual 2020 computational power, a 2048 key would take around 300 trillion years to break, so we can surely say it’s safe.

The (3) and (4) are attributes of the private key we’re creating. On the (3) mark we are telling the method to guard the key created in the keychain this way the private key is strongly hardware protected. And the (4) we are giving a tag to later we can retrieve it.

The (5) SecKeyCreateRandomKey is a function that does the magic for us. This function returns a SecKey, if you want to guard anywhere else, you should get the return of it, in this case, we are storing it in Apple’s keychain.

In Swift we don’t need to generate the public key at the moment we create the private key because we can create just when we need it.

 

Get the private key from Keychain

To get the private key from the keychain it’s like getting any other item of it. We first need to create a query and use the *SecItemCopyMatching* to retrieve the secKey like this:

let query: CFDictionary = [kSecClass as String: kSecClassKey, //1
                           kSecAttrApplicationTag as String: tag, // 2
                           kSecAttrKeyType as String: kSecAttrKeyTypeRSA, //3
                           kSecReturnRef as String: true] as CFDictionary //4

var item: CFTypeRef?
let status = SecItemCopyMatching(query, &item) //5
guard status == errSecSuccess else {
    print("keychain don't have private key")
    return nil
}

privateKey = item as! SecKey //6

The (1,2,3,4) marks are the properties of the query that we are searching in the keychain. The (1) is the class of the item and it’s a key. The (2) is the tag we set at the moment we created the private key. (3) mark is the keyType and the (4) we’re telling to a keychain to return a reference of that object to us.

 

Generate public key

To generate the public is somehow “easy” but it’s important you pay attention to the details.

guard let publicKey = SecKeyCopyPublicKey(privateKey), //1
      let data = SecKeyCopyExternalRepresentation(publicKey, nil) else { //2
    return nil
}

guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, .rsaEncryptionPKCS1) //3
else {
    print("not supported cryptography")
    return nil
}

The (1) mark is creating a SecKey object, to use inside Swift’s code and it’s not exportable. Therefore we need the (2) instructions to create an exportable copy of the key that we can export ( for example for a PHP server).

It’s important always export in a format data you won’t lose data, example exporting transforming the Data to Base64 format you can do this:

let finalData = data as Data //4

return finalData.base64EncodedString()

A simple base64 would be fine to achieve that.

 

The complete example of Cryptography Asymmetric Keys in Swift

The below shows how to create asymmetric keys and also how to perform an encrypt and decrypt operation with them.

If you're a mid/senior iOS developer looking to improve your skills and salary level, join this 100% free online crash course. It's available only until April 28th, so click to get it now!

let textToEncrypt = "Swift is awesome"
print("Crypto - text to encrypt - \(textToEncrypt)")
let tag = "exampleTag"
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512


let attributes: CFDictionary =
[kSecAttrKeyType as String: kSecAttrKeyTypeRSA, // 1
 kSecAttrKeySizeInBits as String: 2048, // 2
 kSecPrivateKeyAttrs as String:
    [kSecAttrIsPermanent as String: true, // 3
     kSecAttrApplicationTag as String: tag] // 4
] as CFDictionary

var error: Unmanaged<CFError>?

do {
    guard SecKeyCreateRandomKey(attributes, &error) != nil else { // 5
        throw error!.takeRetainedValue() as Error
    }
    print("key created")
} catch {
    print(error.localizedDescription)
}

let query: CFDictionary = [kSecClass as String: kSecClassKey, //1
                           kSecAttrApplicationTag as String: tag, // 2
                           kSecAttrKeyType as String: kSecAttrKeyTypeRSA, //3
                           kSecReturnRef as String: true] as CFDictionary //4

var item: CFTypeRef?
let status = SecItemCopyMatching(query, &item) //5
guard status == errSecSuccess else {
    print("keychain don't have private key")
    return
}

let privateKey = item as! SecKey //6

guard let publicKey = SecKeyCopyPublicKey(privateKey), //1
      let publicKeyExportable = SecKeyCopyExternalRepresentation(publicKey, nil) else { //2
    return
}

//check if the public key encrypt data
guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) //3
else {
    print("not supported cryptography")
    return
}

let publicKeyBase64Encoded = (publicKeyExportable as Data).base64EncodedString()
print("Crypto - public key export = \(publicKeyBase64Encoded)")

// the keys created we can now perform a example cryptograph operation

let textToEncryptData = textToEncrypt.data(using: .utf8)!

guard let cipherText = SecKeyCreateEncryptedData(publicKey,
                                                 algorithm,
                                                 textToEncryptData as CFData,
                                                 &error) as Data? else {
    return
}

print("Crypto - encrypted text \(cipherText.base64EncodedString())")

// check if the private key can decrypt
guard SecKeyIsAlgorithmSupported(privateKey, .decrypt, algorithm) else {
    return
}

//check if the text size is compatible with the key size
guard cipherText.count == SecKeyGetBlockSize(privateKey) else {
    return
}

//perform decrypt, the return is Data
guard let clearTextData = SecKeyCreateDecryptedData(privateKey,
                                                    algorithm,
                                                    cipherText as CFData,
                                                    &error) as Data? else {
    return
}

guard let clearText = String(data: clearTextData, encoding: .utf8) else { return }
print("Crypto - decrypted text [\(clearText)]")

And we are almost done, just a last-minute disclosure.

 

Very important – Last-minute disclosure of Cryptography Asymmetric Keys in Swift

If your server is having problems reading your public key, you must read about public key formats like DER/PEM/etc. Java and PHP actually default read in DER format but Swift doesn’t create public key data in that format, so you WILL have to export to DER format. Just search for that.

 

Continue Studying Cryptography

Another interesting kind of cryptography is the HMAC, one of the key-derived function cryptography and you have all the explanations and a tutorial about HMAC in Swift in this article.

It is important to understand how cryptographic keys can be used to secure messages and avoid man-in-the-middle attacks. Check this article that explains all about cryptographic keys.

 

Summary

Today we learned how to create RSA asymmetric keys to encrypt, decrypt, and a method to export the public to send to others servers. I hope you enjoy it as much as I enjoyed writing and any thoughts or feedback are always welcome.

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for the reading and… That’s all folks.

Credits – image