'AES encryption in swift
I'm trying to implement AES encryption in swift. The encryption decryption for Android and C# is working properly. I need to implement it in swift. It's current code for android and C# is followed by this.
I tried to use
But none of it work. When I send the encrypted string on server it's not been decrypted.
Any help will be appreciated
Solution 1:[1]
Swift 5
I refactored @ingconti 's code.
import Foundation
import CommonCrypto
struct AES {
// MARK: - Value
// MARK: Private
private let key: Data
private let iv: Data
// MARK: - Initialzier
init?(key: String, iv: String) {
guard key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES256, let keyData = key.data(using: .utf8) else {
debugPrint("Error: Failed to set a key.")
return nil
}
guard iv.count == kCCBlockSizeAES128, let ivData = iv.data(using: .utf8) else {
debugPrint("Error: Failed to set an initial vector.")
return nil
}
self.key = keyData
self.iv = ivData
}
// MARK: - Function
// MARK: Public
func encrypt(string: String) -> Data? {
return crypt(data: string.data(using: .utf8), option: CCOperation(kCCEncrypt))
}
func decrypt(data: Data?) -> String? {
guard let decryptedData = crypt(data: data, option: CCOperation(kCCDecrypt)) else { return nil }
return String(bytes: decryptedData, encoding: .utf8)
}
func crypt(data: Data?, option: CCOperation) -> Data? {
guard let data = data else { return nil }
let cryptLength = data.count + key.count
var cryptData = Data(count: cryptLength)
var bytesLength = Int(0)
let status = cryptData.withUnsafeMutableBytes { cryptBytes in
data.withUnsafeBytes { dataBytes in
iv.withUnsafeBytes { ivBytes in
key.withUnsafeBytes { keyBytes in
CCCrypt(option, CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), keyBytes.baseAddress, key.count, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength)
}
}
}
}
guard Int32(status) == Int32(kCCSuccess) else {
debugPrint("Error: Failed to crypt data. Status \(status)")
return nil
}
cryptData.removeSubrange(bytesLength..<cryptData.count)
return cryptData
}
}
Use like this
let password = "UserPassword1!"
let key128 = "1234567890123456" // 16 bytes for AES128
let key256 = "12345678901234561234567890123456" // 32 bytes for AES256
let iv = "abcdefghijklmnop" // 16 bytes for AES128
let aes128 = AES(key: key128, iv: iv)
let aes256 = AES(key: key256, iv: iv)
let encryptedPassword128 = aes128?.encrypt(string: password)
aes128?.decrypt(data: encryptedPassword128)
let encryptedPassword256 = aes256?.encrypt(string: password)
aes256?.decrypt(data: encryptedPassword256)
Results
Solution 2:[2]
Based on @zaph great answer, I create this Playground for:
Swift 5
import Foundation
import CommonCrypto
protocol Cryptable {
func encrypt(_ string: String) throws -> Data
func decrypt(_ data: Data) throws -> String
}
struct AES {
private let key: Data
private let ivSize: Int = kCCBlockSizeAES128
private let options: CCOptions = CCOptions(kCCOptionPKCS7Padding)
init(keyString: String) throws {
guard keyString.count == kCCKeySizeAES256 else {
throw Error.invalidKeySize
}
self.key = Data(keyString.utf8)
}
}
extension AES {
enum Error: Swift.Error {
case invalidKeySize
case generateRandomIVFailed
case encryptionFailed
case decryptionFailed
case dataToStringFailed
}
}
private extension AES {
func generateRandomIV(for data: inout Data) throws {
try data.withUnsafeMutableBytes { dataBytes in
guard let dataBytesBaseAddress = dataBytes.baseAddress else {
throw Error.generateRandomIVFailed
}
let status: Int32 = SecRandomCopyBytes(
kSecRandomDefault,
kCCBlockSizeAES128,
dataBytesBaseAddress
)
guard status == 0 else {
throw Error.generateRandomIVFailed
}
}
}
}
extension AES: Cryptable {
func encrypt(_ string: String) throws -> Data {
let dataToEncrypt = Data(string.utf8)
let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128
var buffer = Data(count: bufferSize)
try generateRandomIV(for: &buffer)
var numberBytesEncrypted: Int = 0
do {
try key.withUnsafeBytes { keyBytes in
try dataToEncrypt.withUnsafeBytes { dataToEncryptBytes in
try buffer.withUnsafeMutableBytes { bufferBytes in
guard let keyBytesBaseAddress = keyBytes.baseAddress,
let dataToEncryptBytesBaseAddress = dataToEncryptBytes.baseAddress,
let bufferBytesBaseAddress = bufferBytes.baseAddress else {
throw Error.encryptionFailed
}
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCEncrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES), // alg: CCAlgorithm
options, // options: CCOptions
keyBytesBaseAddress, // key: the "password"
key.count, // keyLength: the "password" size
bufferBytesBaseAddress, // iv: Initialization Vector
dataToEncryptBytesBaseAddress, // dataIn: Data to encrypt bytes
dataToEncryptBytes.count, // dataInLength: Data to encrypt size
bufferBytesBaseAddress + ivSize, // dataOut: encrypted Data buffer
bufferSize, // dataOutAvailable: encrypted Data buffer size
&numberBytesEncrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw Error.encryptionFailed
}
}
}
}
} catch {
throw Error.encryptionFailed
}
let encryptedData: Data = buffer[..<(numberBytesEncrypted + ivSize)]
return encryptedData
}
func decrypt(_ data: Data) throws -> String {
let bufferSize: Int = data.count - ivSize
var buffer = Data(count: bufferSize)
var numberBytesDecrypted: Int = 0
do {
try key.withUnsafeBytes { keyBytes in
try data.withUnsafeBytes { dataToDecryptBytes in
try buffer.withUnsafeMutableBytes { bufferBytes in
guard let keyBytesBaseAddress = keyBytes.baseAddress,
let dataToDecryptBytesBaseAddress = dataToDecryptBytes.baseAddress,
let bufferBytesBaseAddress = bufferBytes.baseAddress else {
throw Error.encryptionFailed
}
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
options, // options: CCOptions
keyBytesBaseAddress, // key: the "password"
key.count, // keyLength: the "password" size
dataToDecryptBytesBaseAddress, // iv: Initialization Vector
dataToDecryptBytesBaseAddress + ivSize, // dataIn: Data to decrypt bytes
bufferSize, // dataInLength: Data to decrypt size
bufferBytesBaseAddress, // dataOut: decrypted Data buffer
bufferSize, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw Error.decryptionFailed
}
}
}
}
} catch {
throw Error.encryptionFailed
}
let decryptedData: Data = buffer[..<numberBytesDecrypted]
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw Error.dataToStringFailed
}
return decryptedString
}
}
do {
let aes = try AES(keyString: "FiugQTgPNwCWUY,VhfmM4cKXTLVFvHFe")
let stringToEncrypt: String = "please encrypt meeee"
print("String to encrypt:\t\t\t\(stringToEncrypt)")
let encryptedData: Data = try aes.encrypt(stringToEncrypt)
print("String encrypted (base64):\t\(encryptedData.base64EncodedString())")
let decryptedData: String = try aes.decrypt(encryptedData)
print("String decrypted:\t\t\t\(decryptedData)")
} catch {
print("Something went wrong: \(error)")
}
Output:
I also created a Swift Package based on it:
Solution 3:[3]
my two cents:
swift 4 / xcode 9 extension for Data:
extension Data{
func aesEncrypt( keyData: Data, ivData: Data, operation: Int) -> Data {
let dataLength = self.count
let cryptLength = size_t(dataLength + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let options = CCOptions(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
self.withUnsafeBytes {dataBytes in
ivData.withUnsafeBytes {ivBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(operation),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
ivBytes,
dataBytes, dataLength,
cryptBytes, cryptLength,
&numBytesEncrypted)
}
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
}
func testAES() -> Bool {
let message = "secret message"
let key = "key890123456"
let ivString = "abcdefghijklmnop" // 16 bytes for AES128
let messageData = message.data(using:String.Encoding.utf8)!
let keyData = key.data(using: .utf8)!
let ivData = ivString.data(using: .utf8)!
let encryptedData = messageData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCEncrypt)
let decryptedData = encryptedData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCDecrypt)
let decrypted = String(bytes:decryptedData, encoding:String.Encoding.utf8)!
return message == decrypted
}
Solution 4:[4]
I have used CryptoSwift.
First I have install cryptoSwift in the pod file. Then in my view controller I have import CryptoSwift.
Here is the code that I have used:
let value = "xyzzy". // This is the value that we want to encrypt
let key = "abc". // This is the key
let EncryptedValue = try! value.aesEncrypt(key: key)
let DecryptedValue = try! EncryptedValue.aesDecrypt(key: key)
Then, using String extension:
extension String {
func aesEncrypt(key: String) throws -> String {
var result = ""
do {
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: .ECB, padding: .pkcs5) // AES128 .ECB pkcs7
let encrypted = try aes.encrypt(Array(self.utf8))
result = encrypted.toBase64()!
print("AES Encryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
func aesDecrypt(key: String) throws -> String {
var result = ""
do {
let encrypted = self
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: .ECB, padding: .pkcs5) // AES128 .ECB pkcs7
let decrypted = try aes.decrypt(Array(base64: encrypted))
result = String(data: Data(decrypted), encoding: .utf8) ?? ""
print("AES Decryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
}
In this I have not used iv and encrypted.toBase64() to encrypt like result = encrypted.toBase64()! in place of result = encrypted.toStringHex()! in encryption
and similar in decryption let decrypted = try aes.decrypt(Array(base64: encrypted)) in place of let decrypted = try aes.decrypt(Array(Hex: encrypted))
Solution 5:[5]
This is easy to use the extension and also supported all platforms for decryption as well.
import CryptoSwift
extension String {
func aesEncrypt() throws -> String {
do {
let encrypted = try AES(key: "your_encryption_key", iv: "your_iv", padding: .pkcs7).encrypt([UInt8](self.data(using: .utf8)!))
return Data(encrypted).base64EncodedString()
} catch {
}
return ""
}
func aesDecrypt() throws -> String {
do {
guard let data = Data(base64Encoded: self) else { return "" }
let decrypted = try AES(key: "your_encryption_key", iv: "your_iv", padding: .pkcs7).decrypt([UInt8](data))
return decrypted
} catch {
}
return ""
}
}
Solution 6:[6]
Found a nice library named RNCryptor implemented in swift language for AES encryption/ decryption.
Installation can be done with Cocoapods or Carthage. Here is the sample code for encryption and decryption.
// Encryption
let data = "sample data string".data(using: String.Encoding.utf8)
let password = "Secret password"
let encryptedData = RNCryptor.encrypt(data: data, withPassword: password)
// Decryption
do {
let originalData = try RNCryptor.decrypt(data: encryptedData, withPassword: password)
// ...
} catch {
print(error)
}
Solution 7:[7]
For anyone who cannot transform array of bytes to a String
String(data: Data(decrypted), encoding: .utf8)
This is my example string extension
extension String {
func decryptAES(key: String, iv: String) -> String {
do {
let encrypted = self
let key = Array(key.utf8)
let iv = Array(iv.utf8)
let aes = try AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
let decrypted = try aes.decrypt(Array(hex: encrypted))
return String(data: Data(decrypted), encoding: .utf8) ?? ""
} catch {
return "Error: \(error)"
}
}
}
Solution 8:[8]
I was looking for AES encryption ECB Mode with PKC5 padding without using any pod. I found a proper way to solve my problem by gathering different information. Maybe it could be helpful for others.
Note: There is no difference between PKCS5 and PKCS7 padding.
import CommonCrypto
func encryptionAESModeECB(messageData data: Data, key: String) -> Data? {
guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let iv: String = ""
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let encryptedString = cryptData.base64EncodedString(options: .lineLength64Characters)
return encryptedString.data(using: .utf8)
} else {
return nil
}
}
func decryptionAESModeECB(messageData: Data, key: String) -> Data? {
guard let messageString = String(data: messageData, encoding: .utf8) else { return nil }
guard let data = Data(base64Encoded: messageString, options: .ignoreUnknownCharacters) else { return nil }
guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let iv: String = ""
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
return cryptData as Data
} else {
return nil
}
}
Use it like this:
let encryptedData = encryptionAESModeECB(messageData: data, key: "keyString")
let decryptedData = decryptionAESModeECB(messageData: data, key: "keyString")
Solution 9:[9]
I know this is an old question.
Since 2019 You can use CryptoKit from Apple. It introduced AES.GCM
import CryptoKit
let key = SymmetricKey(size: .bits256)
let data = Data(...)
do {
let enc = try AES.GCM.seal(data, using: key).combined!
let dec = try AES.GCM.open(try AES.GCM.SealedBox(combined: data), using: key))
} catch {...}
Also I've made a useful swift package with extension to CryptoKit to allow AES.CBC encryption (https://github.com/gal-yedidovich/CryptoExtensions)
Then, just import CBC
import CryptoKit
import CBC
let data: Data = ... //some data to encrypt
let iv: Data = ... //an initial vector
let key: SymmetricKey = ... //encryption key
//one shot crypto operation
do {
let encrypted = try AES.CBC.encrypt(data, using: key, iv: iv)
let decrypted = try AES.CBC.decrypt(encrypted, using: key, iv: iv)
} catch {...}
//using cipher
do {
let cipher = try AES.CBC.Cipher(.encrypt, using: key, iv: iv)
var enc = Data()
enc += try cipher.update(...)
enc += try cipher.update(...)
enc += try cipher.finalize()
} catch {...}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | ingconti |
| Solution 4 | aguilarpgc |
| Solution 5 | Avadh Bambhroliya |
| Solution 6 | Alex Andrews |
| Solution 7 | Khemmachart Chutapetch |
| Solution 8 | |
| Solution 9 |


