本文示例代码详见:https://github.com/52fhy/crypt-demo
DES
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
DES使用简介
使用DES需要设置加密内容、加密key、加密混淆向量iv、分组密码模式、填充模式。
加密内容:
给定的加密的数据。如果数据长度不是 n*分组大小,则在其后使用 ' ' 补齐。
加密Key:
加密密钥。 如果密钥长度不是该算法所能够支持的有效长度,需要填充。如果密钥长度过长,需要截取。
加密iv:
用于CBC, CFB, OFB模式,在ECB模式里不是必须的。
分组密码模式:
常见的分组密码模式有:CBC, OFB,CFB 和 ECB。
填充模式:
Pkcs5、Pkcs7。
填充算法(Pkcs5、Pkcs7)
PKCS5Padding与PKCS7Padding基本上是可以通用的。在PKCS5Padding中,明确定义Block的大小是8位,而在PKCS7Padding定义中,对于块的大小是不确定的,可以在1-255之间(块长度超出255的尚待研究),填充值的算法都是一样的:
pad = k - (l mod k) //k=块大小,l=数据长度,如果k=8, l=9,则需要填充额外的7个byte的7
可以得出:Pkcs5是Pkcs7的特例(Block的大小始终是8位)。当Block的大小始终是8位的时候,Pkcs5和Pkcs7是一样的。(参考)
填充算法实现:
- PHP
function pkcs5_pad($text) {
$pad = 8 - (strlen($text) % 8);
//$pad = 8 - (strlen($text) & 7); //也可以使用这种方法
return $text . str_repeat(chr($pad), $pad);
}
function pkcs7_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
反填充(去掉填充的字符)只需要根据解密后内容最后一个字符,就知道填充了什么、填充了几个,然后截取掉即可:
function _unpad($text){
$pad = ord(substr($text, -1));//取最后一个字符的ASCII 码值
if ($pad < 1 || $pad > strlen($text)) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
- Python
from Crypto.Cipher import AES
def pkcs7_pad(str):
x = AES.block_size - (len(str) % AES.block_size)
if x != 0:
str = str + chr(x)*x
return str
def _unpad(msg):
paddingLen = ord(msg[len(msg)-1])
return msg[0:-paddingLen]
加密解密步骤
加密步骤(以PHP的扩展mcrypt为例):
1、获得加密算法的分组大小(mcrypt_get_block_size);
2、被加密的明文使用Pkcs5或Pkcs7填充;
3、加密密钥key截取或填充至8位;
4、加密向量iv设置;
5、打开指定算法和模式对应的模块,返回加密描述符td(mcrypt_module_open);
6、使用td、key、iv初始化加密所需的缓冲区 (mcrypt_generic_init);
7、加密数据(mcrypt_generic);
8、清理的加密描述符td的缓冲区(mcrypt_generic_deinit);
9、释放加密描述符td(mcrypt_module_close);
10、返回base64_encode的加密结果,可选。
解密步骤(以PHP的扩展mcrypt为例):
1、base64_decode解码,如果加密使用了base64_encode;
2、加密密钥key截取或填充至8位;
3、加密向量iv设置;
4、打开指定算法和模式对应的模块,返回加密描述符td(mcrypt_module_open);
5、使用td、key、iv初始化加密所需的缓冲区 (mcrypt_generic_init);
6、解密数据(mdecrypt_generic);
7、清理的加密描述符td的缓冲区(mcrypt_generic_deinit);
8、释放加密描述符td(mcrypt_module_close);
9、使用Pkcs5去掉填充的内容,返回解密后的结果。
使用DES需要注意下面几点:
- 确保都使用
DES
+ECB
; - 确保明文填充都使用的是
Pkcs5
或者Pkcs7
,此时两者效果一致; - 加密key在DES长度必须是8字节(bytes);如果不够长必须填充,过长必须截取;
- 加密向量iv与加密key有同样的约定;
- 注意加密结果建议都使用base64编码。
只有以上都保持一样,各个语言里最终加密的密文才能保持一致,否则会出现:
- 每次加密的密文不一样,但是能解密;(iv随机生成导致的)
- 不同语言加密出来的密文不一致。
各种语言实现示例
PHP
示例:
- Crypt_DES.php
<?php
include('Crypt_DES.php');
$des = new Crypt_DES();//默认是CBC模式
$plaintext = '123456';
$des->setKey('pwd');
//$des->setIV("