Есть несколько проблем с Java-кодом:
В PHP-коде используется AES-256, таким образом, ключ должен иметь длину 32 байта.Более короткие клавиши автоматически дополняются нулями.Это происходит в вашем PHP-коде, поскольку длина ключа SiadajerSiadajer
составляет всего 16 байт.Клавиши также должны быть выполнены в Java-коде.Для этого, например, можно использовать следующий Java-метод:
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
При Cipher.getInstance("AES")
ECB
-режим и PKCS5Padding
по умолчанию выбраны .CBC
-мод должен быть явно указан в Java-коде с помощью
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
В PHP-методе openssl_encrypt
четвертый параметр $options
установлен на 0
, что означает, что возвращаемые данные закодированы в Base64.Таким образом, в Java-коде данные должны быть декодированы Base64 перед расшифровкой:
byte[]encryptedData = Base64.decode(text);
Поскольку используется CBC
-режим, IV
шифрования долженбыть принятым во внимание.Возможный подход заключается в кодировании Base64 IV
в PHP-коде (после шифрования) с помощью
$encodedIV = base64_encode($iv);
и передаче этого значения в Java-метод decrypt
в качестве второго параметра.Здесь IV
должен быть декодирован и использован для расшифровки:
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
...
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
В PHP-методе openssl_encrypt
четвертый параметр $options
имеет значение 0
Это означает, что используется заполнение по умолчанию (PKCS7
).Более того, в PHP-методе encrypt
реализован вид пользовательского заполнения (кстати: название метода не подходит, так как он не шифруется), и, следовательно, он дополняется дважды.Следовательно, после расшифровки пользовательский отступ (который может состоять из пробелов) должен быть удален в Java-коде:
byte[] unpaddedData = unpad(decryptedData);
с
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
Всего:
public String decrypt(String text, String ivEncoded) throws Exception {
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Base64 decode data
byte[]encryptedData = Base64.decode(text);
// Base64 decode iv
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
Тест:
Ввод PHP-кода: обычный текст ($data
), клавиша ($key
):
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
Вывод PHP-кода: IV в кодировке Base64 ($encodedIV
), зашифрованные данные ($name
):
$encodedIV = 'Dg+Zs3mqIJeDOOEPMT5F4Q==';
$name = '8dXjeQhx2WSswQOQXLcyMKNpa5s413yI2Ku8WiIB/xtA2pEjrKcl5kWtrOh9k4A12Jl0N/z6tH67Wybhp/OwTi1NtiJOZxl3w6YQufE29oU=';
Если этот вывод используется в качестве ввода для метода Java decrypt
дешифрованные данные равны обычному тексту.
Что касается PHP-кода, я бы предложил удалить либо пользовательский, либо стандартный (PKCS7
) отступ (если есть выбор).Последнее может быть достигнуто путем использования флага OPENSSL_ZERO_PADDING
в качестве четвертого параметра в методе openssl_encrypt
(примечание: этот флаг означает не «заполнение с нулевыми значениями», а «отсутствие заполнения»).Если пользовательский отступ сохраняется, вы должны как минимум переименовать PHP-методы encrypt
и decrypt
в pad
и unpad
(или что-то подобное), соответственно.
Как указано уже вкомментарии, режим GCM
может быть лучшим выбором, чем режим CBC
.Однако было бы полезно получить информацию об основах до кодирования, например, разница между CBC
- и GCM
-режимом , объяснение режима GCM здесь и здесь и ловушек вместе с режимом GCM (GCM безопасен, но только если вы следуете определенным рекомендациям, например uniqe IV / nonce для каждого сообщения, зашифрованного тем же ключом).
Вы можете использовать PHP-метод openssl_get_cipher_methods , чтобы узнать допустимые AES-режимы, поддерживаемые в PHP.Это объясняется более подробно здесь .Для AES-256 и AES-режима GCM
необходимо указать aes-256-gcm
(с строчными (!) Буквами).Предположительно, поэтому вы получаете ошибку «Неизвестный алгоритм шифрования».
РЕДАКТИРОВАТЬ:
Вы можете использовать для шифрования следующий PHP-код (который являетсянемного измененная версия вашего PHP-кода из вопроса):
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, 0, $iv);
print base64_encode($iv)."\n".$encryptedData."\n";
РЕДАКТИРОВАТЬ 2:
IV
и зашифрованные данные могут быть объединены до или после Base64-кодировки.Первый более эффективен, и поэтому я реализовал этот вариант.Однако некоторые изменения необходимы в PHP- и Java-коде, и поэтому я публикую все методы, которые были изменены.
PHP-код становится:
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$joinedData = $iv.$encryptedData;
$encodedJoinedData = base64_encode($joinedData);
print $encodedJoinedData."\n";
И Jave-метод decrypt
становится:
public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
и пример для Java-метода main
:
public static void main(String[] args) throws Exception {
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
Использование выглядит следующим образом:
Шифрование вашего простого текста с помощью PHP-код.В приведенном выше примере (PHP-код) обычный текст имеет вид
$data = 'This is a plain text which needs to be encrypted...';
Затем передайте строку, содержащуюся в $ encodedJoinedData, в Java-метод decrypt
.В приведенном выше примере (main
-метод) строка имеет вид
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
Java-метод decrypt
предоставит исходный простой текст.
Последнее замечание : Если вы решили удалить (избыточный) пользовательский отступ, замените в PHP-коде строку
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
с помощью
$encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
и удалите pad
- и unpad
-метод.
В Java-коде замените строки
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
на
return new String(decryptedData);
и удалите unpad
-метод.
Edit 3:
InvalidKeyException (Illegal key size)
, упомянутый в разделе Edit2 разделавопрос уже обсуждался, например, здесь InvalidKeyException Недопустимый размер ключа и здесь Безопасность Java: недопустимый размер ключа или параметры по умолчанию? .