Sodium-compat
(https://github.com/paragonie/sodium_compat)的库可在PHP >= 5.2.4中使用。 - RaelB根据@rqLizard的建议,您可以使用openssl_encrypt
/openssl_decrypt
PHP函数,这提供了一个更好的替代方案来实现AES(高级加密标准),也称为Rijndael加密。
openssl_encrypt()
和openssl_decrypt()
。底层库(libmcrypt
)自2007年以来就已经被弃用,并且表现远不如OpenSSL(后者利用现代处理器上的AES-NI
并具有缓存时间安全性)。MCRYPT_RIJNDAEL_256
不是AES-256
,它是Rijndael块密码的另一种变体。如果您想在mcrypt
中使用AES-256
,则必须使用32字节密钥的MCRYPT_RIJNDAEL_128
。 OpenSSL更明显地显示您正在使用哪个模式(即aes-128-cbc
vs aes-256-ctr
)。PHP 7.1+中使用GCM模式的AES身份验证加密示例
<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
//store $cipher, $iv, and $tag for decryption later
$original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
echo $original_plaintext."\n";
}
?>
PHP 5.6+的AES认证加密示例
<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
echo $original_plaintext."\n";
}
?>
基于以上示例,我修改了以下代码,旨在加密用户的会话ID:
class Session {
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* @param $session_id
* @return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($encrypt);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* @param $encryptedSessionId
* @return string
*/
public function decrypt($encryptedSessionId) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId);
// Decrypt the string.
$decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, "\0");
// Return it.
return $session_id;
}
public function _getIv() {
return md5($this->_getSalt());
}
public function _getSalt() {
return md5($this->drupal->drupalGetHashSalt());
}
}
转换为:
class Session {
const SESS_CIPHER = 'aes-128-cbc';
/**
* Encrypts the session ID and returns it as a base 64 encoded string.
*
* @param $session_id
* @return string
*/
public function encrypt($session_id) {
// Get the MD5 hash salt as a key.
$key = $this->_getSalt();
// For an easy iv, MD5 the salt again.
$iv = $this->_getIv();
// Encrypt the session ID.
$ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Base 64 encode the encrypted session ID.
$encryptedSessionId = base64_encode($ciphertext);
// Return it.
return $encryptedSessionId;
}
/**
* Decrypts a base 64 encoded encrypted session ID back to its original form.
*
* @param $encryptedSessionId
* @return string
*/
public function decrypt($encryptedSessionId) {
// Get the Drupal hash salt as a key.
$key = $this->_getSalt();
// Get the iv.
$iv = $this->_getIv();
// Decode the encrypted session ID from base 64.
$decoded = base64_decode($encryptedSessionId, TRUE);
// Decrypt the string.
$decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
// Trim the whitespace from the end.
$session_id = rtrim($decryptedSessionId, '\0');
// Return it.
return $session_id;
}
public function _getIv() {
$ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
return substr(md5($this->_getSalt()), 0, $ivlen);
}
public function _getSalt() {
return $this->drupal->drupalGetHashSalt();
}
}
MCRYPT_RIJNDAEL
仅支持非标准空填充。 @zaph
以下是@zaph的评论中提到的其他注意事项:
MCRYPT_RIJNDAEL_128
) 等同于 AES,但是 Rijndael 256 (MCRYPT_RIJNDAEL_256
) 不是 AES-256,因为256指定了块大小为256位,而 AES 只有一个块大小:128位。所以基本上具有256位块大小的 Rijndael (MCRYPT_RIJNDAEL_256
) 由于 mcrypt 开发者的选择错误地被命名。 @zaph使用不同块大小的 Rijndael 进行加密会产生不同的加密数据。
例如,MCRYPT_RIJNDAEL_256
(不等同于 AES-256
)定义了一种不同的 Rijndael 块密码变体,其大小为256位,并且基于传入的密钥确定密钥大小,而 aes-256-cbc
则是具有128位块大小和256位密钥大小的 Rijndael。因此,它们使用不同的块大小,产生完全不同的加密数据,因为 mcrypt 使用数字来指定块大小,而 OpenSSL 使用数字来指定密钥大小(AES 只有一个128位块大小)。所以基本上 AES 就是带有128位块大小和128、192和256位密钥大小的 Rijndael。因此最好使用 AES,在 OpenSSL 中称为 Rijndael 128。
$session_id = rtrim($decryptedSessionId, "\0");
?openssl_decrypt
是否可能在结尾返回一些不需要的字符?如果加密变量以0结尾(即encrypt("abc0")
),会发生什么? - hlscalon"\0"
不是 "0"
,而是空字符(NULL character),其 ASCII 码为 0x00(十六进制 0)。 - apaderno如其他答案所述,我发现最好的解决方案是使用OpenSSL。它已经内置于PHP中,您不需要任何外部库。以下是简单的示例:
要加密:
function encrypt($key, $payload) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
解密:
function decrypt($key, $garble) {
list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}
参考链接:https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/
phpseclib是一个基于纯PHP实现的Rijndael算法库,可以作为Composer软件包使用,并且已在我测试的PHP 7.3上工作。
在phpseclib文档中有一个页面,输入基本变量(密码、模式、密钥大小、位数),然后生成示例代码。对于Rijndael,ECB,256,256,它将输出以下内容:
使用mcrypt的代码
$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);
使用该库的方式如下
$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);
$decoded = $rijndael->decrypt($term);
* $term
经过 base64 解码
composer require phpseclib/mcrypt_compat
安装了mcrypt_compat
,但我仍然收到以下错误:PHP致命错误:在/app/kohana/classes/Kohana/Encrypt.php:124中未捕获的错误:调用未定义的函数mcrypt_get_key_size()
我正在使用php 7.2.26
和Kohana框架。
在使用composer安装后,是否还需要执行其他步骤? - M-Dahabbootstrap.php
的底部添加 require APPPATH . '/vendor/autoload.php';
。 - M-Dahab/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeMcryptAES
{
const CIPHER = MCRYPT_RIJNDAEL_128;
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);
// Add PKCS7 Padding
$block = mcrypt_get_block_size(self::CIPHER);
$pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
$message .= str_repeat(chr($pad), $pad);
$ciphertext = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$message,
MCRYPT_MODE_CBC,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = mcrypt_get_iv_size(self::CIPHER);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
$plaintext = mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
);
$len = mb_strlen($plaintext, '8bit');
$pad = ord($plaintext[$len - 1]);
if ($pad <= 0 || $pad > $block) {
// Padding error!
return false;
}
return mb_substr($plaintext, 0, $len - $pad, '8bit');
}
}
下面是使用 OpenSSL 编写的版本:
/**
* This library is unsafe because it does not MAC after encrypting
*/
class UnsafeOpensslAES
{
const METHOD = 'aes-256-cbc';
public static function encrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = openssl_random_pseudo_bytes($ivsize);
$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
return $iv . $ciphertext;
}
public static function decrypt($message, $key)
{
if (mb_strlen($key, '8bit') !== 32) {
throw new Exception("Needs a 256-bit key!");
}
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($message, 0, $ivsize, '8bit');
$ciphertext = mb_substr($message, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
}
我正在使用PHP 7.2.x,对我很好用:
public function make_hash($userStr){
try{
/**
* Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP
*/
return password_hash($userStr, PASSWORD_BCRYPT);
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
然后使用以下函数对哈希进行身份验证:
public function varify_user($userStr,$hash){
try{
if (password_verify($userStr, $hash)) {
return true;
}
else {
return false;
}
}catch(Exception $exc){
$this->tempVar = $exc->getMessage();
return false;
}
}
例子:
//create hash from user string
$user_password = $obj->make_hash2($user_key);
使用以下代码验证此哈希值:
if($obj->varify_user($key, $user_key)){
//this is correct, you can proceed with
}
就这些。
password_hash()
和 password_verify()
。由于哈希自动随机盐,因此黑客无法利用预编译的密码哈希表来破解密码。设置PASSWORD_DEFAULT
选项,未来的PHP版本将自动使用更强大的算法生成密码哈希,而无需更新代码。你应该使用openssl_encrypt()
函数。
我成功地翻译了我的加密对象
获取带有mcrypt的php副本以解密旧数据。我去了http://php.net/get/php-7.1.12.tar.gz/from/a/mirror,编译了它,然后添加了ext/mcrypt扩展(configure;make;make install)。我认为我还必须在php.ini中添加extension=mcrypt.so行。一系列脚本用于构建所有数据未加密的中间版本。
为openssl构建公钥和私钥
openssl genrsa -des3 -out pkey.pem 2048
(设置密码)
openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
要加密(使用公钥),请使用openssl_seal。从我所读到的内容来看,使用RSA密钥的openssl_encrypt受限于比密钥长度少11个字节(请参见http://php.net/manual/en/function.openssl-public-encrypt.php Thomas Horsten的评论)
$pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
$encryptedPassword = base64_encode($sealed);
$key = base64_encode($ekeys[0]);
你可以将原始二进制数据存储。
解密(使用私钥)$passphrase="这里是密码";
$privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
// 我将数据库列中的数据使用 base64_decode()
openssl_open($encryptedPassword, $plain, $key, $privKey);
echo "<h3>密码=$plain</h3>";
附言:你不能加密空字符串("")
再附言:这是用于密码数据库而不是用户验证。
password_hash
哈希它们,并用password_verify
验证它们? - Don't Panic