Reputation: 1
We are doing a AES 128 CBC encryption but the IV is not appending to the encrypted data. Could someone help me.
The num of bytes encrypted is 16 bytes and the IV bytes are 16 in total. So I should get the 32 bytes (16(IV) + 16(cipher)). But I am getting only 16 bytes which is cipher.
I have written a java code that is generating correctly but with Objective-C it is not.
#import "NSData+AES.h"
@implementation NSData(AES)
- (NSData *) EncryptAES: (NSString *) key
{
char keyPtr[kCCKeySizeAES128+1];
bzero( keyPtr, sizeof(keyPtr) );
[key getCString: keyPtr maxLength: sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t numBytesEncrypted = 0;
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
const unsigned char iv[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
CCCryptorStatus result = CCCrypt( kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keyPtr,
kCCKeySizeAES128,
iv,
[self bytes], [self length],
buffer, bufferSize,
&numBytesEncrypted );
if(result==kCCSuccess )
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
Upvotes: 0
Views: 2564
Reputation: 112857
Just prefix the encrypted data with the IV, split it off for decryption.
ObjC:
+ (NSData *)aesCBCEncrypt:(NSData *)data
key:(NSData *)key
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
int ivLength = kCCBlockSizeAES128;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];
SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes,
data.bytes, data.length,
dataOut.mutableBytes + ivLength, dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes + ivLength;
}
else {
if (error) {
*error = [NSError errorWithDomain:@"kEncryptionError" code:ccStatus userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}
Swift 3+
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.
// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) {
let keyLength = keyData.count
let ivSize = kCCBlockSizeAES128;
let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
var numBytesEncrypted :size_t = 0
cryptData.withUnsafeMutableBytes {ivBytes in
SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
}
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
keyBytes, keyLength,
cryptBytes,
dataBytes, data.count,
cryptBytes+kCCBlockSizeAES128, cryptLength,
&numBytesEncrypted)
}
}
}
cryptData.count = numBytesEncrypted + ivSize
return cryptData;
}
Note: error handling has been removed, see the code in the link for error handling.
Upvotes: 3