ios swift cryptography aes commoncrypto

ios - Cifrado AES en Swift



cryptography commoncrypto (7)

Estoy tratando de implementar el cifrado AES de forma rápida. El descifrado de cifrado para Android y C # funciona correctamente. Necesito implementarlo rápidamente. Es el código actual para Android y C # es seguido por esto.

Traté de usar

  1. CryptoSwift
  2. Cifrado AES multiplataforma

Pero nada de eso funciona. Cuando envío la cadena cifrada en el servidor, no se ha descifrado.

Cualquier ayuda será apreciada


Asegúrese de usar los mismos parámetros que parecen ser AES con el modo CBC con iv, PKCS5Padding (en realidad PKCS # 7) y una clave de 16 bytes (128 bits).

El relleno PKCS # 5 y el relleno PKCS#7 son esencialmente iguales, a veces por razones históricas El relleno PKCS # 5 se especifica para usar con AES pero el relleno real es PKCS # 7.

Asegúrese de que las codificaciones de la clave, iv y los datos cifrados coincidan. Hex los descarga en ambas plataformas para garantizar que sean idénticos. Las funciones de cifrado no son difíciles de usar, si todos los parámetros de entrada son correctos, la salida será correcta.

Para hacer esto más seguro, el iv debe ser bytes aleatorios y antepuestos a los datos cifrados para su uso durante el descifrado.

El cifrado AES de plataforma cruzada utiliza una clave de 256 bits, por lo que no funcionará tal cual.

Ejemplo:

Swift 2

// operation: kCCEncrypt or kCCDecrypt func testCrypt(data data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? { let cryptLength = size_t(data.count+kCCBlockSizeAES128) var cryptData = [UInt8](count:cryptLength, repeatedValue:0) let keyLength = size_t(kCCKeySizeAES128) let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128) let options: CCOptions = UInt32(kCCOptionPKCS7Padding) var numBytesEncrypted :size_t = 0 let cryptStatus = CCCrypt(CCOperation(operation), algoritm, options, keyData, keyLength, ivData, data, data.count, &cryptData, cryptLength, &numBytesEncrypted) if UInt32(cryptStatus) == UInt32(kCCSuccess) { cryptData.removeRange(numBytesEncrypted..<cryptData.count) } else { print("Error: /(cryptStatus)") } return cryptData; } let message = "Don´t try to read this text. Top Secret Stuff" let messageData = Array(message.utf8) let keyData = Array("12345678901234567890123456789012".utf8) let ivData = Array("abcdefghijklmnop".utf8) let encryptedData = testCrypt(data:messageData, keyData:keyData, ivData:ivData, operation:kCCEncrypt)! let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)! var decrypted = String(bytes:decryptedData, encoding:NSUTF8StringEncoding)! print("message: /(message)"); print("messageData: /(NSData(bytes:messageData, length:messageData.count))"); print("keyData: /(NSData(bytes:keyData, length:keyData.count))"); print("ivData: /(NSData(bytes:ivData, length:ivData.count))"); print("encryptedData: /(NSData(bytes:encryptedData, length:encryptedData.count))"); print("decryptedData: /(NSData(bytes:decryptedData, length:decryptedData.count))"); print("decrypted: /(String(bytes:decryptedData,encoding:NSUTF8StringEncoding)!)");

Salida:

message: Don´t try to read this text. Top Secret Stuff messageData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666 keyData: 31323334 35363738 39303132 33343536 37383930 31323334 35363738 39303132 ivData: 61626364 65666768 696a6b6c 6d6e6f70 encryptedData: b1b6dc17 62eaf3f8 baa1cb87 21ddc35c dee803ed fb320020 85794848 21206943 a85feb5b c8ee58fc d6fb664b 96b81114 decryptedData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666 decrypted: Don´t try to read this text. Top Secret Stuff

Swift 3 con tipo [UInt8]

func testCrypt(data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? { let cryptLength = size_t(data.count+kCCBlockSizeAES128) var cryptData = [UInt8](repeating:0, count:cryptLength) let keyLength = size_t(kCCKeySizeAES128) let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128) let options: CCOptions = UInt32(kCCOptionPKCS7Padding) var numBytesEncrypted :size_t = 0 let cryptStatus = CCCrypt(CCOperation(operation), algoritm, options, keyData, keyLength, ivData, data, data.count, &cryptData, cryptLength, &numBytesEncrypted) if UInt32(cryptStatus) == UInt32(kCCSuccess) { cryptData.removeSubrange(numBytesEncrypted..<cryptData.count) } else { print("Error: /(cryptStatus)") } return cryptData; }

Swift 3 y 4 con tipo de Data

func testCrypt(data:Data, keyData:Data, ivData:Data, operation:Int) -> Data { let cryptLength = size_t(data.count + 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 data.withUnsafeBytes {dataBytes in ivData.withUnsafeBytes {ivBytes in keyData.withUnsafeBytes {keyBytes in CCCrypt(CCOperation(operation), CCAlgorithm(kCCAlgorithmAES), options, keyBytes, keyLength, ivBytes, dataBytes, data.count, cryptBytes, cryptLength, &numBytesEncrypted) } } } } if UInt32(cryptStatus) == UInt32(kCCSuccess) { cryptData.removeSubrange(numBytesEncrypted..<cryptData.count) } else { print("Error: /(cryptStatus)") } return cryptData; } let message = "Don´t try to read this text. Top Secret Stuff" let messageData = message.data(using:String.Encoding.utf8)! let keyData = "12345678901234567890123456789012".data(using:String.Encoding.utf8)! let ivData = "abcdefghijklmnop".data(using:String.Encoding.utf8)! let encryptedData = testCrypt(data:messageData, keyData:keyData, ivData:ivData, operation:kCCEncrypt) let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt) var decrypted = String(bytes:decryptedData, encoding:String.Encoding.utf8)!

