PHP Java AES CBC Шифрование Различные результаты

PHP Функция:

$privateKey = "1234567812345678"; $iv = "1234567812345678"; $data = "Test string"; $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv); echo(base64_encode($encrypted)); Result: iz1qFlQJfs6Ycp+gcc2z4w== 

Функция Java

 public static String encrypt() throws Exception{ try{ String data = "Test string"; String key = "1234567812345678"; String iv = "1234567812345678"; javax.crypto.spec.SecretKeySpec keyspec = new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES"); javax.crypto.spec.IvParameterSpec ivspec = new javax.crypto.spec.IvParameterSpec(iv.getBytes()); javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/NoPadding"); cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keyspec, ivspec); byte[] encrypted = cipher.doFinal(data.getBytes()); return new sun.misc.BASE64Encoder().encode(encrypted); }catch(Exception e){ return null; } 

}

возвращает null.

Обратите внимание, что нам не разрешено изменять код PHP. Может ли кто-нибудь помочь нам получить те же результаты в Java? Большое спасибо.

У вас было бы лучшее представление о том, что происходит, если вы не просто проглотили возможное Exception в своей процедуре encrypt() . Если ваша функция возвращает null то ясно, что произошло исключение, и вам нужно знать, что это было.

Фактически, исключение:

 javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:854) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:828) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676) at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313) at javax.crypto.Cipher.doFinal(Cipher.java:2087) at Encryption.encrypt(Encryption.java:20) at Encryption.main(Encryption.java:6) 

И, конечно же, ваш открытый текст содержит всего 11 символов Java, которые в вашей кодировке по умолчанию будут составлять 11 байт.

Вам нужно проверить, что на самом деле делает функция PHP mcrypt_encrypt . Поскольку он работает, он явно использует некоторую схему заполнения. Вам нужно выяснить, какой из них он использует, и использовать его в своем Java-коде.

Хорошо. Я просмотрел страницу man для mcrypt_encrypt . В нем говорится:

Данные, которые будут зашифрованы с данным шифром и режимом. Если размер данных не равен n * blocksize , данные будут дополнены с помощью \0 .

Поэтому вам нужно воспроизвести это на Java. Вот один из способов:

 import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class Encryption { public static void main(String args[]) throws Exception { System.out.println(encrypt()); } public static String encrypt() throws Exception { try { String data = "Test string"; String key = "1234567812345678"; String iv = "1234567812345678"; Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); int blockSize = cipher.getBlockSize(); // We need to pad with zeros to a multiple of the cipher block size, // so first figure out what the size of the plaintext needs to be. byte[] dataBytes = data.getBytes(); int plaintextLength = dataBytes.length; int remainder = plaintextLength % blockSize; if (remainder != 0) { plaintextLength += (blockSize - remainder); } // In java, primitive arrays of integer types have all elements // initialized to zero, so no need to explicitly zero any part of // the array. byte[] plaintext = new byte[plaintextLength]; // Copy our actual data into the beginning of the array. The // rest of the array is implicitly zero-filled, as desired. System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length); SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); byte[] encrypted = cipher.doFinal(plaintext); return new sun.misc.BASE64Encoder().encode(encrypted); } catch (Exception e) { e.printStackTrace(); return null; } } } 

И когда я бегу, я получаю:

 iz1qFlQJfs6Ycp+gcc2z4w== 

что и сделала ваша PHP-программа.


Обновление (12 июня 2016 года) : Начиная с Java 8, JavaSE, наконец, поставляется с документированным кодеком base64. Поэтому вместо

 return new sun.misc.BASE64Encoder().encode(encrypted); 

вы должны сделать что-то вроде

 return Base64.Encoder.encodeToString(encrypted); 

В качестве альтернативы, используйте стороннюю библиотеку (например, commons-codec ) для кодирования / декодирования base64, а не используя недокументированный внутренний метод.