当前位置:编程学习 > php >>

php实现3DES加密算法,工作模式CBC,填充模式PKCS7 Padding

概述:
最近工作中需要通过接口交换一些数据,数据采用sha-1,3DES加密,sha-1加密方式可以直接使用php的函数sha1()完成,php对3DES加密方式的支持并不完全,部分功能需要自己实现。
对方提供的接口说明了3DES算法所用参数,包括:
1.工作模式:CBC
2.填充方式:PKCS7 Padding
原理:
3DES:
对于3DES加密算法,我个人也不是太了解,因为这涉及到密码学的范畴,我并没有详细的研究这个算法本身。所以这里我也只能写一点简单的说明和理解,对不住各位看官了。大家有兴趣的话可以自己研究,关于3DES和DES的详细细节,请参见http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf。
wikipedia的关于3DES算法的描述是“3DES(Triple DES),是三重数据加密算法(TDES,Triple Data Encryption Algorithm)的通称。它相当于是对每个数据块应用三次DES加密算法。”
这就要提到DES算法,DES算法是一种块密码加密算法,将明文分成多个长64bit的组,并使用一个64bit的密钥,对每个明文块使用同样的算法从而获得同样长度的密文块。
加密算法流程如下:
 
首先将64bit的明文数据分为左(L0),右(R0)两部分,然后R0经过费斯妥函数处理,将结果与L0做异或操作。异或的结果作为R1,R0作为L1,这样算是完成一个“回次”(round)。在经过16个回次以后(最有一个回次完成异或后不交换位置),再将两个32bit的块合并,这样就得到了这个明文块对应的密文块。
PS:限于篇幅和本人的能力,这里就不具体展开费斯妥函数的处理过程了,有兴趣的朋友可以看一下上面提供的详细内容的链接。
因为3DES算法是对一个数据进行三次DES算法,所以有3个64bit密钥k1、k2、k3。
加密算法为:
密文= EK3(DK2(EK1(明文)))
也就是说,使用K1为密钥进行DES加密,再用K2为密钥进行DES“解密”,最后以K3进行DES加密。
而解密则为其反过程:
明文= DK1(EK2(DK3(密文)))
即以K3解密,以K2“加密”,最后以K1解密。
如果k1=k2=k3,则此时3DES算法兼容DES算法,结果一致。
CBC:
下面我们来看工作模式。上文我们简单描述了DES算法的流程,不过那是针对一个64bit的明文块的加密操作,结果是这个明文块对应的64bit的密文块。那么多个明文块加密是怎么衔接的呢,这就要依靠块密码的工作模式了。
密码学中,块密码的工作模式允许使用同一个块密码密钥对多于一个一块的数据进行加密,并保证其安全性。常见的工作模式包括:ECB,CBC,OFB和CFB等。这里我们就只简单了解一下CBC的工作模式。关于更多工作模式,可以参考wikipedia 的块密码的工作模式。
在CBC模式中,每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。第一个明文块没有前一个密文块,所以我们使用一组初始化向量(IV)代替。CBC模式的加密流程如下图所示:
 
PKCS7:
正如上文所述,明文会被以64bit为一组划分为若干租进行加密,每一组使用DES算法由明文获得密文。可是待加密的明文并不能保证总是可以正好分成若干个64bit的组,最后一组正好满64bit的可能性往往是比较低的,那么为了加密方便,应该怎么办呢,Padding就是用来解决这个问题的。
我们这里简单了解一下Byte Padding中的Zero Padding、PKCS7 Padding 和PKCS5 Padding。更多信息请参考wikipedia的Padding_(cryptography)。
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块,kk...kk。下面的例子是每8byte为一块,最后一块有8byte,需要填充8byte的\x08。
  ... | 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 一致的结果。
实现:
由于加密算法的参数对方在接口中已经定义,所以我就只能自己实现对对方的兼容,所以自己写了PKCS7 Padding的方法,具体实现如下:
 1 <?php
 2 $keyForTDES = "XXXXXXX";//加密所用密钥
 3 $defaultIV = "XXXXXXX";//初始化向量IV
 4
 5 class Cryptogram {
 6     /**
 7      * 使用3DES加密源数据
 8      * @param string $oriSource 源数据
 9      * @param string $key       密钥
10      * @param string $defaultIV 加解密向量
11      * @return string $result   密文
12      */
13     public function encryptByTDES($oriSource, $key, $defaultIV){
14         $oriSource = $this->addPKCS7Padding($oriSource);
15         $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
16         mcrypt_generic_init($td, $key, $defaultIV);
17         $result = mcrypt_generic($td, $oriSource);
18         mcrypt_generic_deinit($td);
19         mcrypt_module_close($td);
20         return $result;
21     }
22    
23    
24     /**
25      * 使用3DES解密密文
26      * @param string $encryptedData 密文
27      * @param string $key           密钥
28      * @param string $defaultIV     加解密向量
29      * @return string $result       解密后的原文
30      */
31     public function decryptByTDES($encryptedData, $key, $defaultIV){
32         $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
33         mcrypt_generic_init($td, $key, $defaultIV);
34         $result = mdecrypt_generic($td, $encryptedData);
35         mcrypt_generic_deinit($td);
36         mcrypt_module_close($td);
37         $result = $this->stripPKSC7Padding($result);
38         return $result;
39     }
40    
41    
42     /**
43      * 为字符串添加PKCS7 Padding
44      * @param string $source    源字符串
45      */
46     private function addPKCS7Padding($source){
47     $block = mcrypt_get_block_size('tripledes', 'cbc');
48     $pad = $block - (strlen($source) % $block);
49     if ($pad <= $block) {
50         $char = chr($pad);
51         $source .= str_repeat($char, $pad);
52     }
53     return $source;
54     }
55    
56    
57     /**
58      * 去除字符串末尾的PKCS7 Padding<
补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,