0

We have code for AES-128 encryption in Java and we want some equivalent code in WP7.

However, we run into a problem: The two implementations yield different encrypted texts

Here is the code we're using:

Java Code

package com.emap.services;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESEcrypt1 {

    static byte[] ibv = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
        0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};

    public String encryptData() {
        String message = "Testing AES encryption-decryption amlgorithm for WP7.";
        String encryptedStr = "";
        try {
            SecretKeySpec skeySpec = new SecretKeySpec("Passkey".getBytes(), "AES");
            IvParameterSpec iv = new IvParameterSpec(ibv);
            // Instantiate the cipher
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(message.getBytes());
            encryptedStr = Base64.encode(encrypted);
        } catch (BadPaddingException ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        } catch (IllegalBlockSizeException ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        } catch (InvalidAlgorithmParameterException ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        } catch (InvalidKeyException ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        } catch (NoSuchAlgorithmException ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        } catch (NoSuchPaddingException ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        } catch (Exception ex) {
            System.out.println("Error: " + ex.getMessage());
            encryptedStr = "error";
        }
        System.out.println("Encrypted: " + encryptedStr);
        return encryptedStr;
    }
}

WP7 Code

static byte[] ibv = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
        0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};

public string Encrypt(string dataToEncrypt, string password)
//public string Encrypt(string dataToEncrypt)  
{
    AesManaged aes = null;
    MemoryStream memStream = null;
    CryptoStream crStream = null;
    try
    {
        //Generate a Key based on a Password and Salt      
        //Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));
        Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, ibv);                
        aes = new AesManaged();
        aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
        aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);
        memStream = new MemoryStream();
        crStream = new CryptoStream(memStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
        byte[] data = Encoding.UTF8.GetBytes(dataToEncrypt);
        crStream.Write(data, 0, data.Length);
        crStream.FlushFinalBlock();

        //Return Base 64 String                
        return Convert.ToBase64String(memStream.ToArray());
    }
    finally
    {
        //cleanup                
        if (crStream != null)
            crStream.Close();
        if (memStream != null)
            memStream.Close();
        if (aes != null)
            aes.Clear();
     }
}

Any help would be greatly appreciated.

joce
  • 9,624
  • 19
  • 56
  • 74
Mohit Leekha
  • 61
  • 11
  • 4
    Aside from anything else, don't use `message.getBytes()` - that will use the platform default encoding in Java. That's probably not the problem, but it's a bad idea anyway. – Jon Skeet Aug 17 '11 at 14:51
  • 2
    http://stackoverflow.com/questions/5295110/aes-encryption-in-java-and-decryption-in-c – BNL Aug 17 '11 at 14:53
  • 1
    It seems that, besides the encoding problems, you're not using the same key, the same IV, (and maybe also not the same chaining mode and the same padding: I don't know the defaults used by your WP7 code). No wonder they don't give the same result. – JB Nizet Aug 17 '11 at 14:57
  • 3
    Don't reuse initialization vectors (IVs). They must be generated randomly for each message that is encrypted. Send the random IV alongside the encrypted message. This way, identical portions of plaintext at the beginning of the message will not always yield the same ciphertext. It's important for security. – James Johnston Aug 17 '11 at 18:14

2 Answers2

3

Note that in Java, you have three parts in the cypher designation - algorithm, mode (CBC vs. ECB, etc) and padding. You must match all three, not just the algorithm. If the WP7 won't let you specify mode and padding explicitly, figure them out and match on the Java side.

Oh, and do make sure that the key is the same. It's not obvious from the example.

EDIT: for debugging padding issues, a trivial key is a great help. That is, a key which makes the algorithm emit a copy of its plaintext as a cyphertext. For RSA, for example, it's the one with public exponent 1. Not sure what would be a trivial key for AES - try all zeros.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
2

It doesn't look like you are using the same key. In the Windows Phone 7 implementation, you appear to be properly deriving a key from a password, while the Java code is improperly using some encoding of a password directly.

See my previous answer for a general overview of password-based encryption in Java. It will show you how to derive a key (according to PBKDF2 in RFC 2898), and describes the proper generation and exchange of an per-message initialization vector.


JB Nizet's comment on the post made me look more closely at how you are using the ibv data, and he's right: you aren't using the same IV in both operations. It's used as the IV in the Java code, but in the Windows code, it is used as "salt" for the key derivation process. These are two different purposes, and should use two distinct values.

Because ibv is a fixed value, it's more suitable for key derivation (each password should have one unique salt). Each message should have a new IV randomly chosen and sent with the cipher text. You could use ibv as the salt in the Java example I linked to above.

Community
  • 1
  • 1
erickson
  • 265,237
  • 58
  • 395
  • 493