Aes/Cbc/Pkcs5Padding in iOS Objective C Result Differs from Android

Decrypting AES/CBC/PKCS5Padding in iOS

Okay, I think I figured the problem. Obviously, there is a problem with NSData.bytes and one should work withUnsafeBytes. Furthermore, my issue may was that the IV was not part of the data, as many examples assumed, so I missed 16 bytes when decrypting. The following code works for me, hope it will help someone!

func decrypt(data: Data) -> Data {
let key: Data = keyStr.data(using: .utf8) ?? Data()
let iv: Data = ivStr.data(using: .utf8) ?? Data()

if(keyStr.count == kCCKeySizeAES128){print("Key OKAY")} else {print("Key NOT okay")}
if(ivStr.count == kCCBlockSizeAES128){print("IV OKAY")} else {print("IV NOT okay")}

var buffer = Data(count: data.count)

var numberBytesDecrypted: Int = 0

let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
data.withUnsafeBytes {dataBytes in
buffer.withUnsafeMutableBytes {bufferBytes in iv.withUnsafeBytes {ivBytes in
CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
CCOptions(kCCOptionPKCS7Padding), // options: CCOptions
keyBytes.baseAddress, // key: the "password"
key.count, // keyLength: the "password" size
ivBytes.baseAddress, // iv: Initialization Vector
dataBytes.baseAddress!, // dataIn: Data to decrypt bytes
data.count, // dataInLength: Data to decrypt size
bufferBytes.baseAddress, // dataOut: decrypted Data buffer
data.count, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)}
}
}
}

if(cryptStatus == CCCryptorStatus(kCCSuccess)){
return buffer[..<numberBytesDecrypted]
} else {
print("Decryption failed")
return data
}
}

AES/cbc/pkcs5padding encription IOS

You can use the following snippet as start point:

+ (NSData*)encryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv;
{
NSData* result = nil;

// setup key
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:FBENCRYPT_KEY_SIZE];

// setup iv
char cIv[FBENCRYPT_BLOCK_SIZE];
bzero(cIv, FBENCRYPT_BLOCK_SIZE);
if (iv) {
[iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
}

// setup output buffer
size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
void *buffer = malloc(bufferSize);

// do encrypt
size_t encryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
FBENCRYPT_ALGORITHM,
kCCOptionPKCS7Padding,
cKey,
FBENCRYPT_KEY_SIZE,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&encryptedSize);
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
} else {
free(buffer);
NSLog(@"[ERROR] failed to encrypt|CCCryptoStatus: %d", cryptStatus);
}

return result;
}

+ (NSData*)decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv;
{
NSData* result = nil;

// setup key
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:FBENCRYPT_KEY_SIZE];

// setup iv
char cIv[FBENCRYPT_BLOCK_SIZE];
bzero(cIv, FBENCRYPT_BLOCK_SIZE);
if (iv) {
[iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
}

// setup output buffer
size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
void *buffer = malloc(bufferSize);

// do decrypt
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
FBENCRYPT_ALGORITHM,
kCCOptionPKCS7Padding,
cKey,
FBENCRYPT_KEY_SIZE,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&decryptedSize);

if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(@"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus);
}

return result;
}

Constants

#define FBENCRYPT_ALGORITHM     kCCAlgorithmAES128
#define FBENCRYPT_BLOCK_SIZE kCCBlockSizeAES128
#define FBENCRYPT_KEY_SIZE kCCKeySizeAES256

For more information, see FBEncryptor

Hope this helps.

AES CBC Encryption With PKCS7Padding Has Different Results In Java And Objective-C

Your problem is with the Objective-C code. You should use same method in both Java and Obj-C. You can use this code in order to make it return the same results:

AES.h

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

@interface AES : NSObject

+ (NSData *)Encrypt:(NSString *)data WithKey:(NSString *)key;
+ (NSString *)Decrypt:(NSData *)data WithKey:(NSString *)key;

+ (NSData *)AESOperation:(CCOperation)operation OnData:(NSData *)data key:(NSString *)key;

@end

AES.m

#import "AES.h"

@implementation AES

+ (NSData *)Encrypt:(NSString *)data WithKey:(NSString *)key {
return [self AESOperation:kCCEncrypt OnData:[data dataUsingEncoding:NSUTF8StringEncoding] key:key];
}
+ (NSString *)Decrypt:(NSData *)data WithKey:(NSString *)key {
return [[NSString alloc] initWithData:[self AESOperation:kCCDecrypt OnData:data key:key] encoding:NSUTF8StringEncoding];
}