Ejemplo de la sección de documentación expirada:

Cifrado AES en modo CBC con un IV aleatorio (Swift 3+)

El iv está prefijado a los datos encriptados

aesCBC128Encrypt creará un IV aleatorio y con el prefijo del código cifrado.
aesCBC128Decrypt usará el IV prefijado durante el descifrado.

Las entradas son los datos y la clave son objetos de datos. Si es una forma codificada como Base64 si es necesario, convierta ay desde el método de llamada.

La clave debe tener exactamente 128 bits (16 bytes), 192 bits (24 bytes) o 256 bits (32 bytes) de longitud. Si se utiliza otro tamaño de clave, se generará un error.

PKCS#7 está configurado de forma predeterminada.

Este ejemplo requiere criptografía común
Es necesario tener un encabezado de puente para el proyecto:
#import <CommonCrypto/CommonCrypto.h>
Agregue Security.framework al proyecto.

Este es un ejemplo, no un código de producción.

enum AESError: Error { case KeyError((String, Int)) case IVError((String, Int)) case CryptorError((String, Int)) } // The iv is prefixed to the encrypted data func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data { let keyLength = keyData.count let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256] if (validKeyLengths.contains(keyLength) == false) { throw AESError.KeyError(("Invalid key length", keyLength)) } let ivSize = kCCBlockSizeAES128; let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128) var cryptData = Data(count:cryptLength) let status = cryptData.withUnsafeMutableBytes {ivBytes in SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes) } if (status != 0) { throw AESError.IVError(("IV generation failed", Int(status))) } var numBytesEncrypted :size_t = 0 let options = CCOptions(kCCOptionPKCS7Padding) let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in data.withUnsafeBytes {dataBytes in keyData.withUnsafeBytes {keyBytes in CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), options, keyBytes, keyLength, cryptBytes, dataBytes, data.count, cryptBytes+kCCBlockSizeAES128, cryptLength, &numBytesEncrypted) } } } if UInt32(cryptStatus) == UInt32(kCCSuccess) { cryptData.count = numBytesEncrypted + ivSize } else { throw AESError.CryptorError(("Encryption failed", Int(cryptStatus))) } return cryptData; } // The iv is prefixed to the encrypted data func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? { let keyLength = keyData.count let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256] if (validKeyLengths.contains(keyLength) == false) { throw AESError.KeyError(("Invalid key length", keyLength)) } let ivSize = kCCBlockSizeAES128; let clearLength = size_t(data.count - ivSize) var clearData = Data(count:clearLength) var numBytesDecrypted :size_t = 0 let options = CCOptions(kCCOptionPKCS7Padding) let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in data.withUnsafeBytes {dataBytes in keyData.withUnsafeBytes {keyBytes in CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES128), options, keyBytes, keyLength, dataBytes, dataBytes+kCCBlockSizeAES128, clearLength, cryptBytes, clearLength, &numBytesDecrypted) } } } if UInt32(cryptStatus) == UInt32(kCCSuccess) { clearData.count = numBytesDecrypted } else { throw AESError.CryptorError(("Decryption failed", Int(cryptStatus))) } return clearData; }

