如何在 PHP 中加密字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1788150/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I encrypt a string in PHP?
提问by Pascal MARTIN
I want to make an encryption function that should have some secret key. Something like the following:
我想做一个应该有一些秘密密钥的加密函数。类似于以下内容:
function encrypt($string) {
$key = "mastermind";
$enc = encryptfunc($string, $key);
return $enc;
}
The same thing should apply for decryption.
同样的事情应该适用于解密。
回答by Eugene Fidelin
Here is simple, but secure implementation of AES-256encryption in CBC mode that uses PBKDF2 to create encryption key out of plain-text password and HMAC to authenticate the encrypted message.
这是在 CBC 模式下AES-256加密的简单但安全的实现,它使用 PBKDF2 从纯文本密码和 HMAC 中创建加密密钥来验证加密消息。
It works with PHP 5.3 and higher.
它适用于 PHP 5.3 及更高版本。
/**
* Implements AES-256 encryption/decryption in CBC mode.
*
* PBKDF2 is used for creation of encryption key.
* HMAC is used to authenticate the encrypted message.
*
* Requires PHP 5.3 and higher
*
* Gist: https://gist.github.com/eugef/3d44b2e0a8a891432c65
*/
class McryptCipher
{
const PBKDF2_HASH_ALGORITHM = 'SHA256';
const PBKDF2_ITERATIONS = 64000;
const PBKDF2_SALT_BYTE_SIZE = 32;
// 32 is the maximum supported key size for the MCRYPT_RIJNDAEL_128
const PBKDF2_HASH_BYTE_SIZE = 32;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $secureEncryptionKey;
/**
* @var string
*/
private $secureHMACKey;
/**
* @var string
*/
private $pbkdf2Salt;
public function __construct($password)
{
$this->password = $password;
}
/**
* Compares two strings.
*
* This method implements a constant-time algorithm to compare strings.
* Regardless of the used implementation, it will leak length information.
*
* @param string $knownHash The string of known length to compare against
* @param string $userHash The string that the user can control
*
* @return bool true if the two strings are the same, false otherwise
*
* @see https://github.com/symfony/security-core/blob/master/Util/StringUtils.php
*/
private function equalHashes($knownHash, $userHash)
{
if (function_exists('hash_equals')) {
return hash_equals($knownHash, $userHash);
}
$knownLen = strlen($knownHash);
$userLen = strlen($userHash);
if ($userLen !== $knownLen) {
return false;
}
$result = 0;
for ($i = 0; $i < $knownLen; $i++) {
$result |= (ord($knownHash[$i]) ^ ord($userHash[$i]));
}
// They are only identical strings if $result is exactly 0...
return 0 === $result;
}
/**
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*
* @param string $algorithm The hash algorithm to use. Recommended: SHA256
* @param string $password The password
* @param string $salt A salt that is unique to the password
* @param int $count Iteration count. Higher is better, but slower. Recommended: At least 1000
* @param int $key_length The length of the derived key in bytes
* @param bool $raw_output If true, the key is returned in raw binary format. Hex encoded otherwise
* @return string A $key_length-byte key derived from the password and salt
*
* @see https://defuse.ca/php-pbkdf2.htm
*/
private function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if (!in_array($algorithm, hash_algos(), true)) {
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
}
if ($count <= 0 || $key_length <= 0) {
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
}
if (function_exists('hash_pbkdf2')) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length *= 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, '', true));
$block_count = ceil($key_length / $hash_length);
$output = '';
for ($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack('N', $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if ($raw_output) {
return substr($output, 0, $key_length);
} else {
return bin2hex(substr($output, 0, $key_length));
}
}
/**
* Creates secure PBKDF2 derivatives out of the password.
*
* @param null $pbkdf2Salt
*/
private function derivateSecureKeys($pbkdf2Salt = null)
{
if ($pbkdf2Salt) {
$this->pbkdf2Salt = $pbkdf2Salt;
}
else {
$this->pbkdf2Salt = mcrypt_create_iv(self::PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM);
}
list($this->secureEncryptionKey, $this->secureHMACKey) = str_split(
$this->pbkdf2(self::PBKDF2_HASH_ALGORITHM, $this->password, $this->pbkdf2Salt, self::PBKDF2_ITERATIONS, self::PBKDF2_HASH_BYTE_SIZE * 2, true),
self::PBKDF2_HASH_BYTE_SIZE
);
}
/**
* Calculates HMAC for the message.
*
* @param string $message
* @return string
*/
private function hmac($message)
{
return hash_hmac(self::PBKDF2_HASH_ALGORITHM, $message, $this->secureHMACKey, true);
}
/**
* Encrypts the input text
*
* @param string $input
* @return string Format: hmac:pbkdf2Salt:iv:encryptedText
*/
public function encrypt($input)
{
$this->derivateSecureKeys();
$mcryptIvSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
// By default mcrypt_create_iv() function uses /dev/random as a source of random values.
// If server has low entropy this source could be very slow.
// That is why here /dev/urandom is used.
$iv = mcrypt_create_iv($mcryptIvSize, MCRYPT_DEV_URANDOM);
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->secureEncryptionKey, $input, MCRYPT_MODE_CBC, $iv);
$hmac = $this->hmac($this->pbkdf2Salt . $iv . $encrypted);
return implode(':', array(
base64_encode($hmac),
base64_encode($this->pbkdf2Salt),
base64_encode($iv),
base64_encode($encrypted)
));
}
/**
* Decrypts the input text.
*
* @param string $input Format: hmac:pbkdf2Salt:iv:encryptedText
* @return string
*/
public function decrypt($input)
{
list($hmac, $pbkdf2Salt, $iv, $encrypted) = explode(':', $input);
$hmac = base64_decode($hmac);
$pbkdf2Salt = base64_decode($pbkdf2Salt);
$iv = base64_decode($iv);
$encrypted = base64_decode($encrypted);
$this->derivateSecureKeys($pbkdf2Salt);
$calculatedHmac = $this->hmac($pbkdf2Salt . $iv . $encrypted);
if (!$this->equalHashes($calculatedHmac, $hmac)) {
trigger_error('HMAC ERROR: Invalid HMAC.', E_USER_ERROR);
}
// mcrypt_decrypt() pads the *RETURN STRING* with nulls ('$c = new McryptCipher('secret key goes here');
$encrypted = $c->encrypt('secret message');
$decrypted = $c->decrypt($encrypted);
') to fill out to n * blocksize.
// rtrim() is used to delete them.
return rtrim(
mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->secureEncryptionKey, $encrypted, MCRYPT_MODE_CBC, $iv),
"$methods = openssl_get_cipher_methods();
var_dump($methods);
$textToEncrypt = "he who doesn't do anything, doesn't go wrong -- Zeev Suraski";
$secretKey = "glop";
echo '<pre>';
foreach ($methods as $method) {
$encrypted = openssl_encrypt($textToEncrypt, $method, $secretKey);
$decrypted = openssl_decrypt($encrypted, $method, $secretKey);
echo $method . ': ' . $encrypted . ' ; ' . $decrypted . "\n";
}
echo '</pre>';
"
);
}
}
Usage:
用法:
bf-ecb: /nyRYCzQPE1sunxSBclxXBd7p7gl1fUnE80gBCS1NM4s3wS1Eho6rFHOOR73V9UtnolYW+flbiCwIKa/DYh5CQ== ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
bf-ofb: M9wwf140zhwHo98k8sj2MEXdogqXEQ+TjN81pebs2tmhNOsfU3jvMy91MBM76dWM7GVjeh95p8oDybDt ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
cast5-cbc: xKgdC1y654PFYW1rIjdevu8MsQOegvJoZx0KmMwb8aCHFmznxIQVy1yvAWR3bZztvGCGrM84WkpbG33pZcxUiQ== ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
cast5-cfb: t8ABR9mPvocRikrX0Kblq2rUXHiVnA/OnjR/mDJDq8+/nn6Z9yfPbpcpRat0lYqfVAcwlypT4A4KNq4S ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
cast5-ecb: xKgdC1y654NIzRl9gJqbhYKtmJoXBoFpgLhwgdtPtYB7VZ1tRHLX0MjErtfREMJBAonp48zngSiTKlsKV0/WhQ== ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
cast5-ofb: t8ABR9mPvofCv9+AKTcRO4Q0doYlavn8zRzLvV3dZk0niO7l20KloA4nUll4VN1B5n89T/IuGh9piPte ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
des-cbc: WrCiOVPU1ipF+0trwXyVZ/6cxiNVft+TK2+vAP0E57b9smf9x/cZlQQ4531aDX778S3YJeP/5/YulADXoHT/+Q== ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
des-cfb: cDDlaifQN+hGOnGJ2xvGna7y8+qRxwQG+1DJBwQm/4abKgdZYUczC4+aOPGesZM1nKXjgoqB4+KTxGNo ; he who doesn't do anything, doesn't go wrong -- Zeev Suraski
Notice about performance
业绩通知
By default mcrypt_create_iv() function uses /dev/random as a source of random values. If server has low entropy this source could be very slow. This is why /dev/urandom is used.
默认情况下, mcrypt_create_iv() 函数使用 /dev/random 作为随机值的来源。如果服务器具有低熵,则此源可能会非常慢。这就是使用 /dev/urandom 的原因。
Here is a good explanation what is the difference between them http://www.onkarjoshi.com/blog/191/device-dev-random-vs-urandom/
这是一个很好的解释,它们之间有什么区别http://www.onkarjoshi.com/blog/191/device-dev-random-vs-urandom/
So, if you are not using this encryption for something critical (I hope you don't) then you can use /dev/urandom to improve encryption performance, otherwise just replace MCRYPT_DEV_URANDOM with MCRYPT_DEV_RANDOM.
因此,如果您没有将这种加密用于重要的事情(我希望您不要),那么您可以使用 /dev/urandom 来提高加密性能,否则只需将 MCRYPT_DEV_URANDOM 替换为 MCRYPT_DEV_RANDOM。
Important security update #1
重要的安全更新 #1
Thanks to @HerrK who pointed out that using a simple hash to create an encryption key is not secure enough - now the PBKDF2 algorithm is used for that (read more about PBKDF2 http://en.wikipedia.org/wiki/PBKDF2).
感谢@HerrK,他指出使用简单的散列来创建加密密钥不够安全 - 现在使用 PBKDF2 算法(阅读有关 PBKDF2 http://en.wikipedia.org/wiki/PBKDF2 的更多信息)。
Implementation of the PBKDF2 algorithm is copied from https://defuse.ca/php-pbkdf2.htm.
PBKDF2 算法的实现是从https://defuse.ca/php-pbkdf2.htm复制的。
Important security update #2
重要的安全更新 #2
Thanks to @Scott who paid attention that encrypted message should be authenticated - now HMAC is used to verify that message was not changed.
感谢@Scott,他注意到应该对加密的消息进行身份验证 - 现在 HMAC 用于验证消息未更改。
回答by Pascal MARTIN
Security Warning: Encryption without authenticationis vulnerable to something called a chosen-ciphertext attack. See Eugene's answerfor a solution that offers authenticated encryption.
安全警告:未经身份验证的加密容易受到称为选择密文攻击的攻击。有关提供经过身份验证的加密的解决方案,请参阅Eugene 的回答。
If you are using PHP >= 5.3, the new openssl_encryptmight help you: It allows encryption of data using a wide range of cypher methods.
如果您使用的是 PHP >= 5.3,新版本openssl_encrypt可能对您有所帮助:它允许使用多种密码方法对数据进行加密。
Those data can later be decrypted with openssl_decrypt, which, obviously, does the exact opposite.
这些数据以后可以用 解密openssl_decrypt,显然,这正好相反。
And if you want to know which cypher functions you can use, openssl_get_cipher_methodswill be helpful ;-)
There are quite a lot of those, it seems ^^
如果你想知道你可以使用哪些密码函数,openssl_get_cipher_methods会很有帮助;-)
有很多,似乎 ^^
Here's a portion of code I posted on my blog some time ago, that should demonstrate the usage of those three functions:
这是我前段时间在我的博客上发布的部分代码,应该演示这三个函数的用法:
function crypt($dataToEncrypt){
$appKey = '%39d15#13P0£df458asdc%/dfr_A!8792*dskjfzaesdfpopdfo45s4dqd8d4fsd+dfd4s"Z1';
$td = mcrypt_module_open(MCRYPT_SERPENT, '', MCRYPT_MODE_CBC, '');
// Creates IV and gets key size
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_DEV_RANDOM);
$ks = mcrypt_enc_get_key_size($td);
// Creates key from application key
$key = substr($appKey, 0, $ks);
// Initialization
mcrypt_generic_init($td, $key, $iv);
// Crypt data
$encrypted = mcrypt_generic($td, $dataToEncrypt);
// Close
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return array($encrypted, $iv);
}
The output I got when writing this was something like that:
我在写这篇文章时得到的输出是这样的:
function decrypt($encryptedData, $iv){
$appKey = '%39d15#13P0£df458asdc%/dfr_A!8792*dskjfzaesdfpopdfo45s4dqd8d4fsd+dfd4s"Z1';
$td = mcrypt_module_open(MCRYPT_SERPENT, '', MCRYPT_MODE_CBC, '');
// Gets key size
$ks = mcrypt_enc_get_key_size($td);
// Creates key from application key
$key = substr($appKey, 0, $ks);
// Initialization
mcrypt_generic_init($td, $key, $iv);
// Decrypt data
$decrypted = mdecrypt_generic($td, $encryptedData);
// Close
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return trim($decrypted);
}
And if you are not using PHP 5.3, you might want to take a look to the Mcryptsection of the manual, and functions such as mcrypt_encrypt;-)
如果您没有使用 PHP 5.3,您可能需要查看手册的Mcrypt部分,以及诸如mcrypt_encrypt;-) 之类的功能
This is an interface to the mcrypt library, which supports a wide variety of block algorithms such as DES, TripleDES, Blowfish (default), 3-WAY, SAFER-SK64, SAFER-SK128, TWOFISH, TEA, RC2 and GOST in CBC, OFB, CFB and ECB cipher modes.
这是 mcrypt 库的接口,它支持多种块算法,例如 CBC 中的 DES、TripleDES、Blowfish(默认)、3-WAY、SAFER-SK64、SAFER-SK128、TWOFISH、TEA、RC2 和 GOST, OFB、CFB 和 ECB 密码模式。
回答by Arkh
I'm not a crypto guy, but I use this kind of things:
我不是一个加密货币的人,但我使用这种东西:
class Cipher
{
/**
----------------------------------------------
Original Code by Eugene Fidelin
----------------------------------------------
**/
private $key;
private $salt;
private $iv;
function __construct()
{
}
function set_salt( $salt )
{
$this->salt = $salt;
}
function generate_salt()
{
$this->salt = mcrypt_create_iv( 32, MCRYPT_DEV_RANDOM ); // abuse IV function for random salt
}
function set_iv( $iv )
{
$this->iv = $iv;
}
function generate_iv()
{
$this->iv = mcrypt_create_iv( mcrypt_get_iv_size( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC ) );
}
function generate_key( $passphrase, $iterations = 10000, $length = 32 )
{
$this->key = hash_pbkdf2 ( 'sha256', $passphrase, $this->salt, $iterations, $length );
}
function get_key()
{
echo $this->key;
}
function encrypt( $plaintext )
{
$ciphertext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $this->key, $plaintext, MCRYPT_MODE_CBC, $this->iv );
$data_return = array();
$data_return['iv'] = base64_encode( $this->iv );
$data_return['salt'] = base64_encode( $this->salt );
$data_return['ciphertext'] = base64_encode( $ciphertext );
return json_encode( $data_return );
}
function decrypt( $data_enciphered, $passphrase )
{
$data_decoded = json_decode( $data_enciphered, TRUE );
$this->set_iv( base64_decode( $data_decoded['iv'] ) );
$this->set_salt( base64_decode( $data_decoded['salt'] ) );
$this->generate_key( $passphrase );
$ciphertext = base64_decode( $data_decoded['ciphertext'] );
return trim( mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $this->key, $ciphertext, MCRYPT_MODE_CBC, $this->iv ) );
}
}
$cipher = new Cipher();
$cipher->generate_salt();
$cipher->generate_iv();
$cipher->generate_key( '123' ); // the key will be generated from the passphrase "123"
// echo $cipher->get_key();
$data_encrypted = $cipher->encrypt( 'hello' );
echo 'encrypted:';
echo '<pre>';
print_r( $data_encrypted );
echo '</pre>';
unset( $cipher );
echo 'decrypted:';
$cipher = new Cipher();
$decrypted = $cipher->decrypt( $data_encrypted, '123' );
echo '<pre>';
print_r( $decrypted );
echo '</pre>';
die();
To decrypt a string you need the key and the initialization vector ($iv).
要解密字符串,您需要密钥和初始化向量 ( $iv)。
<?php
require 'vendor/autoload.php';
use Coreproc\CryptoGuard\CryptoGuard;
// This passphrase should be consistent and will be used as your key to encrypt/decrypt
// your string
$passphrase = 'whatever-you-want';
// Instantiate the CryptoGuard class
$cryptoGuard = new CryptoGuard($passphrase);
$stringToEncrypt = 'test';
// This will spit out the encrypted text
$encryptedText = $cryptoGuard->encrypt($stringToEncrypt);
// This should give you back the string you encrypted
echo $cryptoGuard->decrypt($encryptedText);
回答by Herr
Here is an updated and secured version to Eugene Fidelin's original code.
这是 Eugene Fidelin 原始代码的更新和安全版本。
Please notice the output has the IV and Salt in it, which you also need to store securely with the decryption key.
请注意输出中包含 IV 和 Salt,您还需要使用解密密钥安全地存储它们。
##代码##回答by chrisbjr
Here is a good PHP library that can help you encrypt and decrypt strings - available via Composer and easy to use too:
这是一个很好的 PHP 库,可以帮助您加密和解密字符串 - 可通过 Composer 获得并且也易于使用:
https://github.com/CoreProc/crypto-guard
https://github.com/CoreProc/crypto-guard
Here is a sample:
这是一个示例:
##代码##
