6

I'm trying to implement AES128 encryption on an Android. I've got a solution working on an iPhone with Objective C but having trouble porting it to Android. I've searched stackoverflow for a solution, but I seem to be doing something wrong. I'm fairly new to Java so I think I'm missing something to do with data, string conversion.

Here is my iPhone encrypt:

char keyPtr[kCCKeySizeAES128+1];
[keyString getCString:keyPtr
            maxLength:sizeof(keyPtr)
             encoding:NSASCIIStringEncoding];

// CString for the plain text
char plainBytes[[plainString length]+1];
[plainString getCString:plainBytes
              maxLength:sizeof(plainBytes)
               encoding:NSASCIIStringEncoding];

size_t bytesEncrypted = 0;

// Allocate the space for encrypted data
NSUInteger dataLength = [plainString length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void* buffer = malloc(bufferSize);

// Encrypt
CCCryptorStatus ret = CCCrypt(kCCEncrypt,
                              kCCAlgorithmAES128,
                              kCCOptionPKCS7Padding | kCCOptionECBMode,
                              keyPtr,
                              kCCKeySizeAES128,
                              NULL,
                              plainBytes, sizeof(plainBytes),
                              buffer, bufferSize,
                              &bytesEncrypted);
if (ret != kCCSuccess) {
    free(buffer);
}

encryptedData = [NSData dataWithBytes:buffer length:bytesEncrypted];

Here is my Java:

    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));

Using the same key and plaintext in iPhone and Java give different results. My iPhone result works the way I need it so I'm trying to get java to give me the iPhone result. I'm missing something in the Java for sure, just not sure what it is.

edit

based on suggestions below I modified my Java to this

    byte[] keyBytes = plainTextKey.getBytes("US-ASCII");
    SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(plainText.getBytes("US-ASCII"));

but I'm still getting different results between android and iPhone

Pabs
  • 243
  • 5
  • 12
  • 3
    Your encoding is different for the plaintext. The android is using UTF8, the iPhone is using ASCII. – vcsjones Mar 13 '13 at 16:08
  • 1
    As a side note, I would discourage using ECB for your cipher mode (unless you are trying to maintain compatibility with a system that already uses it). It makes it rather easy to discern patterns in encrypted data. – vcsjones Mar 13 '13 at 16:10
  • Thanks vcsjones, i've updated my code to plainText.getBytes("ASCII") but the result is still different – Pabs Mar 13 '13 at 16:37
  • 2
    As vcsjones notes, ECB is insanely insecure (it barely qualifies as encryption in many cases). You're also generating your key incorrectly. You can't just copy ASCII bytes into an AES key. That throws away almost all of the keyspace and makes it easy to brute-force. You want to use a Key Derivation Function (generally PBKDF2) to convert strings into keys. If you need an out-of-the-box crypto solution that runs on iOS and Android, see RNCryptor for iOS and JNCryptor for Java. They write the same format. – Rob Napier Mar 13 '13 at 19:55

3 Answers3

4

in addition to encoding difficulties with your plaintext (as vcsjones pointed out in the comments), make sure that the encoding is the same for the key string (note that using a raw string, like a password, directly as an crypto key is bad news bears, use a key derivation function like PBKDF2 on the password to get derive a key).

Also, the encoding string for Java for ASCII is US-ASCII, not just ASCII, so make sure to use that in your getBytes calls.

EDIT: found your problem: the iOS string is being encrypted with an extra null charcter (0x00) at the end, while the java was not. So encrypting "hello world\0" in java will give you the same output as "hello world" does in iOS

Peter Elliott
  • 3,273
  • 16
  • 30
  • Thanks for the tips, i totally missed "US-ASCII". unfortunately the problem is still the same. I understand the problems with EBC, but in this particular case I need to use it. I've also updated the code to use "US-ASCII" both plaintext and key but still same result. – Pabs Mar 13 '13 at 17:40
0

Most of the examples on internet are weak implementation of AES. For an implementation to be strong, random IV should be used all the time and key should be hashed.

For more secure(random IV + hashed key) cross platform (android, ios, c#) implementation of AES see my answer here - https://stackoverflow.com/a/24561148/2480840

Community
  • 1
  • 1
Navneet Kumar
  • 909
  • 7
  • 6
0

I have written this manager file and its functions are working perfectly fine for me. This is for AES 128 and without any salt.

public class CryptoManager {

private static CryptoManager shared;
private String privateKey = "your_private_key_here";
private String ivString = "your_iv_here";


private CryptoManager(){
}

public static CryptoManager getShared() {
    if (shared != null ){
        return shared;
    }else{
        shared = new CryptoManager();
        return shared;
    }
}

public String encrypt(String value) {
    try {
        IvParameterSpec iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(privateKey.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

        byte[] encrypted = cipher.doFinal(value.getBytes());
        return android.util.Base64.encodeToString(encrypted, android.util.Base64.DEFAULT);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}


public String decrypt(String encrypted) {
    try {
        IvParameterSpec iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
        SecretKeySpec skeySpec = new SecretKeySpec(privateKey.getBytes("UTF-8"), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = new byte[0];
        original = cipher.doFinal(android.util.Base64.decode(encrypted, android.util.Base64.DEFAULT));

        return new String(original);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return null;
}
}

You need to call the functions like this.

String dataToEncrypt = "I need to encrypt myself";
String encryptedData = CryptoManager.getShared().encrypt(data);

And you will get your encrypted string with the following line

String decryptedString = CryptoManager.getShared().decrypt(encryptedData);