iOS Aes Encryption - Fail to Encrypt

iOS AES Encryption - Fail to Encrypt

There is no need to make the crypto so complicated, here is a basic encrypt/decrypt method. The iv and key must be the correct length. The value context is either kCCEncrypt or kCCDecrypt.

+ (NSData *)doCipher:(NSData *)dataIn
iv:(NSData *)iv
key:(NSData *)symmetricKey
context:(CCOperation)encryptOrDecrypt
error:(NSError **)error
{
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];

ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
symmetricKey.bytes,
kCCKeySizeAES128,
iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);

if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:@"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}

return dataOut;
}

AES encryption not working properly on iOS 13.4

After much research I made it work on all the versions.
if in the above code “passphrase” is shorted than 16 bytes, it uses whatever's in-memory past the end.
It seems like improper use of the CommonCrypto APIs was the issue here. Really don’t know why this worked before, but maybe we got lucky with the memory layout but the issues above need to be remedied before this will function as expected.

Why is text encryption failing in CryptoSwift on Swift5?

This error occurred in a simple place. The byte value for ivData and keyData was not 16 bytes. If their value is not 16 bytes, an error occurs.

AES Encrypt and Decrypt

I found the solution, it is a good library.

Cross platform 256bit AES encryption / decryption.

This project contains the implementation of 256 bit AES encryption which works on all the platforms (C#, iOS, Android). One of the key objective is to make AES work on all the platforms with simple implementation.

Platforms Supported:
iOS ,
Android ,
Windows (C#).

https://github.com/Pakhee/Cross-platform-AES-encryption

AES encryption between iOS and Python

Edited to final answer with both ECB and CBC working between iOS and Python:

With credit to others who built the origional NSData_AESCrypt code at:

//  AES Encrypt/Decrypt
// Created by Jim Dovey and 'Jean'
// See http://iphonedevelopment.blogspot.com/2009/02/strong-encryption-for-cocoa-cocoa-touch.html
//
// BASE64 Encoding/Decoding
// Copyright (c) 2001 Kyle Hammond. All rights reserved.
// Original development by Dave Winer.
//
// Put together by Michael Sedlaczek, Gone Coding on 2011-02-22
//

On iOS, encryption logic is modified from the original NSData+AESCrypt is modified as:

@implementation NSData (AESCrypt)

- (NSData *)AES256EncryptWithKey:(NSString *)key
{
return [self AES256EncryptWithKey:key ECB:false];
}
- (NSData *)AES256EncryptWithKey:(NSString *)key ECB:(Boolean) ecb
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)

// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

// create results buffer with extra space for padding
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCKeySizeAES256;
void *buffer = malloc( bufferSize );

size_t numBytesEncrypted = 0;
NSString *hardCodeIV = @"1111111111111111";
char iv[17];
bzero( iv, sizeof( iv ) );
[hardCodeIV getCString:iv maxLength:sizeof(iv) encoding:NSUTF8StringEncoding];
//CBC
CCCryptorStatus cryptStatus = kCCSuccess;
if (ecb == false)
{
cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
iv ,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted );
} else
{
// ECB
cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode,
keyPtr, kCCKeySizeAES256,
NULL,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted );
}
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}

free( buffer ); //free the buffer
return nil;
}
- (NSData *)AES256DecryptWithKey:(NSString *)key
{
return [self AES256DecryptWithKey:key ECB:false];
}
- (NSData *)AES256DecryptWithKey:(NSString *)key ECB:(Boolean) ecb
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)

// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];

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

size_t numBytesDecrypted = 0;
NSString *hardCodeIV = @"1111111111111111";
char iv[17];
bzero( iv, sizeof( iv ) );
[hardCodeIV getCString:iv maxLength:sizeof(iv) encoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = kCCSuccess;
if (ecb == false)
{
cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
iv ,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesDecrypted );
} else {
cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding | kCCOptionECBMode,
keyPtr, kCCKeySizeAES256,
NULL,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesDecrypted );
}
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}

free( buffer ); //free the buffer
return nil;
}

The resulting NSData element is then base64 encoded using some helper classes (used unmodified from the class) as:

static char encodingTable[64] = 
{
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
+ (NSData *)dataWithBase64EncodedString:(NSString *)string
{
return [[NSData allocWithZone:nil] initWithBase64EncodedString:string];
}

- (id)initWithBase64EncodedString:(NSString *)string
{
NSMutableData *mutableData = nil;

if( string )
{
unsigned long ixtext = 0;
unsigned long lentext = 0;
unsigned char ch = 0;
unsigned char inbuf[4], outbuf[3];
short i = 0, ixinbuf = 0;
BOOL flignore = NO;
BOOL flendtext = NO;
NSData *base64Data = nil;
const unsigned char *base64Bytes = nil;

// Convert the string to ASCII data.
base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
base64Bytes = [base64Data bytes];
mutableData = [NSMutableData dataWithCapacity:base64Data.length];
lentext = base64Data.length;

while( YES )
{
if( ixtext >= lentext ) break;
ch = base64Bytes[ixtext++];
flignore = NO;

if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
else if( ch == '+' ) ch = 62;
else if( ch == '=' ) flendtext = YES;
else if( ch == '/' ) ch = 63;
else flignore = YES;

if( ! flignore )
{
short ctcharsinbuf = 3;
BOOL flbreak = NO;

if( flendtext )
{
if( ! ixinbuf ) break;
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
else ctcharsinbuf = 2;
ixinbuf = 3;
flbreak = YES;
}

inbuf [ixinbuf++] = ch;

if( ixinbuf == 4 )
{
ixinbuf = 0;
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );

for( i = 0; i < ctcharsinbuf; i++ )
[mutableData appendBytes:&outbuf[i] length:1];
}

if( flbreak ) break;
}
}
}

self = [self initWithData:mutableData];
return self;
}