+ (NSData *)AESOperation:(CCOperation)operation OnData:(NSData *)data key:(NSString *)key {
char keyPtr[kCCKeySizeAES128];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

NSUInteger dataLength = [data length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);

size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keyPtr,
kCCBlockSizeAES128,
keyPtr,
[data bytes],
dataLength,
buffer,
bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}

free(buffer);
return nil;
}

@end

Generate same encrypted string AES/CBC/PKCS7Padding in Objective C, .net and Android

Thank you, Rob. I used the RNCryptor library for both (Objective C and C#) and it solved my problem.

Here is my code for Objective C:

-(NSString *)encryptUsingRNCryptor:(NSString *)strPassword{

NSData *data = [strPassword dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSData *encryptedData = [RNEncryptor encryptData:data
withSettings:kRNCryptorAES256Settings
password:kKeyForEncryption
error:&error];
NSString *encryptString = [encryptedData base64EncodedStringWithOptions:0];

NSLog(@"enccrypted data ----->>>## %@ ## %@", encryptedData, encryptString);

// Decryption

NSData *decryptedData = [RNDecryptor decryptData:encryptedData
withPassword:kKeyForEncryption
error:&error];
NSString *decodedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];

NSLog(@" Decryted data ----%@ --------and string %@", decryptedData, decodedString);

return [encryptedData base64EncodedStringWithOptions:0];

}

and for C# decryption we used RNCryptor-cs library.

Once again thank you Rob :)

AES encryption on iOS and android, output and buffer size is different

Updates after you provided iOS code:

  • The aesEncryptData should be your output. Get rid of the hmac, that has nothing to do with AES encryption (instead, it is for message integrity).
  • The only way you are going to match your Android code is if you are using the same IV that the Android code is using.

Earlier reply:

How long is the input? Providing source code and sample data may help us solve the problem quicker.

Without the requested information, I don't have your answer, but I have a few pointers that might help you get to the bottom of it:

  • Your padding is okay. PKCS5Padding in Java is wrongly named implementation of PKCS#7, so it should be compatible with Apple's kCCOptionPKCS7Padding.
  • Apple by default uses CBC mode under the hood if no mode is specified, so that agrees with the Android code. So that cannot be the problem either.
  • When you encrypt, the ciphertext will be a multiple of 16 bytes (because AES has N=16 bytes block size and according to defn of PKCS #7). Specifically:

    • If the input is a multiple of 16 bytes, then the output should be exactly 16 bytes more than the input.
    • If the input is not a multiple of 16 bytes, then the output should be 16*Ceiling(Input length/16). Example: 47 byte input should be 16*Ceiling(17/16) = 16*3 = 48 byte output.
  • It is possible that one of the implementations is outputting the IV as part of the ciphertext. If this is happening, it should be at the beginning of the ciphertext. You should be able to test to see if this is happening. (Let me know if that is happening please)

Having said that, something is weird and likely implemented wrong, and we need the code to get to the bottom of it. It makes no sense that the Android code results in 3 blocks of 16 whereas the Apple code results in 5 blocks of 16.

Also, as I commented above, even though Apple tells you that the IV is optional, they mean that it is optional in terms of getting the code to work. It is not optional for security. IVs are required and must be unpredictable for CBC mode of operation, and they should never be repeated. If you ignore that, you leak information about your data, and in some situations, an attacker may be able to decrypt the data (padding oracle attacks).

AES128 CBC encryption in Java and Objective C giving different outputs

I was able to solve the issue on the java code thanks to suggestions above. Here is the Java code that worked for me to achieve AES128 CBC mode on both java and objective c

public static String AESEncrypt(String text, String key, String iv) throws Exception {

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = new BASE64Decoder().decodeBuffer(key);
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);

byte[] keyBytesiv = new byte[16];
byte[] biv = new BASE64Decoder().decodeBuffer(iv);
int leniv = biv.length;
if (leniv > keyBytesiv.length)
leniv = keyBytesiv.length;
System.arraycopy(biv, 0, keyBytesiv, 0, len);

SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytesiv);

cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(results);
}


Related Topics



Leave a reply



Submit