AES256 CBC en Swift I Por Tomás Rolón

Bueno, como desarrollador he tenido bastantes problemas buscando documentación o soluciones sobre diferentes temas a lo largo del tiempo. Se podría decir que aquí está plasmado mi último problema.

Primeramente aclaro que este será un ejemplo de como podemos desarrollar una herramienta que nos sirva para encriptar y desencriptar datos.

Librerías: CommonCrypto Foundation.

En primera instancia extenderemos las funciones de la clase Data:

import Foundation
import CommonCrypto

extension Data {
func sha256() -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(self.count), &hash)
}

return Data(hash)
}

func toHexString() -> String {
return self.map { String(format: "%02hhx", $0) }.joined()
}
}

Extendemos las funcionalidades solo para poder hacer uso de las funciones de sha256 y toHexString.

sha256 nos calcula el hash SHA-256 para los datos a los cuales lo aplicamos. Devuelve también un Data con el hash.

toHexString nos devuelve un String en el formato hexadecimal.

Usaremos estas funciones para armar nuestro vector de inicio.

Acto seguido pasaremos a crear nuestra clase CryptoHelper.

class CryptoHelper {
let APP_KEY = "YOUR_BASE64_KEY" // base64 key
let keyLength = kCCKeySizeAES256 // con un length de 32
let ivLength = kCCBlockSizeAES128 // con un length de 16

private func getKey() -> Data {
return APP_KEY.data(using: .utf8)
}
}

Y ahora pasaremos a crear nuestras funciones para encriptar y desencriptar:

func encrypt(data: String) -> String? {
// Obtenemos la key:
let keyData = getKey()

let hashData = keyData.sha256() // aplicamos la extensión que agregamos antes.
let hashHex = hashData.toHexString() // Obtenemos los datos como un String en hexadecimal

// Vector de inicio:
let ivData = String(hashHex.prefix(ivLength)).data(using: .utf8)

guard let dataToEncrypt = data.data(using: .utf8) else {
print("ERROR AL CONVERTIR A DATA")
return nil
}

let bufferSize = dataToEncrypt.count + ivLength
var encryptedData = Data(count: bufferSize)

var numBytesEncrypted: size_t = 0
let cryptStatus = encryptedData.withUnsafeMutableBytes { encryptedBytes in
dataToEncrypt.withUnsafeBytes { dataBytes in
keyData.withUnsafeBytes { keyBytes in
ivData.withUnsafeBytes { ivBytes in
CCCrypt(
CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
keyBytes.baseAddress, keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress, dataToEncrypt.count,
encryptedBytes.baseAddress, bufferSize,
&numBytesEncrypted
)
}
}
}
}

// en caso de que todo haya ido bien deberíamos de llegar a esta parte
if cryptStatus == kCCSuccess {
encryptedData.removeSubrange(numBytesEncrypted..<encryptedData.count)
return encryptedData.base64Encoding() // Devolvemos como base64
} else {
print("ERROR ENCRIPTANDO DATOS: \(cryptStatus)")
return nil
}
}

Realmente el método para desencriptar datos en bastante parecido al de encriptar con ligeros cambios:

func decrypt(data: String) -> String? {
let keyData = getKey()

let hash = keyData.sha256()
let ivHex = hash.toHexString().prefix(ivLength)
let ivData = String(ivHex).data(using: .utf8)!

guard let dataToDecrypt = Data(base64Encoded: data) else {
print("ERROR AL DECODIFICAR DATOS EN BASE64")
return nil
}

let bufferSize = dataToDecrypt.count + ivLength
var decryptedData = Data(count: bufferSize)

var numBytesDecrypted: size_t = 0

let cryptStatus = decryptedData.withUnsafeMutableBytes { decryptedBytes in
dataToDecrypt.withUnsafeBytes { dataBytes in
keyData.withUnsafeBytes { keyBytes in
ivData.withUnsafeBytes { ivBytes in
CCCrypt(
CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
keyBytes.baseAddress, keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress, dataToDecrypt.count,
decryptedBytes.baseAddress, bufferSize,
&numBytesDecrypted
)
}
}
}
}

if cryptStatus == kCCSuccess {
decryptedData.removeSubrange(numBytesDecrypted..<decryptedData.count)
return String(data: decryptedData, encoding: .utf8)
} else {
print("ERROR DESENCRIPTANDO DATOS: \(cryptStatus)")
return nil
}
}

