PHP AES шифрует/расшифровывает
Я нашел пример для en/decoding строк в PHP. Сначала он выглядит очень хорошо, но он не будет работать: - (
Кто-нибудь знает, в чем проблема?
$Pass = "Passwort";
$Clear = "Klartext";
$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypted: ".$crypted."</br>";
$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypted: ".$newClear."</br>";
function fnEncrypt($sValue, $sSecretKey) {
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sDecrypted, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
function fnDecrypt($sValue, $sSecretKey) {
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sEncrypted), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
Результат:
Зашифровано: boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=
Расшифровка: —‚(ÑÁ ^ yË~F'¸®Ó–í œð2Á_B‰Â—
Ответы
Ответ 1
$sDecrypted
и $sEncrypted
были undefined в вашем коде. См. Решение, которое работает (, но не безопасно!):
STOP!
Этот пример небезопасен! Не используйте его!
$Pass = "Passwort";
$Clear = "Klartext";
$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypred: ".$crypted."</br>";
$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypred: ".$newClear."</br>";
function fnEncrypt($sValue, $sSecretKey)
{
return rtrim(
base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey, $sValue,
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND)
)
), "\0"
);
}
function fnDecrypt($sValue, $sSecretKey)
{
return rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey,
base64_decode($sValue),
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND
)
), "\0"
);
}
Но в этом коде есть и другие проблемы, которые делают его небезопасным, в частности использование ECB (который не является режимом шифрования, только строительным блоком, поверх которого могут быть определены режимы шифрования). См. Fab Sa answer для быстрого устранения худших проблем и Скотт ответ за то, как это сделать правильно.
Ответ 2
Как правило, плохая идея написать свою собственную криптографию, если у вас нет опыта в реализации криптографии других людей.
Ни один из примеров здесь аутентифицирует зашифрованный текст, что делает их уязвимыми для бит-переписывающих атак.
Если вы можете установить расширения PECL, libsodium еще лучше
<?php
// PECL libsodium 0.2.1 and newer
/**
* Encrypt a message
*
* @param string $message - message to encrypt
* @param string $key - encryption key
* @return string
*/
function safeEncrypt($message, $key)
{
$nonce = \Sodium\randombytes_buf(
\Sodium\CRYPTO_SECRETBOX_NONCEBYTES
);
return base64_encode(
$nonce.
\Sodium\crypto_secretbox(
$message,
$nonce,
$key
)
);
}
/**
* Decrypt a message
*
* @param string $encrypted - message encrypted with safeEncrypt()
* @param string $key - encryption key
* @return string
*/
function safeDecrypt($encrypted, $key)
{
$decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
return \Sodium\crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
}
Затем, чтобы проверить это:
<?php
// This refers to the previous code block.
require "safeCrypto.php";
// Do this once then store it somehow:
$key = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';
$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);
var_dump($ciphertext);
var_dump($plaintext);
Это можно использовать в любой ситуации, когда вы передаете данные клиенту (например, зашифрованные файлы cookie для сеансов без серверного хранилища, зашифрованные параметры URL и т.д.) с достаточно высокой степенью уверенности, которую конечный пользователь не может расшифровать или надежно вмешиваться в него.
Так как libsodium является межплатформенным, это также упрощает общение с PHP, например, Java-приложения или собственные мобильные приложения.
Примечание. Если вам необходимо добавить зашифрованные файлы cookie с помощью libsodium для вашего приложения, мой работодатель Paragon Initiative Enterprises разрабатывает библиотеку под названием Halite, который делает все это для вас.
Ответ 3
Если вы не хотите использовать сильную зависимость для чего-то решаемого в 15 строках кода, используйте встроенные функции OpenSSL. Большинство установок PHP поставляются с OpenSSL, который обеспечивает быстрое, совместимое и безопасное шифрование AES в PHP. Что ж, это безопасно, если вы следуете передовым методам.
Следующий код:
- использует AES256 в режиме CBC
- совместим с другими реализациями AES, но не mcrypt, поскольку mcrypt использует PKCS # 5 вместо PKCS # 7.
- генерирует ключ из предоставленного пароля, используя SHA256
- генерирует hmac-хэш зашифрованных данных для проверки целостности
- генерирует случайный IV для каждого сообщения
- добавляет IV (16 байтов) и хеш (32 байта) к зашифрованному тексту
- должен быть в безопасности
IV является публичной информацией и должна быть случайной для каждого сообщения. Хеш гарантирует, что данные не были подделаны.
function encrypt($plaintext, $password) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
function decrypt($ivHashCiphertext, $password) {
$method = "AES-256-CBC";
$iv = substr($ivHashCiphertext, 0, 16);
$hash = substr($ivHashCiphertext, 16, 32);
$ciphertext = substr($ivHashCiphertext, 48);
$key = hash('sha256', $password, true);
if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}
Использование:
$encrypted = encrypt('Plaintext string.', 'password'); // this yields a binary string
echo decrypt($encrypted, 'password');
// decrypt($encrypted, 'wrong password') === null
редактирование: обновлено для использования hash_equals
и добавлено IV к хешу.
Ответ 4
Для информации MCRYPT_MODE_ECB
не используется IV (вектор инициализации). Режим ECB делит ваше сообщение на блоки, и каждый блок зашифровывается отдельно. Я действительно не рекомендую.
Режим CBC использует IV, чтобы каждое сообщение было уникальным. CBC рекомендуется и должен использоваться вместо ECB.
Пример:
<?php
$password = "myPassword_!";
$messageClear = "Secret message";
// 32 byte binary blob
$aes256Key = hash("SHA256", $password, true);
// for good entropy (for MCRYPT_RAND)
srand((double) microtime() * 1000000);
// generate random iv
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND);
$crypted = fnEncrypt($messageClear, $aes256Key);
$newClear = fnDecrypt($crypted, $aes256Key);
echo
"IV: <code>".$iv."</code><br/>".
"Encrypred: <code>".$crypted."</code><br/>".
"Decrypred: <code>".$newClear."</code><br/>";
function fnEncrypt($sValue, $sSecretKey) {
global $iv;
return rtrim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_CBC, $iv)), "\0\3");
}
function fnDecrypt($sValue, $sSecretKey) {
global $iv;
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_CBC, $iv), "\0\3");
}
Вы должны запастись IV, чтобы декодировать каждое сообщение (IV не являются секретными). Каждое сообщение уникально, потому что каждое сообщение имеет уникальный IV.
Ответ 5
Несколько важных замечаний, которые следует учитывать при шифровании AES:
- Никогда не используйте обычный текст в качестве ключа шифрования. Всегда хешируйте обычный текстовый ключ, а затем используйте для шифрования.
- Всегда используйте Random IV (вектор инициализации) для шифрования и дешифрования. Важное значение имеет правильная рандомизация.
- Как уже упоминалось выше, не используйте ecb, используйте
CBC
вместо этого.
Ответ 6
Если вы используете MCRYPT_RIJNDAEL_128, попробуйте rtrim($output, "\0\3")
. Если длина строки меньше 16, функция decrypt вернет строку длиной 16 символов, добавив 03 в конец.
Вы можете легко проверить это, например. попробовав:
$string = "TheString";
$decrypted_string = decrypt_function($stirng, $key);
echo bin2hex($decrypted_string)."=".bin2hex("TheString");
Ответ 7
Я использую код вещи для CCAVenue Payment Getway
class AES {
public function encrypt($plainText, $key) {
$secretKey = $this->hextobin(md5($key));
$initVector = pack("C*", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
$openMode = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
$plainPad = $this->pkcs5_pad($plainText, $blockSize);
if (mcrypt_generic_init($openMode, $secretKey, $initVector) != -1) {
$encryptedText = mcrypt_generic($openMode, $plainPad);
mcrypt_generic_deinit($openMode);
}
return bin2hex($encryptedText);
}
public function decrypt($encryptedText, $key) {
$secretKey = $this->hextobin(md5($key));
$initVector = pack("C*", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
$encryptedText = $this->hextobin($encryptedText);
$openMode = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
mcrypt_generic_init($openMode, $secretKey, $initVector);
$decryptedText = mdecrypt_generic($openMode, $encryptedText);
$decryptedText = rtrim($decryptedText, "\0");
mcrypt_generic_deinit($openMode);
return $decryptedText;
}
//*********** Padding Function *********************
public function pkcs5_pad($plainText, $blockSize) {
$pad = $blockSize - (strlen($plainText) % $blockSize);
return $plainText . str_repeat(chr($pad), $pad);
}
//********** Hexadecimal to Binary function for php 4.0 version ********
public function hextobin($hexString) {
$length = strlen($hexString);
$binString = "";
$count = 0;
while ($count < $length) {
$subString = substr($hexString, $count, 2);
$packedString = pack("H*", $subString);
if ($count == 0) {
$binString = $packedString;
} else {
$binString .= $packedString;
}
$count += 2;
}
return $binString;
}
}
Пользователь кода
$obj = new AES();
$key = "XXXXXXXXXXXXXXXX";
$plainText = "Hello World";
$encryptedText = $obj->encrypt($plainText, $key);
$rcvdString=$obj->decrypt($encryptedText,$key);
Ответ 8
Это рабочее решение AES encryption
- реализовано с использованием openssl
. Он использует режим цепочки блоков шифрования (CBC-Mode). Таким образом, наряду с data
и key
, вы можете указать iv
и block size
<?php
class AESEncryption {
protected $key;
protected $data;
protected $method;
protected $iv;
/**
* Available OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
*
* @var type $options
*/
protected $options = 0;
/**
*
* @param type $data
* @param type $key
* @param type $iv
* @param type $blockSize
* @param type $mode
*/
public function __construct($data = null, $key = null, $iv = null, $blockSize = null, $mode = 'CBC') {
$this->setData($data);
$this->setKey($key);
$this->setInitializationVector($iv);
$this->setMethod($blockSize, $mode);
}
/**
*
* @param type $data
*/
public function setData($data) {
$this->data = $data;
}
/**
*
* @param type $key
*/
public function setKey($key) {
$this->key = $key;
}
/**
* CBC 128 192 256
CBC-HMAC-SHA1 128 256
CBC-HMAC-SHA256 128 256
CFB 128 192 256
CFB1 128 192 256
CFB8 128 192 256
CTR 128 192 256
ECB 128 192 256
OFB 128 192 256
XTS 128 256
* @param type $blockSize
* @param type $mode
*/
public function setMethod($blockSize, $mode = 'CBC') {
if($blockSize==192 && in_array('', array('CBC-HMAC-SHA1','CBC-HMAC-SHA256','XTS'))){
$this->method=null;
throw new Exception('Invalid block size and mode combination!');
}
$this->method = 'AES-' . $blockSize . '-' . $mode;
}
/**
*
* @param type $data
*/
public function setInitializationVector($iv) {
$this->iv = $iv;
}
/**
*
* @return boolean
*/
public function validateParams() {
if ($this->data != null &&
$this->method != null ) {
return true;
} else {
return FALSE;
}
}
//it must be the same when you encrypt and decrypt
protected function getIV() {
return $this->iv;
}
/**
* @return type
* @throws Exception
*/
public function encrypt() {
if ($this->validateParams()) {
return trim(openssl_encrypt($this->data, $this->method, $this->key, $this->options,$this->getIV()));
} else {
throw new Exception('Invalid params!');
}
}
/**
*
* @return type
* @throws Exception
*/
public function decrypt() {
if ($this->validateParams()) {
$ret=openssl_decrypt($this->data, $this->method, $this->key, $this->options,$this->getIV());
return trim($ret);
} else {
throw new Exception('Invalid params!');
}
}
}
Пример использования:
<?php
$data = json_encode(['first_name'=>'Dunsin','last_name'=>'Olubobokun','country'=>'Nigeria']);
$inputKey = "W92ZB837943A711B98D35E799DFE3Z18";
$iv = "tuqZQhKP48e8Piuc";
$blockSize = 256;
$aes = new AESEncryption($data, $inputKey, $iv, $blockSize);
$enc = $aes->encrypt();
$aes->setData($enc);
$dec=$aes->decrypt();
echo "After encryption: ".$enc."<br/>";
echo "After decryption: ".$dec."<br/>";
Ответ 9
Если вы используете PHP> = 7.2, рассмотрите возможность использования встроенного расширения ядра натрия для шифрования.
Найти больше информации здесь - http://php.net/manual/en/intro.sodium.php
.