Android Android - 20 days ago 5
PHP Question

Encrypting with PHP mcrypt, decrypting with Android. Is this decryption right?

Am I doing it right or is there a mistake? The test program I'm still trying to implement is running without any exceptions or errors. But it's not doing the thing it has to do and I can't find the problem.

Here is the Android code that attempts to decrypt:

private static final int IO_BUFFER_SIZE = 4 * 1024;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

try {
AssetManager am = this.getAssets();
InputStream is = am.open("2000_1.jpg_encrypted"); // get the encrypted image from assets folder

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[IO_BUFFER_SIZE];
int read;
while ((read = is.read(b)) != -1) { //convert inputstream to bytearrayoutputstream
baos.write(b, 0, read);
}
//START
long start = System.currentTimeMillis()/1000L; // start

//byte[] keyStart = "MARTIN_123_MARTIN_123".getBytes(); // specific key value
KeyGenerator kgen = KeyGenerator.getInstance("AES/CBC/PKCS5Padding"); //aes
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
//sr.setSeed(keyStart);
kgen.init(128, sr);
//SecretKey skey = kgen.generateKey();
//byte[] key = skey.getEncoded();

byte[] key = "MARTIN_123_MARTIN_123".getBytes("UTF-8");
byte[] iv = "1234567890123456".getBytes("UTF-8");
byte[] decryptedData = decrypt(key, iv, b);


//END
long end = System.currentTimeMillis()/1000L; // end
Log.d("TEST","Time start "+ String.valueOf(start)); //showing the strat in ms
Log.d("TEST","Time end "+ String.valueOf(end)); //showing the end in ms

Bitmap bitmap = BitmapFactory.decodeByteArray(decryptedData , 0, decryptedData .length); //decoding bytearrayoutputstream to bitmap
//String filepath = Environment.getExternalStorageDirectory()+"bitmap";
FileOutputStream fos = new FileOutputStream("sdcard/DCIM/100ANDRO");
fos.write(decryptedData);
fos.close();

is.close(); // close the inputstream
baos.close(); // close the bytearrayoutputstream
}
catch(Exception e){
e.fillInStackTrace();
}
}

//decrypt
private byte[] decrypt(byte[] raw, byte[] iv, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
byte[] decrypted = cipher.doFinal(encrypted);

return decrypted;
}


Here is the PHP code that encrypts:

$folder = $this->getConfiguration()->getAppRootDir() . '/temp_encryption_testing/';

$files = array(
'007FRAMESUPERIOR.jpg',
'2000_1.jpg',
'APLICACIONdescargaliga.jpg',
'APPCOMMENTS.pdf',
'AUDIOVISUALFOTO02.jpg'
);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "MARTIN_123_MARTIN_123";
foreach($files as $file)
{
$input_file = $folder . $file;
$text = file_get_contents($input_file);
//$text = "Meet me at 11 o'clock behind the monument.";
echo strlen($text) . "\n";
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
echo strlen($crypttext) . "\n";
file_put_contents($input_file . '_encrypted', $crypttext);
}


Any help will be appreciated :)

Answer

You are trying to decrypt with a randomly generated key (adding a seed to a SecureRandom only supplements the random state). That is sure to fail.

If "MARTIN_123_MARTIN_123".getBytes() is your key (or rather: the first 16 bytes of it), you should just pass that directly to your decrypt-method:

byte[] decryptedData = decrypt(keyStart,b);

Okay, from your comment, I can see that you are encrypting with PHP:mcrypt using AES-256/ECB/ZeroBytePadding (zero-byte padding is implicit with mcrypt).

First of all: ECB is not a safe mode - especially for images (though JPGs are probably not quite as bad as BMPs). If you decide to change to CBC, remember that you need to transmit the IV, f.x. by prepending it to the encrypted data. But if you want to decrypt CBC, you need to indicate it in your Cipher.getInstance()-call (you currently use AES/CBC/PKCS5Padding).

Zero-byte-padding is not really removable unless you know the length of the data. So you must either transmit the length or add PKCS#7 padding before encrypting the data.

Finally: you encrypt using AES-256. That uses a 32-byte key. According to the mcrypt-documentation it adds \0s if the key is too short, so you will either have to use an actual 32-byte key or add \0s to the Android-side, too.

Change your code to this on the Android side:

byte[] key = "MARTIN_123456789".getBytes("UTF-8");
byte[] iv = "1234567890123456".getBytes("UTF-8");
byte[] decryptedData = decrypt(key, iv, b);

private byte[] decrypt(byte[] raw, byte[] iv, byte[] encrypted) throws Exception {
  SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
  Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
  IvParameterSpec ivspec = new IvParameterSpec(iv);         
  cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
  byte[] decrypted = cipher.doFinal(encrypted);

  return decrypted;
}

And on the PHP side:

function addpadding($string, $blocksize = 16){
    $len = strlen($string);
    $pad = $blocksize - ($len % $blocksize);
    $string .= str_repeat(chr($pad), $pad);
    return $string;
}

$key = "MARTIN_123456789";
$iv = "1234567890123456"
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, addpadding($text), MCRYPT_MODE_CBC, $iv);
Comments