El código completo nos quedaría de la siguiente manera:

import Foundation
import CommonCrypto

extension Data {
func sha256() -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(self.count), &hash)
}

return Data(hash)
}

func toHexString() -> String {
return self.map { String(format: "%02hhx", $0) }.joined()
}
}

// ============================================

class CryptoHelper {
let APP_KEY = "YOUR_BASE64_KEY" // base64 key
let keyLength = kCCKeySizeAES256 // 32
let ivLength = kCCBlockSizeAES128 // 16

private func getKey() -> Data {
return APP_KEY.data(using: .utf8)
}

func encrypt(data: String) -> String? {
// Obtenemos la key:
let keyData = getKey()

let hashData = keyData.sha256() // aplicamos la extensión que agregamos antes.
let hashHex = hashData.toHexString() // Obtenemos los datos como un String en hexadecimal

// Vector de inicio:
let ivData = String(hashHex.prefix(ivLength)).data(using: .utf8)

guard let dataToEncrypt = data.data(using: .utf8) else {
print("ERROR AL CONVERTIR A DATA")
return nil
}

let bufferSize = dataToEncrypt.count + ivLength
var encryptedData = Data(count: bufferSize)

var numBytesEncrypted: size_t = 0
let cryptStatus = encryptedData.withUnsafeMutableBytes { encryptedBytes in
dataToEncrypt.withUnsafeBytes { dataBytes in
keyData.withUnsafeBytes { keyBytes in
ivData.withUnsafeBytes { ivBytes in
CCCrypt(
CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
keyBytes.baseAddress, keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress, dataToEncrypt.count,
encryptedBytes.baseAddress, bufferSize,
&numBytesEncrypted
)
}
}
}
}

// en caso de que todo haya ido bien deberíamos de llegar a esta parte
if cryptStatus == kCCSuccess {
encryptedData.removeSubrange(numBytesEncrypted..<encryptedData.count)
return encryptedData.base64Encoding() // Devolvemos como base64
} else {
print("ERROR ENCRIPTANDO DATOS: \(cryptStatus)")
return nil
}
}

func decrypt(data: String) -> String? {
let keyData = getKey()

let hash = keyData.sha256()
let ivHex = hash.toHexString().prefix(ivLength)
let ivData = String(ivHex).data(using: .utf8)!

guard let dataToDecrypt = Data(base64Encoded: data) else {
print("ERROR AL DECODIFICAR DATOS EN BASE64")
return nil
}

let bufferSize = dataToDecrypt.count + ivLength
var decryptedData = Data(count: bufferSize)

var numBytesDecrypted: size_t = 0

let cryptStatus = decryptedData.withUnsafeMutableBytes { decryptedBytes in
dataToDecrypt.withUnsafeBytes { dataBytes in
keyData.withUnsafeBytes { keyBytes in
ivData.withUnsafeBytes { ivBytes in
CCCrypt(
CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
keyBytes.baseAddress, keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress, dataToDecrypt.count,
decryptedBytes.baseAddress, bufferSize,
&numBytesDecrypted
)
}
}
}
}

if cryptStatus == kCCSuccess {
decryptedData.removeSubrange(numBytesDecrypted..<decryptedData.count)
return String(data: decryptedData, encoding: .utf8)
} else {
print("ERROR DESENCRIPTANDO DATOS: \(cryptStatus)")
return nil
}
}
}

Espero que esto sea de ayuda para alguien que este pasando por lo mismo o que haya pasado por lo mismo en cuestión.

Scroll al inicio