Be sure to use the same parameters that seem to be AES with CBC mode, with the addition of iv, PKCS5Padding (actually PKCS # 7) and a 16-byte (128-bit) key.
PKCS # 5 padding and PKCS # 7 are essentially the same add-on, sometimes for historical reasons. PKCS # 5 gasket is indicated for use with AES but actual filling is PKCS # 7.
Verify that the key encodings, iv, and encrypted data match. Hex resets them on both platforms to make sure 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, iv must be a random byte and appended to the encrypted data for use during decryption.
AES Cross Platform encryption uses a 256-bit key, so it won’t work as it 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 type [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 and 4 with type 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)!
An example from the hardened documentation section:
AES encryption in CBC mode with random IV (Swift 3 +)
Document iv prefix for encrypted data
aesCBC128Encrypt will create a random IV and prefix for the encrypted code.
aesCBC128Decrypt will use the IV prefix during decryption.
Inputs are data and key are data objects. If encoded form, such as Base64, if required, convert to and / or from the calling method.
The key must consist of 128-bit (16-byte), 192-bit (24 bytes) or 256-bit (32 bytes) in length. If a different key size is used, an error will be generated.
PKCS # 7 padding is installed by default.
This example requires Common Crypto. You must have a title header for the project:
#import <CommonCrypto/CommonCrypto.h>
Add Security.framework to the project.
This is an example, not a 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; }
Usage example:
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)") }
Result:
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 an example CBC mode code is that it leaves the creation and sharing of a random IV to the user. This example includes creating an IV, prefixing the encrypted data, and using the IV prefix during decryption. This frees the casual user from the details that are necessary for CBC mode .
For security, the encrypted data must also be authenticated; this sample code does not provide for being small and providing better compatibility for other platforms.
There is also no key output of the key from the password; it is proposed to use PBKDF2 , since text passwords are used as material keys.
For a robust off-the-shelf multi-platform encryption code, see RNCryptor .