I am using serpro's Android-PHP-Encrypt-Decrypt project to encrypt images with PHP and decrypt them with JAVA. The encryption and decryption is working if solely done in PHP but I am getting an error when I try to use java to encrypt that says 'java.lang.Exception: [encrypt] Input length not multiple of 16 bytes
' and when I try to decrypt the image encrypted by php I get : 'java.lang.Exception: [decrypt] For input string: "nu"
'
Here is my code:
Mcrypt php class by serpro:
<?php
class MCrypt
{
private $iv = 'fedcba9876543210';
private $key = '0123456789abcdef';
function __construct()
{
}
function encrypt($str, $isBinary = false) {
$iv = $this->iv;
$str = $isBinary ? $str : utf8_decode($str);
$td = mcrypt_module_open('rijndael-128', ' ', 'cbc', $iv);
mcrypt_generic_init($td, $this->key, $iv);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $isBinary ? $encrypted : bin2hex($encrypted);
}
function decrypt($code, $isBinary = false) {
$code = $isBinary ? $code : $this->hex2bin($code);
$iv = $this->iv;
$td = mcrypt_module_open('rijndael-128', ' ', 'cbc', $iv);
mcrypt_generic_init($td, $this->key, $iv);
$decrypted = mdecrypt_generic($td, $code);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $isBinary ? trim($decrypted) : utf8_encode(trim($decrypted));
}
protected function hex2bin($hexdata) {
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
My Php class to encrypt images:
<?php
require_once('MCrypt.php');
$file = file_get_contents($argv[1]);
$mcrypt = new MCrypt();
$encrypted = $mcrypt->encrypt($file, true); //true to set is as binary
file_put_contents($argv[1], $encrypted);
serpro JAVA class:
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MCrypt {
static char[] HEX_CHARS = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public MCrypt()
{
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
System.out.println("after if");
byte[] decrypted = null;
try {
System.out.println("in try");
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
System.out.println("2");
decrypted = cipher.doFinal(hexToBytes(code));
System.out.println("3");
//Remove trailing zeroes
if( decrypted.length > 0)
{
System.out.println("in if");
int trim = 0;
for( int i = decrypted.length - 1; i >= 0; i-- ) if( decrypted[i] == 0 ) trim++;
if( trim > 0 )
{
byte[] newArray = new byte[decrypted.length - trim];
System.arraycopy(decrypted, 0, newArray, 0, decrypted.length - trim);
decrypted = newArray;
}
}
System.out.println("after if");
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] buf)
{
char[] chars = new char[2 * buf.length];
for (int i = 0; i < buf.length; ++i)
{
chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
System.out.println("else");
int len = str.length() / 2;
System.out.println(len);
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
System.out.println(str.substring(i*2,i*2+2));
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
System.out.println("in for");
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = 0;
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
My Java class:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Decrypt {
public static void main(String[] args) {
File file = new File(args[0]);
FileInputStream fin = null;
try {
fin = new FileInputStream(file);
byte fileContent[] = new byte[(int) file.length()];
fin.read(fileContent);
String fileToDecrypt = new String(fileContent);
MCrypt crypter = new MCrypt();
String decrypted = new String(crypter.decrypt(fileToDecrypt));
System.out.println(decrypted);
} catch (FileNotFoundException e) {
System.out.println("File not found" + e);
} catch (IOException ioe) {
System.out.println("Exception while reading file " + ioe);
} catch (Exception e){
System.out.println("An exception occured: " + e);
} finally {
try {
if (fin != null) {
fin.close();
}
} catch (IOException ioe) {
System.out.println("Error while closing stream: " + ioe);
}
}
}
}
String fileToDecrypt = null;
while ((currentLine = input.readLine()) != null) {
fileToDecrypt = fileToDecrypt + currentLine;
}
This will give you a String that starts with "n", "u", "l", "l".
Try to initialize fileToDecrypt to an empty String.
Or even better, use a StringBuilder.
decrypted = new String(crypter.decrypt(fileToDecrypt));
Isn't this supposed to be a JPEG image? You cannot convert that to a String.
Use a byte[]
instead.
(The same issue may apply to the encrypted input data that you try to read into Strings line by line. If this is binary data, you cannot use String there, either).