How to encrypt/decrypt with AES-GCM using CryptoKit in Swift

Takuya Matsuyama - Jan 26 '21 - - Dev Community

I'm developing a native module for React Native that allows you to encrypt/decrypt data with AES-GCM for my Markdown note-taking app. Here is a working memo.

CryptoKit is available from iOS 13.0.

Convert hexadecimal strings

First, you need to extend Data class to deal with hexadecimal strings:

public extension Data {
    init?(hexString: String) {
      let len = hexString.count / 2
      var data = Data(capacity: len)
      var i = hexString.startIndex
      for _ in 0..<len {
        let j = hexString.index(i, offsetBy: 2)
        let bytes = hexString[i..<j]
        if var num = UInt8(bytes, radix: 16) {
          data.append(&num, count: 1)
        } else {
          return nil
        }
        i = j
      }
      self = data
    }
    /// Hexadecimal string representation of `Data` object.
    var hexadecimal: String {
        return map { String(format: "%02x", $0) }
            .joined()
    }
}
Enter fullscreen mode Exit fullscreen mode

Load key

A key is given in hex string. The key length is 256 bits.

import CryptoKit

let keyStr = "d5a423f64b607ea7c65b311d855dc48f36114b227bd0c7a3d403f6158a9e4412"
let key = SymmetricKey(data: Data(hexString:keyStr)!)
Enter fullscreen mode Exit fullscreen mode

Decrypt

An encrypted data comes with a ciphertext, an initialization vector (a.k.a. nonce), and an auth tag.

let ciphertext = Data(base64Encoded: "LzpSalRKfL47H5rUhqvA")
let nonce = Data(hexString: "131348c0987c7eece60fc0bc") // = initialization vector
let tag = Data(hexString: "5baa85ff3e7eda3204744ec74b71d523")
let sealedBox = try! AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: nonce!),
                                       ciphertext: ciphertext!,
                                       tag: tag!)

let decryptedData = try! AES.GCM.open(sealedBox, using: key)
print(String(decoding: decryptedData, as: UTF8.self))
Enter fullscreen mode Exit fullscreen mode

Encrypt

let plainData = "This is a plain text".data(using: .utf8)
let sealedData = try! AES.GCM.seal(plainData!, using: key, nonce: AES.GCM.Nonce(data:nonce!))
let encryptedContent = try! sealedData.combined!
print("Nonce: \(sealedData.nonce.withUnsafeBytes { Data(Array($0)).hexadecimal })")
print("Tag: \(sealedData.tag.hexadecimal)")
print("Data: \(sealedData.ciphertext.base64EncodedString())")
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .