Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

  1. CryptoSwift
  2. Cross platform AES encryption

But none of it work. When I send the encrypted string on server it's not been decrypted.

Any help will be appreciated

like image 705
Ankita Shah Avatar asked Jun 07 '16 13:06

Ankita Shah


People also ask

How do I encrypt in Swift?

Encryption. In the extension file encryption method, first, we need to convert the given code to data using utf8 after with given key and iv parameter, we need to use initialization of AES with key, iv, and padding parameter. After that cast it to data and convert it to hexadecimalString .

What is AES 256 encryption algorithm?

The AES Encryption algorithm (also known as the Rijndael algorithm) is a symmetric block cipher algorithm with a block/chunk size of 128 bits. It converts these individual blocks using keys of 128, 192, and 256 bits. Once it encrypts these blocks, it joins them together to form the ciphertext.


1 Answers

Be sure to use the same parameters which seem to be AES with CBC mode with iv, PKCS5Padding (actually PKCS#7) padding and a 16-byte (128-bit) key.

PKCS#5 padding and PKCS#7 padding are essentially the same, sometimes for historic reasons PKCS#5 padding is specified for use with AES but the actual padding is PKCS#7.

Make sure the encodings of the key, iv and encrypted data all match. Hex dump them on both platforms to ensure they are identical. Encryption functions are not difficult to use, if all the input parameters are correct the output will be correct.

To make this more secure the iv should be random bytes and prepended to the encrypted data for use during decryption.

The Cross platform AES encryption uses a 256-bit key so will not work as-is.

Example:

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)!)"); 

Output:

 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 with [UInt8] type

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 & 4 with Data type

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)! 

Example from sunsetted documentation section:

AES encryption in CBC mode with a random IV (Swift 3+)

The iv is prefixed to the encrypted data

aesCBC128Encrypt will create a random IV and prefixed to the encrypted code.
aesCBC128Decrypt will use the prefixed IV during decryption.

Inputs are the data and key are Data objects. If an encoded form such as Base64 if required convert to and/or from in the calling method.

The key should be exactly 128-bits (16-bytes), 192-bits (24-bytes) or 256-bits (32-bytes) in length. If another key size is used an error will be thrown.

PKCS#7 padding is set by default.

This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.

This is example, not production code.

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

Example usage:

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

Example Output:

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> 

Notes:
One typical problem with CBC mode example code is that it leaves the creation and sharing of the random IV to the user. This example includes generation of the IV, prefixed the encrypted data and uses the prefixed IV during decryption. This frees the casual user from the details that are necessary for CBC mode.

For security the encrypted data also should have authentication, this example code does not provide that in order to be small and allow better interoperability for other platforms.

Also missing is key derivation of the key from a password, it is suggested that PBKDF2 be used is text passwords are used as keying material.

For robust production ready multi-platform encryption code see RNCryptor.

like image 97
zaph Avatar answered Oct 03 '22 04:10

zaph