#pragma mark -

- (NSString *)base64Encoding
{
return [self base64EncodingWithLineLength:0];
}

- (NSString *)base64EncodingWithLineLength:(NSUInteger)lineLength
{
const unsigned char *bytes = [self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:self.length];
unsigned long ixtext = 0;
unsigned long lentext = self.length;
long ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
unsigned short i = 0;
unsigned short charsonline = 0, ctcopy = 0;
unsigned long ix = 0;

while( YES )
{
ctremaining = lentext - ixtext;
if( ctremaining <= 0 ) break;

for( i = 0; i < 3; i++ )
{
ix = ixtext + i;
if( ix < lentext ) inbuf[i] = bytes[ix];
else inbuf [i] = 0;
}

outbuf [0] = (inbuf [0] & 0xFC) >> 2;
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
outbuf [3] = inbuf [2] & 0x3F;
ctcopy = 4;

switch( ctremaining )
{
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}

for( i = 0; i < ctcopy; i++ )
[result appendFormat:@"%c", encodingTable[outbuf[i]]];

for( i = ctcopy; i < 4; i++ )
[result appendString:@"="];

ixtext += 3;
charsonline += 4;

if( lineLength > 0 )
{
if( charsonline >= lineLength )
{
charsonline = 0;
[result appendString:@"\n"];
}
}
}

return [NSString stringWithString:result];
}

The resulting base64 encoded string is then sent to the cloud where python pycryptodome can decrypt it as:

    def aesCBCEncrypt(self,key,stri):
if isinstance(key, str):
key = key.encode('utf-8')
cipher = AES.new(key, AES.MODE_CBC, self.nonce) # , nonce=self.nonce)
if isinstance(stri, str):
data = stri.encode('utf-8')
try:
data = pad(data, 16)
ciphertext = cipher.encrypt(data)
ciphertext = b64encode(ciphertext)
ret = ciphertext.decode('utf-8')
except:
print("Some Error")
ret = ""
return ret

def aesCBCDecrypt(self,key,data):
if isinstance(key, str):
key = key.encode('utf-8')
cipher = AES.new(key, AES.MODE_CBC, self.nonce) # , nonce=self.nonce)
if isinstance(data, str):
data = data.encode('utf-8')
data = b64decode(data)
try:
result = cipher.decrypt(data)
result = unpad(result, 16)
ret = result.decode('utf-8')
except:
print("Some Error")
ret = ""
return ret

def aesECBEncrypt(self,key,stri):
if isinstance(key, str):
key = key.encode('utf-8')
cipher = AES.new(key, AES.MODE_ECB) # , nonce=self.nonce)
if isinstance(stri, str):
data = stri.encode('utf-8')
try:
data = pad(data,16)
ciphertext = cipher.encrypt(data)
ciphertext = b64encode(ciphertext)
ret = ciphertext.decode('utf-8')
except:
print("Some Error")
ret = ""
return ret

def aesECBDecrypt(self,key,data):
if isinstance(key, str):
key = key.encode('utf-8')
cipher = AES.new(key, AES.MODE_ECB) #, nonce=self.nonce)
if isinstance(data, str):
data = data.encode('utf-8')
data = b64decode(data)
try:
result = cipher.decrypt(data)
result = unpad(result,16)
ret = result.decode('utf-8')
except:
print("Some Error")
ret = ""
return ret

It is extremely important that the IV (aka nonce) is the same on iOS and Python when using CBC - it will not work otherwise. This is set in this case to a string of 16 '1' characters terminated with a null. This is not really a secret key in itself, but it is likely worth changing it and securing it (possibly sending it as well with asymmetric, RSA in my case, encryption). The AES however is the critical key and should certainly be sent encrypted between the devices.

Finally, I'd recommend using the CBC even though the IV needs to be considered, as it is more secure. And when I have time I will look into integration of the Swift only Apple Crypto Kit library to support other forms as well...

Problem with decrypt String which is Encrypted in iOS AES/CBC/PKCS7Padding 128 bit Algorithm in Android

I got real string by using following method.

  public static String decryptKey(String key_id) {
// Decode base64 to get bytes
String decrypted = "";
try {

Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

dcipher.init(DECRYPT_MODE, aesKey, ivSpec);

byte[] utf8 = dcipher.doFinal(hexToBytes(key_id));

// Decode using utf-8
decrypted = new String(utf8, "UTF8").trim();

} catch (Exception e) {


e.printStackTrace();
}

return decrypted;
}

public static byte[] hexToBytes(String str) {
if (str == null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i = 0; i < len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
}
return buffer;
}
}


Related Topics



Leave a reply



Submit