Я думал, что понял это, но моя программа не расшифровывает и говорит, что ключ неправильный, поэтому я понимаю, что мне нужна помощь. Я думал, что алгоритм пошел:
Шифрование:
Дешифрирование:
Это не правильно? (Изменить: Оказывается, это так, но это был слишком высокий уровень — моя проблема была более конкретной.)
Изменить: вот мой код:
<?php
$GLOBALS['key_size'] = 32; // 256 bits
class WeakCryptographyException extends Exception {
public function errorMessage() {
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b>There was a problem creating strong pseudo-random bytes: system may be broken or old.';
return $errorMsg;
}
}
class FailedCryptographyException extends Exception {
public function errorMessage() {
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b>There was a problem with encryption/decryption.';
return $errorMsg;
}
}
class InvalidHashException extends Exception {
public function errorMessage() {
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b>Password verification failed.';
return $errorMsg;
}
}
function generate_key_from_password($password) {
$iterations = 100000;
$salt = openssl_random_pseudo_bytes($GLOBALS['key_size'], $strong);
$output = hash_pbkdf2("sha256", $password, $salt, $iterations, $GLOBALS['key_size'], true);
if ($strong) {
return $output;
} else {
// system did not use a cryptographically strong algorithm to produce the pseudo-random bytes
throw new WeakCryptographyException();
}
}
/** Encrypts the input data with Authenticated Encryption. We specifically use
* openssl_encrypt($data, 'AES-256-CBC', $encryption_key, OPENSSL_RAW_DATA, $iv), where $iv is a 256-bit nonce
* generated with openssl_random_pseudo_bytes. Then we hash the output with bcrypt and prepend the hash and iv to
* the ciphertext to create an 'authenticated ciphertext' that can be fed directly into the my_decrypt method.
*
* @param $data string; The data to be encrypted
* @param $encryption_key string; A 256-bit key (which PHP reads as a string of characters)
* @return string The authenticated ciphertext, with the format: $hash . $iv . $ciphertext
* @throws FailedCryptographyException If there are errors during encryption
* @throws WeakCryptographyException If the openssl_random_pseudo_bytes method fails to use a cryptographically strong
* algorithm to produce pseudo-random bytes.
*
* Note that in creating a hash for the ciphertext, we use bcrypt instead of sha2. In particular, the difference in lines is:
* bcrypt: password_hash($ciphertext, PASSWORD_DEFAULT);
* sha2: hash_hmac('sha256', $ciphertext, $auth_key, true);
*
* And we chose this despite the fact that sha2 is the only acceptable hashing algorithm for NIST, because:
* 1. bcrypt is also widely considered a cryptographically secure hashing algorithm.
* 2. sha2 is not supported by PHP 5's password_hash method and bcrypt is.
* 3. PHP's password_verify method uses a hash created by the password_hash method and compares hashes in a way that is
* safe against timing attacks. There is no known way to make this comparison for other hashes in PHP.
*/
function my_openssl_encrypt($data, $encryption_key) {
$iv_size = 16; // 128 bits to match the block size for AES
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
if (!$strong) {
// system did not use a cryptographically strong algorithm to produce the bytes, don't consider them pseudo-random
throw new WeakCryptographyException();
}
$ciphertext = openssl_encrypt(
$data, // data
'AES-256-CBC', // cipher and mode
$encryption_key, // secret key
OPENSSL_RAW_DATA, // options: we use openssl padding
$iv // initialisation vector
);
if (!$ciphertext) {
$errormes = "";
while ($msg = openssl_error_string())
$errormes .= $msg . "<br />";
throw new FailedCryptographyException($errormes);
}
$auth = password_hash($ciphertext, PASSWORD_DEFAULT);
$auth_enc_name = $auth . $iv . $ciphertext;
return $auth_enc_name;
}
/** Decrypts a ciphertext encrypted with the method my_openssl_encrypt. First checks if the hash of the ciphertext
* matches the hash supplied in the input ciphertext, then decrypts the message if so. We specifically use
* openssl_decrypt($enc_name, 'AES-256-CBC', $encryption_key, OPENSSL_RAW_DATA, $iv), where $iv is a 256-bit nonce
* stored with the ciphertext.
*
* @param $ciphertext string; An authenticated ciphertext produced by my_openssl_encrypt
* @param $encryption_key string; A 256-bit key (which PHP reads as a string of characters)
* @return string The decrypted plaintext
* @throws FailedCryptographyException If there are errors during decryption
* @throws InvalidHashException If the password hash doesn't match the stored hash (this will almost always happen when
* any bits in the ciphertext are changed)
*/
function my_openssl_decrypt($ciphertext, $encryption_key) {
// verification
$auth = substr($ciphertext, 0, 60);
$iv = substr($ciphertext, 60, 16);
$enc_name = substr($ciphertext, 76);
if (password_verify($enc_name, $auth)) {
// perform decryption
$output = openssl_decrypt(
$enc_name,
'AES-256-CBC',
$encryption_key,
OPENSSL_RAW_DATA,
$iv
);
if (!$output) {
$errormes = "";
while ($msg = openssl_error_string())
$errormes .= $msg . "<br />";
throw new FailedCryptographyException($errormes);
}
return $output;
} else {
throw new InvalidHashException();
}
}
// Testing
function testEnc($message)
{
$encryption_key = generate_key_from_password("123456");
$auth_ciphertext = my_openssl_encrypt($message, $encryption_key);
$encryption_key = generate_key_from_password("123456");
$plaintext = my_openssl_decrypt($auth_ciphertext, $encryption_key);
echo "<p>Original message: " . $message .
"</p><p>Encryption (hex): " . bin2hex($auth_ciphertext) .
"</p><p>Plaintext: " . $plaintext . "</p>";
echo "<p>Bytes of input: " . (strlen($message) * 2) .
"<br />Bytes of ciphertext: " . (strlen($auth_ciphertext) * 2) . "</p>";
}
echo '<p>Test 1: ';
testEnc('Hello World');
echo '</p>';
Проблема в функции:
function generate_key_from_password($password)
линия:
$salt = openssl_random_pseudo_bytes($GLOBALS['key_size'], $strong);
Для получения одного и того же ключа необходимо использовать одну и ту же соль.
Соль должна быть создана за пределами generate_key_from_password
функция и передается, и она должна быть той же солью для шифрования и дешифрования. Обычно это делается путем создания соли в функции шифрования, передачи ее в функцию PBKDF2 и добавления соли к зашифрованному выходу таким же образом, как в iv. Затем та же соль доступна для функции дешифрования.
Именно такие мелочи делают безопасным использование шифрования. Увидеть RNCryptor-PHP а также RNCryptor-Spec для примера, который также включает в себя аутентификацию, количество итераций и версию.
Пропустите шаг 3, это не обязательно.
Убедитесь, что ключ и iv точно правильной длины. Убедитесь, что вы используете режим CBC и PKCS # 7 (или PKCS # 5).
Используйте необязательный 5-й параметр $length
за hash_pbkdf2()
:
var_dump(hash_pbkdf2("sha256", "foobar", "salty", 100));
// string(64) "5d808ee6539c7d0437e857a586c844900bf0969d1af70aea4c3848550d9038ab"
var_dump(hash_pbkdf2("sha256", "foobar", "salty", 100, 32));
// string(32) "5d808ee6539c7d0437e857a586c84490"
var_dump(hash_pbkdf2("sha256", "foobar", "salty", 100, 128));
// string(128) "5d808ee6539c7d0437e857a586c844900bf0969d1af70aea4c3848550d9038abb2853bf0cf24c9d010555394f958fa647a04b232f993c35916977b4ef5a57dcc"
Вам, вероятно, также нужен необработанный вывод, поэтому прочитайте ссылку на вышеупомянутую страницу документации для метода и предостережения по этому поводу.