PHP实现3DES加密,加密模式MCRYPT_MODE_CBC,填充⽅式
PKCS7Padd。。。
为什么要对数据进⾏加密
数据的安全性越来越得以重视。举个例⼦说,保存在数据库中的⽤户密码并不是明⽂保存的,⽽是采⽤md5加密后存储,这样即使数据库被脱库,仍能保证⽤户密码安全。但是,md5是不可逆的,开发⼈员根本就不知道⽤户的密码到底是什么。有些时候,我们希望加密后存储的数据是可逆的,⽐如⼀些接⼝密钥,这样即使数据库被脱库,如果没有对应的解密⽅式,攻击者盗取的密钥也是不能使⽤的。
什么是3DES(原理):
3DES(Triple DES),是三重数据加密算法的通称,相当于对每个数据块应⽤三次DES加密算法。由于计算机运算能⼒的增强,原版DES密码的密钥长度变得容易被暴⼒破解;3DES即是设计⽤来提供⼀种相对简单的⽅法,即通过增加DES的密钥长度来避免类似的攻击,⽽不是设计⼀种全新的块密码算法。3DES是DES向AES过渡的加密算法。
DES算法是⼀种块密码加密算法,将明⽂分成多个长64bit的组,并使⽤⼀个64bit的密钥,对每个明⽂块使⽤同样的算法从⽽获得同样长度的密⽂块。
⾸先将64bit的明⽂数据分为左(L0),右(R0)两部分,然后R0经过费斯妥函数处理,将结果与L0做异或操作。异或的结果作为R1,R0作为L1,这样算是完成⼀个“回次”(round)。在经过16个回次以后(最有⼀个回次完成异或后不交换位置),再将两个32bit的块合并,这样就得到了这个明⽂块对应的密⽂块。
因为3DES算法是对⼀个数据进⾏三次DES算法,所以有3个64bit密钥k1、k2、k3。
加密算法为:
密⽂ = E K3(D K2(E K1(明⽂)))
也就是说,使⽤K1为密钥进⾏DES加密,再⽤K2为密钥进⾏DES“解密”,最后以K3进⾏DES加密。
⽽解密则为其反过程:
明⽂ = D K1(E K2(D K3(密⽂)))
即以K3解密,以K2“加密”,最后以K1解密。
如果k1=k2=k3,则此时3DES算法兼容DES算法,结果⼀致。
上⽂我们简单描述了DES算法的流程,不过那是针对⼀个64bit的明⽂块的加密操作,结果是这个明⽂
块对应的64bit的密⽂块。那么对于多个明⽂块加密是怎么衔接的呢?这就要依靠块密码的加密模式了。
加密模式:
密码学中,块密码的⼯作模式允许使⽤同⼀个块密码密钥对多于⼀个⼀块的数据进⾏加密,并保证其安全性。常见的⼯作模式包括:ECB,CBC,OFB和CFB等。这⾥我们就只简单了解⼀下CBC的⼯作模式。
在CBC模式中,每个明⽂块先与前⼀个密⽂块进⾏异或后,再进⾏加密。在这种⽅法中,每个密⽂块都依赖于它前⾯的所有明⽂块。第⼀个明⽂块没有前⼀个密⽂块,所以我们使⽤⼀组初始化向量(IV)代替。CBC模式的加密流程如下图所⽰:
正如上⽂所述,明⽂会被以64bit为⼀组划分为若⼲租进⾏加密,每⼀组使⽤DES算法由明⽂获得密⽂。可是待加密的明⽂并不能保证总是可以正好分成若⼲个64bit的组,最后⼀组正好满64bit的可能性往往是⽐较低的,那么为了加密⽅便,应该怎么办呢,Padding就是⽤来解决这个问题的。
填充⽅式(PKCS7 Padding):
我们这⾥简单了解⼀下Byte Padding中的Zero Padding、PKCS7 Padding 和 PKCS5 Padding。更多信息请参考wikipedia的。
Zero Padding:所有需要填充的地⽅都以0填充。下⾯的例⼦是每8byte为⼀块的数据格式,最后⼀块只有4byte,所以要填充4byte的\x00。
... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
PKCS7 Padding:填充的内容是需要填充的字节数。如果最后⼀个数据块长度为len,每个块的长度为k,则要填充的内容为:
01 -- if lth mod k = k-1
02 -- if lth mod k = k-2
.
.
.
k k ... k k -- if lth mod k = 0
需要注意的是,如果最后⼀个数据块的长度len恰好等于k,则需要在后⾯再添加⼀个完整的padding块,
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD DD DD | 08 08 08 08 08 08 08 08 |
PKCS5 Padding:PKCS5 和 PKCS7 的唯⼀区别是PKCS5只能⽤来填充64bit的数据块,除此之外可以混⽤。
PHP的mcrypt 默认的填充值为 null ('\0'),java或.NET 默认填充⽅式为 PKCS7 。如果把java或.NET 填充模式改为 Zeros 即可得到与mcrypt ⼀致的结果。
使⽤PHP实现3DES加密:
流程图:
代码实现:
<?php
/**
* Created by PhpStorm.
* User: zjl
* Date: 2018/9/18
* Time: 17:11
*/
namespace app\des;
/**
* 3DES-CBC 加密解密算法
*
*/
class Mcrypt3DES{
//加密秘钥,
private $_key;
private $_iv;
public function __construct($key, $iv)
{
$this->_key = $key;
$this->_iv = $iv;
}
/
**
* 对字符串进⾏3DES加密
* @param string 要加密的字符串
* @return mixed 加密成功返回加密后的字符串,否则返回false
*/
public function encrypt3DES($str)
{
$td = mcrypt_module_open(MCRYPT_3DES, "", MCRYPT_MODE_CBC, "");
if ($td === false) {
return false;
}
/
/检查加密key,iv的长度是否符合算法要求
$key = $this->fixLen($this->_key, mcrypt_enc_get_key_size($td));
if ( empty($this->_iv) )
{
$iv_t = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);//从随机源创建初始向量
}
else
{
$iv_t = $this->_iv;
}
$iv = $this->fixLen($iv_t, mcrypt_enc_get_iv_size($td));
/
/加密数据长度处理,长度必须是 n * 分组⼤⼩,否则需要后补数据,根据不同的补码⽅式,来补不同的数据 $str = $this->addPKCS7Padding($str, mcrypt_enc_get_block_size($td));
//初始化加密所需的缓冲区
if (mcrypt_generic_init($td, $key, $iv) !== 0) {
return false;
}
$result = mcrypt_generic($td, $str);
/**
* 对加密后的数据进⾏base64加密处理,在⼊库时,varchar类型会⾃动移除字符串末尾的“空格”。
* 由于加密后的数据可能是以空格(ASCII 32)结尾,这种特性会导致数据损坏。
* 官⽅建议请使⽤ tinyblob/tinytext(或 larger)字段来存储加密数据。
*/
$result = base64_encode($result);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
php实例代码解密return $result;
}
/**
* 对加密的字符串进⾏3DES解密
* @param string 要解密的字符串
* @return mixed 加密成功返回加密后的字符串,否则返回false
*/
public function decrypt3DES($str)
{
$td = mcrypt_module_open(MCRYPT_3DES, "", MCRYPT_MODE_CBC, "");
if ($td === false) {
return false;
}
//检查加密key,iv的长度是否符合算法要求
$key = $this->fixLen($this->_key, mcrypt_enc_get_key_size($td));
if ( empty($this->_iv) )
{
$iv_t = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);//从随机源创建初始向量 }
else
{
$iv_t = $this->_iv;
}
$iv = $this->fixLen($iv_t, mcrypt_enc_get_iv_size($td));
//初始化加密所需的缓冲区
if (mcrypt_generic_init($td, $key, $iv) !== 0) {
return false;
}
$result = mdecrypt_generic($td, base64_decode($str));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
/
**
* 通过 mdecrypt_generic() 函数解密之后的数据是加密之前对加密数据长度补"\0"的数据。
* 使⽤ rtrim($str, "\0") 移除字符串末尾的 "\0" 。
*/
return $this->stripPKSC7Padding($result);
}
/**
* 返回适合算法长度的key,iv字符串,末尾使⽤0补齐
* @param string $str key或iv的值
* @param int $td_len 符合条件的key或iv长度
* @return string 返回处理后的key或iv值
*/
private function fixLen($str, $td_len)
{
$str_len = strlen($str);
if ($str_len > $td_len) {
return substr($str, 0, $td_len);
} else if($str_len < $td_len) {
return str_pad($str, $td_len, '0');
}
return $str;
}
/
**
* 返回适合算法的分组⼤⼩的字符串长度,末尾使⽤\0补齐
* @param string $str 要加密的字符串
* @param int $td_group_len 符合算法的分组长度
* @return string 返回处理后字符串
*/
private function strPad($str, $td_group_len)
{
$padding_len = $td_group_len - (strlen($str) % $td_group_len);
return str_pad($str, strlen($str) + $padding_len, "\0");
}
/
**
* 返回解密后移除字符串末尾的 "\0"的数据
* @param string $str 解密后的字符串
* @return string 返回处理后字符串
*/
private function strUnPad($str)
{
return rtrim($str, "\0");
}
/**
* 为字符串添加PKCS7 Padding
* @param string $str 源字符串
*/
private function addPKCS7Padding($str, $td_group_len){
$pad = $td_group_len - (strlen($str) % $td_group_len);
if ($pad <= $td_group_len) {
$char = chr($pad);
$str .= str_repeat($char, $pad);
}
return $str;
}
/**
* 去除字符串末尾的PKCS7 Padding
* @param string $source 带有padding字符的字符串
*/
private function stripPKSC7Padding($str){
$char = substr($str, -1, 1);
$num = ord($char);
if($num > 8){//8是此算法的分组⼤⼩,可通过mcrypt_enc_get_block_size获取 return $str;
}
$len = strlen($str);
for($i = $len - 1; $i >= $len - $num; $i--){
if(ord(substr($str, $i, 1)) != $num){
return $str;
}
}
$source = substr($str, 0, -$num);
return $source;
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论