How do I encrypt a string in PHP?
我想做一个加密函数,它应该有一些密钥。如下所示:
1 2 3 4 5 6 | function encrypt($string) { $key ="mastermind"; $enc = encryptfunc($string, $key); return $enc; } |
同样的事情也应该适用于解密。
这里简单但安全地实现了CBC模式下的AES-256加密,它使用pbkdf2创建纯文本密码的加密密钥,并使用hmac对加密的消息进行身份验证。
它适用于php 5.3及更高版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | /** * 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 ('\0') 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), "\0" ); } } |
用途:
1 2 3 4 | $c = new McryptCipher('secret key goes here'); $encrypted = $c->encrypt('secret message'); $decrypted = $c->decrypt($encrypted); |
关于绩效的通知
默认情况下,mcrypt_create_iv()函数使用/dev/random作为随机值的源。如果服务器的熵很低,那么这个源可能非常慢。这就是使用/dev/urandom的原因。
下面是一个很好的解释,它们之间的区别是什么http://www.onkarjoshi.com/blog/191/device-dev-random-vs-urandom/
因此,如果您没有将此加密用于关键的内容(我希望您不要这样做),那么您可以使用/dev/urandom来提高加密性能,否则只需将mcrypt_dev_urandom替换为mcrypt_dev_random。
重要安全更新1
多亏了@herrk,他指出使用一个简单的哈希来创建加密密钥不够安全-现在使用pbkdf2算法来实现这一点(请阅读关于pbkdf2的更多信息http://en.wikipedia.org/wiki/pbkdf2)。
pbkdf2算法的实现从https://defuse.ca/php-pbkdf2.htm复制。
重要安全更新2
感谢@scott,他注意到加密消息应该经过身份验证—现在,hmac用于验证消息是否被更改。
Security Warning: Encryption without authentication is vulnerable to something called a chosen-ciphertext attack. See Eugene's answer for a solution that offers authenticated encryption.
如果您使用的是php>=5.3,那么新的
这些数据稍后可以用
如果你想知道你可以使用哪种密码功能,
好像有很多这样的东西^^
< BR>下面是我前一段时间在博客上发布的部分代码,它将演示这三个函数的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | $methods = openssl_get_cipher_methods(); var_dump($methods); $textToEncrypt ="he who doesn't do anything, doesn't go wrong -- Zeev Suraski"; $secretKey ="glop"; echo '[cc lang="php"]'; foreach ($methods as $method) { $encrypted = openssl_encrypt($textToEncrypt, $method, $secretKey); $decrypted = openssl_decrypt($encrypted, $method, $secretKey); echo $method . ': ' . $encrypted . ' ; ' . $decrypted ." "; } echo ' |
;< /代码>
我写这篇文章时得到的输出是这样的:
1 2 3 4 5 6 7 8 | 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 |
如果您不使用php 5.3,那么您可能需要查看手册的mcrypt部分,以及诸如
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.
我不是一个加密人,但我使用这种东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } |
要解密字符串,需要密钥和初始化向量(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } |
这是尤金·菲德林原始代码的更新和安全版本。
请注意,输出中有IV和salt,您还需要使用解密密钥安全地存储这些内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 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 '[cc lang="php"]'; print_r( $data_encrypted ); echo ' |
;未结算($cipher);echo'已解密:';$cipher=新的cipher();$decrypted=$cipher->decrypt($data_encrypted,'123');回声"
1 2 3 | '; print_r( $decrypted ); echo ' |
";De();< /代码>
这里有一个很好的PHP库,可以帮助您加密和解密字符串-通过编写器提供,也很容易使用:
https://github.com/coreproc/crypto-guard
这是一个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?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); |