AES encryption for iOS and Android, output and buffer size are different

Implement AES256 on iOS using the CCCrypt function. But the length of the output and output buffer is different from Android.

The Cipher class in Android gives data with 48 bytes, where on iOS we get 80 bytes of data.

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

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

Please, help.

Find the login 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 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]; } 

Also find the Android code,

  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; } 
+5
source share
1 answer

Updates after submitting iOS code:

  • AesEncryptData should be your way out. Get rid of hmac, which has nothing to do with AES encryption (instead, it is designed for message integrity).
  • The only way you are going to match your Android code is to use the same IV as the Android code.

Previous answer:

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

Without the requested information, I do not have your answer, but I have a few pointers that can help you figure it out:

  • Your fill is in order. PKCS5Padding in Java is an incorrectly named implementation of PKCS # 7, so it must be compatible with Apple's kCCOptionPKCS7Padding.
  • Apple by default uses CBC mode under the hood if no mode is specified, so this is consistent with the Android code. So this is not a problem either.
  • When encrypting, the ciphertext will be a multiple of 16 bytes (since AES has a block size of N = 16 bytes and according to defn PKCS # 7 ). In particular:
    • If the input is a multiple of 16 bytes, then the output must 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: an input of 47 bytes should be 16 * Ceiling (17/16) = 16 * 3 = 48 bytes.
  • It is possible that one of the implementations displays IV as part of the ciphertext. If this happens, it should be at the beginning of the ciphertext. You should be able to check if this happens. (Let me know if this happens, please)

Having said that, something is strange and probably implemented incorrectly, and we need code to understand it. It makes no sense that the Android code leads to 3 blocks of 16, while the Apple code leads to 5 blocks of 16.

Also, as I said above, although Apple tells you that IV is optional, they mean that it is optional in terms of code usage. optional for security . IVs are necessary and should be unpredictable for the CBC regimen and should never be repeated. If you ignore this, you will leak information about your data, and in some situations an attacker can decrypt the data (attacks against abusive attacks).

+3
source

Source: https://habr.com/ru/post/1265691/


All Articles