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()
}
}
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)!)
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))
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())")