Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Implementing AES256 on iOS using CCCrypt function. But output and output buffer length is different than Android.

Cipher class in Android produces 48 bytes data where in iOS we get 80 bytes data.

In IOS using kCCAlgorithmAES, kCCOptionPKCS7Padding and in android using AES/CBC/PKCS5Padding.

in IOS IV is NULL and in android creating iv as new 16 bytes array.

Please help.

please find input and code for reference.

 - (void)viewDidLoad {
    [super viewDidLoad];

    NSString *message = [NSString stringWithFormat:@"com.myapp.com|355004059196637|911111111111|11341e5e-9643-4559-bbb7-34d40555e96c"];
    NSString *key = [NSString stringWithFormat:@"4f28d5901b4b7b80d33fda76ca372c2a20bd1a6c2aad7fa215dc79d507330678"];
    NSString *shaEncryptMessage = [self sha256:message length:0];
    NSData *aesEncryptData = [self aesEncrypt:[shaEncryptMessage dataUsingEncoding:NSUTF8StringEncoding] key:key iv:nil];
    NSString *hMac = [aesEncryptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    NSLog(@"hMac = %@",hMac);

    // IOS output : Can+oQR79D3/lsQGctzY/d2VBNZbWWtJxGI8iRIu80R2yTskn9gf2oKHaRESX73u
    //                  LpJHLx1Xr6iH11jFPlmqwW7mQz0xAW4uACNAMEoZ0kY=
    // Android output : MiMDkdo5cGsPMj2qCnNobgp7dr5KMvBhGuKTonrqr1lCYte/kKegGMtI/4TPhUNI
}


- (NSString*) sha256:(NSString *)key length:(NSInteger) length{
    const char *s=[key cStringUsingEncoding:NSASCIIStringEncoding];
    NSData *keyData=[NSData dataWithBytes:s length:strlen(s)];

    uint8_t digest[CC_SHA256_DIGEST_LENGTH]={0};
    CC_SHA256(keyData.bytes, (unsigned int)keyData.length, digest);
    NSData *out=[NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
    NSString *hash=[out description];
    hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
    hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
    hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
    return  hash;
}
-  (NSData *)aesEncrypt:(NSData *)plainText key:(NSString *)key iv:(NSString *)iv {
    char keyPointer[kCCKeySizeAES256+2],// room for terminator (unused) ref: https://devforums.apple.com/message/876053#876053
    ivPointer[kCCBlockSizeAES128];
    BOOL patchNeeded;
    bzero(keyPointer, sizeof(keyPointer)); // fill with zeroes for padding
    //key = [[StringEncryption alloc] md5:key];
    key = [self stringFromHex:key];
    patchNeeded= ([key length] > kCCKeySizeAES256+1);
    if(patchNeeded)
    {
        key = [key substringToIndex:kCCKeySizeAES256]; // Ensure that the key isn't longer than what's needed (kCCKeySizeAES256)
    }

    [key getCString:keyPointer maxLength:sizeof(keyPointer) encoding:NSUTF8StringEncoding];
    [iv getCString:ivPointer maxLength:sizeof(ivPointer) encoding:NSUTF8StringEncoding];

    //    if (patchNeeded) {
    //        keyPointer[0] = '\0';  // Previous iOS version than iOS7 set the first char to '\0' if the key was longer than kCCKeySizeAES256
    //    }

    NSUInteger dataLength = [plainText length];

    // For block ciphers, the output size will always be less than or equal to the input size plus the size of one block.
    size_t buffSize = dataLength + kCCBlockSizeAES128;
    void *buff = malloc(buffSize);

    size_t numBytesEncrypted = 0;



    CCCryptorStatus status = CCCrypt(kCCEncrypt, /* kCCEncrypt, etc. */
                                     kCCAlgorithmAES128, /* kCCAlgorithmAES128, etc. */
                                     kCCOptionPKCS7Padding, /* kCCOptionPKCS7Padding, etc. */
                                     keyPointer, kCCKeySizeAES256, /* key and its length */
                                     NULL, /* initialization vector - use random IV everytime */
                                     [plainText bytes], [plainText length], /* input  */
                                     buff, buffSize,/* data RETURNED here */
                                     &numBytesEncrypted);


    if (status == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buff length:numBytesEncrypted];
    }

    free(buff);
    return nil;
}

- (NSString *) stringFromHex:(NSString *)str
{
    NSMutableData *stringData = [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [str length] / 2; i++) {
        byte_chars[0] = [str characterAtIndex:i*2];
        byte_chars[1] = [str characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [stringData appendBytes:&whole_byte length:1];
    }
    return [[NSString alloc] initWithData:stringData encoding:NSASCIIStringEncoding];
}

Please find android code also,

   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    generateHMAC();
}

String K0 = "4f28d5901b4b7b80d33fda76ca372c2a20bd1a6c2aad7fa215dc79d507330678";
String generatedString = "com.myapp.com|355004059196637|911111111111|11341e5e-9643-4559-bbb7-34d40555e96c";

private void generateHMAC() {
    Log.d("Message of Hash", generatedString);
    byte[] var14 = new byte[0];
    try {
        var14 = SHA256(generatedString);
        byte[] var15 = new byte[0];
        var15 = encrypt(var14, hexStringToByteArray(K0));
        String var4 = Base64.encodeToString(var15, 2);
        Log.d("Existing K0", K0);
        Log.d("HMAC", var4);
    } catch (Exception e) {
        e.printStackTrace();
    }
}


public byte[] SHA256(String paramString) throws Exception {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    md.update(paramString.getBytes("UTF-8"));
    byte[] digest = md.digest();
    return digest;
}

public byte[] encrypt(byte[] var1, byte[] var2) throws Exception {
    SecretKeySpec var3 = new SecretKeySpec(var2, "AES");
    byte[] var4 = new byte[16];
    IvParameterSpec var5 = new IvParameterSpec(var4);
    Cipher var6 = Cipher.getInstance("AES/CBC/PKCS5Padding");
    var6.init(1, var3, var5);
    byte[] var7 = var6.doFinal(var1);
    return var7;
}

public byte[] hexStringToByteArray(String var1) {
    byte[] var2 = new byte[var1.length() / 2];

    for (int var3 = 0; var3 < var2.length; ++var3) {
        int var4 = var3 * 2;
        int var5 = Integer.parseInt(var1.substring(var4, var4 + 2), 16);
        var2[var3] = (byte) var5;
    }

    return var2;
}
like image 625
Pradip Avatar asked Oct 18 '22 15:10

Pradip


1 Answers

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

like image 147
TheGreatContini Avatar answered Oct 21 '22 04:10

TheGreatContini