Ejemplo de uso:

let clearData = "clearData0123456".data(using:String.Encoding.utf8)! let keyData = "keyData890123456".data(using:String.Encoding.utf8)! print("clearData: /(clearData as NSData)") print("keyData: /(keyData as NSData)") var cryptData :Data? do { cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData) print("cryptData: /(cryptData! as NSData)") } catch (let status) { print("Error aesCBCEncrypt: /(status)") } let decryptData :Data? do { let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData) print("decryptData: /(decryptData! as NSData)") } catch (let status) { print("Error aesCBCDecrypt: /(status)") }

Salida de ejemplo:

clearData: <636c6561 72446174 61303132 33343536> keyData: <6b657944 61746138 39303132 33343536> cryptData: <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0> decryptData: <636c6561 72446174 61303132 33343536>

Notas:
Un problema típico con el código de ejemplo del modo CBC es que deja la creación y el uso compartido del IV aleatorio para el usuario. Este ejemplo incluye la generación del IV, prefija los datos cifrados y usa el IV prefijado durante el descifrado. Esto libera al usuario ocasional de los detalles necesarios para el modo CBC .

Por seguridad, los datos cifrados también deben tener autenticación, este código de ejemplo no proporciona eso para que sea pequeño y permita una mejor interoperabilidad para otras plataformas.

También falta la derivación de la clave de una contraseña, se sugiere que se use PBKDF2 contraseñas de texto se usan como material de claves.

Para obtener un código de cifrado multiplataforma listo para la producción sólida, consulte RNCryptor .


Basado en la gran respuesta de @zaph, creo este Playground para:

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

Salida:

También creé un paquete Swift basado en él:

https://github.com/backslash-f/aescryptable ✌🏻


Encontré una buena biblioteca llamada RNCryptor implementada en lenguaje rápido para el cifrado / descifrado AES.

La instalación se puede hacer con Cocoapods o Carthage. Aquí está el código de muestra para el cifrado y descifrado.

// 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) }


He usado CryptoSwift.

Primero tengo que instalar cryptoSwift en el archivo pod. Luego, en mi controlador de vista, importé CryptoSwift.

Aquí está el código que he usado:

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)

Luego, usando la extensión String:

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 } }

¡En esto no he usado iv y encrypted.toBase64 () para cifrar como result = encrypted.toBase64()! en lugar del result = encrypted.toStringHex()! en encriptación

y similar en descifrado let decrypted = try aes.decrypt(Array(base64: encrypted)) en lugar de let decrypted = try aes.decrypt(Array(Hex: encrypted))


Mis dos centavos:

Extensión swift 4 / xcode 9 para datos:

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 }


Para cualquiera que no pueda transformar la matriz de bytes en una Cadena

String(data: Data(decrypted), encoding: .utf8)

Este es mi ejemplo de extensión de cadena

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


Swift 4.2

Refactoré el código de @ingconti.

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 = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128).count var cryptData = Data(count: cryptLength) let keyLength = [UInt8](repeating: 0, count: kCCBlockSizeAES128).count let options = CCOptions(kCCOptionPKCS7Padding) 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), options, keyBytes, keyLength, ivBytes, dataBytes, data.count, cryptBytes, cryptLength, &bytesLength) } } } } guard UInt32(status) == UInt32(kCCSuccess) else { debugPrint("Error: Failed to crypt data. Status /(status)") return nil } cryptData.removeSubrange(bytesLength..<cryptData.count) return cryptData } }

Usar así

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)

Resultados