Apple Developer
Apple Developer

Reputation: 99

Encryption method for iPhone and Android

I have tried to generate an encrypted text using AES algorithm In both ( Android and IPhone ) platforms but the problem is even we use the same encryption decryption library (AES-128) and with the same fixed variables ( Key , IV ,mode) but I get two different result for them.

I appreciate any help . :(

The below code presents the used encryption and decryption methods ,

Code version for Android:

Code :

  public class Crypto {
public static String encrypt(String seed, String cleartext) throws Exception {
        byte[] rawKey = getRawKey(seed.getBytes());
        byte[] result = encrypt(rawKey, cleartext.getBytes());
        return toHex(result);
}



public static String decrypt(String seed, String encrypted) throws Exception {
        byte[] rawKey = getRawKey(seed.getBytes());
        byte[] enc = toByte(encrypted);
        byte[] result = decrypt(rawKey, enc);
        return new String(result);
}


private static byte[] getRawKey(byte[] seed) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance(“CBC”);
        SecureRandom sr = SecureRandom.getInstance(“SHA1PRNG”);
        sr.setSeed(seed);
    kgen.init(128, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey();
    byte[] raw = skey.getEncoded();
    return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, “AES”);
        Cipher cipher = Cipher.getInstance(“AES”);
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(clear);
        return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
    SecretKeySpec skeySpec = new SecretKeySpec(raw, “AES”);
        Cipher cipher = Cipher.getInstance(“AES”);
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
}
public static String toHex(String txt) {
        return toHex(txt.getBytes());
}
public static String fromHex(String hex) {
        return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
        int len = hexString.length()/2;
        byte[] result = new byte[len];
        for (int i = 0; i < len; i++)
                result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
        return result;
}
public static String toHex(byte[] buf) {
        if (buf == null)
                return “”;
        StringBuffer result = new StringBuffer(2*buf.length);
        for (int i = 0; i < buf.length; i++) {
                appendHex(result, buf[i]);
        }
        return result.toString();
}
private final static String HEX = “0123456789ABCDEF”;
private static void appendHex(StringBuffer sb, byte b) {
        sb.append(HEX.charAt((b»4)&0x0f)).append(HEX.charAt(b&0x0f));
}

}

Code version for IPhone:

- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {

NSData* secretKey = [Cipher md5:cipherKey];

CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;

uint8_t iv[kCCBlockSizeAES128];
memset((void *) iv, 0x0, (size_t) sizeof(iv));

status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                         [secretKey bytes], kCCKeySizeAES128, iv, &cryptor);

if (status != kCCSuccess) {
    return nil;
}

size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length], true);

void * buf = malloc(bufsize * sizeof(uint8_t));
memset(buf, 0x0, bufsize);

size_t bufused = 0;
size_t bytesTotal = 0;

status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
                         buf, bufsize, &bufused);

if (status != kCCSuccess) {
    free(buf);
    CCCryptorRelease(cryptor);
    return nil;
}

bytesTotal += bufused;

status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);

if (status != kCCSuccess) {
    free(buf);
    CCCryptorRelease(cryptor);
    return nil;
}

bytesTotal += bufused;

CCCryptorRelease(cryptor);

return [NSData dataWithBytesNoCopy:buf length:bytesTotal];

}

 + (NSData *) md5:(NSString *) stringToHash {

const char *src = [stringToHash UTF8String];

unsigned char result[CC_MD5_DIGEST_LENGTH];

CC_MD5(src, strlen(src), result);

return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];

}

Some of my references :

Upvotes: 4

Views: 2117

Answers (3)

Nikolay Elenkov
Nikolay Elenkov

Reputation: 52936

The code has various problems, but your the cause of you getting different ciphertext on each encryption on Android is that you are not specifying an IV, and a random one is being generated. Also you say 'no padding', but the iOS code is doing padding, and the Android is most probably also is by default, since you are not specifying it explicitly. Finally, you do need padding if you are encrypting anything that is not a multiple of the block size (as your sample plain text is). getBytes() default to UTF-8 on Android, so that is the least of your problems, but do make it explicit.

Upvotes: 0

Shark
Shark

Reputation: 6610

oh man, you forgot to use .getBytes("UTF-8"); ...

extremely important.

EDIT:

I know I should improve the quality of this answer but what I said here + what rossum said below is the trick to getting this right.

It's just about cryption/decryption compliance - when you're working across platforms you have to have binary compliance as well.

so dump the raw data and inspect them for discrepancies until your eyes bleed out and you spot and successfully patch that little quirk that makes that difference.

for starters, .getBytes("UTF-8") will get you consistent strings across Java/android environments (because getBytes() doesn't work the way you'd expect it to work unless you read the docs)

Upvotes: 3

rossum
rossum

Reputation: 15685

First thing to check is the bytes you are using. You list your IV, key and plaintext as characters. Those characters will get converted to bytes using some sort of encoding. Check what the bytes look like, not the characters. You need to list everything, byte by byte, on both machines and compare. Alternatively specify exactly what conversion you are going to use on each machine. The machine defaults could well be different.

Once you know the input bytes are the same, then we can look for other problems.

As a minor point, it is generally better to use padding, such as PKCS#7; that way you don't have to fit your messages to the cypher's block size.

Upvotes: 4

Related Questions