将此 Encrypt - Decrypt PHP Class 转换为完整的静态 PHP class
Converting this Encrypt - Decrypt PHP Class into a full static PHP class
这里有一个很棒的 php class 可以在 php 中进行安全的双向加密。我在一个需要对其方法进行数千次调用的项目中使用它,
我想将它转换成一个完整的静态 class 及其所有方法以提高性能并避免在每次新方法调用时使用新实例
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
class Encryption {
/**
* @var string $cipher The mcrypt cipher to use for this instance
*/
protected $cipher = '';
/**
* @var int $mode The mcrypt cipher mode to use
*/
protected $mode = '';
/**
* @var int $rounds The number of rounds to feed into PBKDF2 for key generation
*/
protected $rounds = 100;
/**
* Constructor!
*
* @param string $cipher The MCRYPT_* cypher to use for this instance
* @param int $mode The MCRYPT_MODE_* mode to use for this instance
* @param int $rounds The number of PBKDF2 rounds to do on the key
*/
public function __construct($cipher, $mode, $rounds = 100) {
$this->cipher = $cipher;
$this->mode = $mode;
$this->rounds = (int) $rounds;
}
/**
* Decrypt the data with the provided key
*
* @param string $data The encrypted datat to decrypt
* @param string $key The key to use for decryption
*
* @returns string|false The returned string if decryption is successful
* false if it is not
*/
public function decrypt($data, $key) {
$salt = substr($data, 0, 128);
$enc = substr($data, 128, -64);
$mac = substr($data, -64);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
if ($mac !== hash_hmac('sha512', $enc, $macKey, true)) {
return false;
}
$dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);
$data = $this->unpad($dec);
return $data;
}
/**
* Encrypt the supplied data using the supplied key
*
* @param string $data The data to encrypt
* @param string $key The key to encrypt with
*
* @returns string The encrypted data
*/
public function encrypt($data, $key) {
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
$data = $this->pad($data);
$enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);
$mac = hash_hmac('sha512', $enc, $macKey, true);
return $salt . $enc . $mac;
}
/**
* Generates a set of keys given a random salt and a master key
*
* @param string $salt A random string to change the keys each encryption
* @param string $key The supplied key to encrypt with
*
* @returns array An array of keys (a cipher key, a mac key, and a IV)
*/
protected function getKeys($salt, $key) {
$ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
$keySize = mcrypt_get_key_size($this->cipher, $this->mode);
$length = 2 * $keySize + $ivSize;
$key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$cipherKey = substr($key, 0, $keySize);
$macKey = substr($key, $keySize, $keySize);
$iv = substr($key, 2 * $keySize);
return array($cipherKey, $macKey, $iv);
}
/**
* Stretch the key using the PBKDF2 algorithm
*
* @see http://en.wikipedia.org/wiki/PBKDF2
*
* @param string $algo The algorithm to use
* @param string $key The key to stretch
* @param string $salt A random salt
* @param int $rounds The number of rounds to derive
* @param int $length The length of the output key
*
* @returns string The derived key.
*/
protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
$size = strlen(hash($algo, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
$tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
$res = $tmp;
for ($j = 1; $j < $rounds; $j++) {
$tmp = hash_hmac($algo, $tmp, $key, true);
$res ^= $tmp;
}
$result .= $res;
}
return substr($result, 0, $length);
}
protected function pad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$padAmount = $length - strlen($data) % $length;
if ($padAmount == 0) {
$padAmount = $length;
}
return $data . str_repeat(chr($padAmount), $padAmount);
}
protected function unpad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$last = ord($data[strlen($data) - 1]);
if ($last > $length) return false;
if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
return false;
}
return substr($data, 0, -1 * $last);
}
}
它的用法是
$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);
$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);
我想要
Encryption::encrypt($data, $key);
Encryption::decrypt($encryptedData, $key);
谢谢。
您正在寻找静态方法,只需在方法声明中的 public
或 protected
之前添加 static
关键字即可。
但是,请注意您不能从静态方法调用非静态方法,因此您还必须将所有实用方法声明为静态方法。
有关详细信息,请参阅 http://php.net/manual/en/language.oop5.static.php。
我将提出一些解决方案和想法,让您朝着正确的方向前进。但是,我对您的代码有严重的担忧,首先必须请您不要在实时环境中使用此代码。
与许多密码学研究人员会告诉您 "don't write crypto code" 并就此打住不同,我选择了 write crypto code, don't publish or use it 路径。
您询问 Stack Overflow 关于将您的 class 转换为使用静态方法的事实告诉我您可能还没有资格在 PHP 中编写密码学。即使你对这门语言更熟悉,我也会强烈警告你远离你前进的方向。
请改用this PHP library。我已经审查过了。其他人也一样。它尚未经过正式审计,但这更多是因为作者没有投入数千美元进行正式审计,而不是缺乏对安全和高质量代码的奉献精神。
这里有一些我一眼就发现的东西,它们对密码学库来说是危险的。这并不意味着攻击;我只想向您展示该地区的一些危险。
构造函数中没有完整性检查
你怎么知道 $this->cipher
和 $this->mode
是有效的 mcrypt 常量?
浪费的熵
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
128字节的熵是1024比特的熵;远远超过您对 AES 的需求。
使用 substr()
被认为对加密有害
PHP 具有称为 mbstring.func_overload
的功能,它完全改变了 substr()
、strlen(),
等函数的行为,以使用多字节 (Unicode) 逻辑而不是二进制字符串逻辑。这种行为的典型后果是它会导致结果子字符串中的字节数不正确。这不好。有多糟糕取决于攻击者的创造力。
在启用此功能的系统中,您必须显式调用 mb_substr($string, '8bit')
以获取原始二进制字符串中的字节数。但是,此功能并非在所有系统上都可用。
使用 substr()
随意分割原始二进制字符串是危险的。
对MAC验证的定时攻击
if ($mac !== hash_hmac('sha512', $enc, $macKey, true))
Anthony Ferrara already covered this topic in detail.
总结
我真诚地希望这个 post 以建设性批评的预期语气收到,并且您对密码工程中的疯狂细节数量有所启发。
此外,我希望这不会阻止您进一步了解该领域。 请多多了解! 这是一个令人兴奋的领域,每个人都因为有更多的参与而变得更好!你甚至有一天可以教我一种我永远无法想象的复杂攻击。
但是今天,现在,在生产系统中使用业余密码学来保护敏感数据是很危险的。您应该为自己(或您的 client/employer,如果适用)决定更重要的事情:实验,或保护他们的信息免受损害。我不会为你做那个选择。
这里有一个很棒的 php class 可以在 php 中进行安全的双向加密。我在一个需要对其方法进行数千次调用的项目中使用它,
我想将它转换成一个完整的静态 class 及其所有方法以提高性能并避免在每次新方法调用时使用新实例
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
class Encryption {
/**
* @var string $cipher The mcrypt cipher to use for this instance
*/
protected $cipher = '';
/**
* @var int $mode The mcrypt cipher mode to use
*/
protected $mode = '';
/**
* @var int $rounds The number of rounds to feed into PBKDF2 for key generation
*/
protected $rounds = 100;
/**
* Constructor!
*
* @param string $cipher The MCRYPT_* cypher to use for this instance
* @param int $mode The MCRYPT_MODE_* mode to use for this instance
* @param int $rounds The number of PBKDF2 rounds to do on the key
*/
public function __construct($cipher, $mode, $rounds = 100) {
$this->cipher = $cipher;
$this->mode = $mode;
$this->rounds = (int) $rounds;
}
/**
* Decrypt the data with the provided key
*
* @param string $data The encrypted datat to decrypt
* @param string $key The key to use for decryption
*
* @returns string|false The returned string if decryption is successful
* false if it is not
*/
public function decrypt($data, $key) {
$salt = substr($data, 0, 128);
$enc = substr($data, 128, -64);
$mac = substr($data, -64);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
if ($mac !== hash_hmac('sha512', $enc, $macKey, true)) {
return false;
}
$dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);
$data = $this->unpad($dec);
return $data;
}
/**
* Encrypt the supplied data using the supplied key
*
* @param string $data The data to encrypt
* @param string $key The key to encrypt with
*
* @returns string The encrypted data
*/
public function encrypt($data, $key) {
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
$data = $this->pad($data);
$enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);
$mac = hash_hmac('sha512', $enc, $macKey, true);
return $salt . $enc . $mac;
}
/**
* Generates a set of keys given a random salt and a master key
*
* @param string $salt A random string to change the keys each encryption
* @param string $key The supplied key to encrypt with
*
* @returns array An array of keys (a cipher key, a mac key, and a IV)
*/
protected function getKeys($salt, $key) {
$ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
$keySize = mcrypt_get_key_size($this->cipher, $this->mode);
$length = 2 * $keySize + $ivSize;
$key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$cipherKey = substr($key, 0, $keySize);
$macKey = substr($key, $keySize, $keySize);
$iv = substr($key, 2 * $keySize);
return array($cipherKey, $macKey, $iv);
}
/**
* Stretch the key using the PBKDF2 algorithm
*
* @see http://en.wikipedia.org/wiki/PBKDF2
*
* @param string $algo The algorithm to use
* @param string $key The key to stretch
* @param string $salt A random salt
* @param int $rounds The number of rounds to derive
* @param int $length The length of the output key
*
* @returns string The derived key.
*/
protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
$size = strlen(hash($algo, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
$tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
$res = $tmp;
for ($j = 1; $j < $rounds; $j++) {
$tmp = hash_hmac($algo, $tmp, $key, true);
$res ^= $tmp;
}
$result .= $res;
}
return substr($result, 0, $length);
}
protected function pad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$padAmount = $length - strlen($data) % $length;
if ($padAmount == 0) {
$padAmount = $length;
}
return $data . str_repeat(chr($padAmount), $padAmount);
}
protected function unpad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$last = ord($data[strlen($data) - 1]);
if ($last > $length) return false;
if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
return false;
}
return substr($data, 0, -1 * $last);
}
}
它的用法是
$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);
$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);
我想要
Encryption::encrypt($data, $key);
Encryption::decrypt($encryptedData, $key);
谢谢。
您正在寻找静态方法,只需在方法声明中的 public
或 protected
之前添加 static
关键字即可。
但是,请注意您不能从静态方法调用非静态方法,因此您还必须将所有实用方法声明为静态方法。
有关详细信息,请参阅 http://php.net/manual/en/language.oop5.static.php。
我将提出一些解决方案和想法,让您朝着正确的方向前进。但是,我对您的代码有严重的担忧,首先必须请您不要在实时环境中使用此代码。
与许多密码学研究人员会告诉您 "don't write crypto code" 并就此打住不同,我选择了 write crypto code, don't publish or use it 路径。
您询问 Stack Overflow 关于将您的 class 转换为使用静态方法的事实告诉我您可能还没有资格在 PHP 中编写密码学。即使你对这门语言更熟悉,我也会强烈警告你远离你前进的方向。
请改用this PHP library。我已经审查过了。其他人也一样。它尚未经过正式审计,但这更多是因为作者没有投入数千美元进行正式审计,而不是缺乏对安全和高质量代码的奉献精神。
这里有一些我一眼就发现的东西,它们对密码学库来说是危险的。这并不意味着攻击;我只想向您展示该地区的一些危险。
构造函数中没有完整性检查
你怎么知道 $this->cipher
和 $this->mode
是有效的 mcrypt 常量?
浪费的熵
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
128字节的熵是1024比特的熵;远远超过您对 AES 的需求。
使用 substr()
被认为对加密有害
PHP 具有称为 mbstring.func_overload
的功能,它完全改变了 substr()
、strlen(),
等函数的行为,以使用多字节 (Unicode) 逻辑而不是二进制字符串逻辑。这种行为的典型后果是它会导致结果子字符串中的字节数不正确。这不好。有多糟糕取决于攻击者的创造力。
在启用此功能的系统中,您必须显式调用 mb_substr($string, '8bit')
以获取原始二进制字符串中的字节数。但是,此功能并非在所有系统上都可用。
使用 substr()
随意分割原始二进制字符串是危险的。
对MAC验证的定时攻击
if ($mac !== hash_hmac('sha512', $enc, $macKey, true))
Anthony Ferrara already covered this topic in detail.
总结
我真诚地希望这个 post 以建设性批评的预期语气收到,并且您对密码工程中的疯狂细节数量有所启发。
此外,我希望这不会阻止您进一步了解该领域。 请多多了解! 这是一个令人兴奋的领域,每个人都因为有更多的参与而变得更好!你甚至有一天可以教我一种我永远无法想象的复杂攻击。
但是今天,现在,在生产系统中使用业余密码学来保护敏感数据是很危险的。您应该为自己(或您的 client/employer,如果适用)决定更重要的事情:实验,或保护他们的信息免受损害。我不会为你做那个选择。