AES是一个对称密码,旨在取代DES成为广泛使用的标准。
一、AES的加密过程
二、AES的数据结构
加密解密算法的输入是一个128位分组。这些分组被描述成4×4的字节方阵,这个分组被复制到state数组中,并在加密和解密的每一阶段都被修改。在字节方阵中,每一格都是一个字,包含了4字节。在矩阵中字是按列排序的。
加密由N轮构成,轮数依赖于密钥长度:16字节密钥对应10轮,24字节密钥对应12轮,32字节对应14轮。
三、加密解密的详细结构
AES未使用Feistel结构。其前N-1轮由4个不同的变换组成:字节代替、行移位、列混淆和轮密钥加。最后一轮仅包含三个变换。而在第一轮前面有一个起始的单变换(轮密钥加),可以视为0轮。
字节代替(SubBytes):用一个S盒完成分组的字节到字节的代替。
行移位(ShiftRows):一个简单的置换。
列混淆(MixColumns):利用域GF(28)上的算术特性的一个代替。
轮密钥加(AddRoundKey):当前分组和扩展密钥的一部分进行按位异或XOR。
首尾使用轮密钥加的理由:若将其他不需要密钥的阶段放在首尾,在不知道密钥的情况下就能计算其逆,这就不能增加算法的安全性。
加密原理:轮密钥加实际是一种Vernam密码形式,其本身不难被破解。另外三个阶段一起提供了混淆、扩散和非线性功能。这三个阶段没有涉及密钥,就它们自身而言,并未提供算法的安全性。然而,该算法经历一个分组的XOR加密(轮密钥加),再对该分组混淆扩散(其他三个阶段),再接着又是XOR加密,如此交替进行,这种方式非常有效非常安全。
可逆原理:每个阶段均可逆。对字节代替、行移位和列混淆,在解密算法中用它们相对应的逆函数。轮密钥加的逆就是用同样的轮密钥和分组相异或,其原理就是A⊕B⊕B = A。和大多数分组密码一样,AES解密算法按逆序利用扩展密钥,然而其解密算法和加密算法并不一样,这是由AES的特定结构决定的。图5.3中加密和解密流程在纵向上是相反的,在每个水平点上,state数组在加密和解密函数中都是一样的。
常用语言的AES加密
PHP 7.1以下版本:
/**
* HMAC-SHA256加密算法生成签名
* @param $secret_key //生成签名的秘钥
* @param $base_str //生成签名的基本字符串(签名信息)
* @return string
*/
function
get_signature_hmac_sha256(
$secret_key
,
$base_str
)
{
$signature_info
=
base64_encode
(hash_hmac(
'sha256'
,
$base_str
,
$secret_key
, true));
return
$signature_info
;
}
/**
* AES128加密算法生成签名
* @param $secret_key //生成签名的秘钥
* @param $base_str //生成签名的基本字符串(签名信息)
* @return string
*/
function
get_signature_aes128(
$secret_key
,
$base_str
)
{
$size
= mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$input
= pkcs5_pad(
$base_str
,
$size
);
$td
= mcrypt_module_open(MCRYPT_RIJNDAEL_128,
''
, MCRYPT_MODE_ECB,
''
);
$iv
= mcrypt_create_iv (mcrypt_enc_get_iv_size(
$td
), MCRYPT_RAND);
mcrypt_generic_init(
$td
,
$secret_key
,
$iv
);
$data
= mcrypt_generic(
$td
,
$input
);
mcrypt_generic_deinit(
$td
);
mcrypt_module_close(
$td
);
$signature_info
=
base64_encode
(
$data
);
return
$signature_info
;
}
//AES 相关函数
function
pkcs5_pad (
$text
,
$blocksize
)
{
$pad
=
$blocksize
- (
strlen
(
$text
) %
$blocksize
);
return
$text
.
str_repeat
(
chr
(
$pad
),
$pad
);
}
/*** * AES128加密算法加密明文 * @author haolin.jiang<haolin.jiang@starcor.cn> * @param $base_str //待加密字符串 * @param $key //秘钥 * @return string //生成的签名 */ static public function encrypt_aes128($base_str, $key) { $encrypt_str = openssl_encrypt($base_str,'AES-128-ECB',$key,OPENSSL_RAW_DATA); return base64_encode($encrypt_str); } /** * AES128解密算法解密密文 * @author haolin.jiang<haolin.jiang@starcor.cn> * @param $encrypt_str * @param $encrypt_key * @return string */ static public function decrypt_aes128($encrypt_str, $encrypt_key) { $decrypt_str = openssl_decrypt(base64_decode($encrypt_str),'AES-128-ECB',$encrypt_key,OPENSSL_RAW_DATA); return $decrypt_str; }
<script src="./CryptoJS v3.1.2/rollups/aes.js"></script>
<script src="./CryptoJS v3.1.2/components/mode-ecb-min.js"></script>
<script type="text/javascript">
//加密key
var key = CryptoJS.enc.Utf8.parse('1111111111111111');//转为128 bit
//待加密字符
var str = CryptoJS.enc.Utf8.parse('12345');
var result = CryptoJS.AES.encrypt(str,key,{
mode:CryptoJS.mode.ECB,
padding:CryptoJS.pad.Pkcs7
});
console.log(result.toString());
</script>
JAVA
import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; class EncryptDemo { public static void main(String[] args) throws Exception { EncryptDemo demo = new EncryptDemo(); String key = "1111111111111111"; String input = "12345"; String a = demo.enctypt(input,key); System.out.println(a); String b = demo.decrypt(a,key); System.out.println(b); //比较加密之前的串与解密出来的串是否一致 if (input.equals(b)) { System.out.println("相等"); } else { System.out.println("不相等"); } } /* * @name 加密 * @param string input 待加密的字符 * @param string key 加密key * @return string 加密后的字符 * */ public String enctypt(String input, String key) throws Exception { if (key == null) { System.out.print("加密key不能为空"); return null; } System.out.println(key.length()); if (key.length() != 16) { System.out.print("加密key长度必须为16位"); return null; } byte[] raw = key.getBytes("utf-8");//将字符串转化为字节数组 SecretKeySpec keySpec = new SecretKeySpec(raw,"AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//算法/模式/填充 cipher.init(cipher.ENCRYPT_MODE,keySpec); byte[] encrypted = cipher.doFinal(input.getBytes("utf-8")); return new BASE64Encoder().encode(encrypted); } /* * @name 解密 * @param String input 加密后的串 * @param String key 解密key * @return string * */ public String decrypt(String input,String key) throws Exception { try { if (key == null) { System.out.print("解密key不能为空"); return null; } if (key.length() != 16) { System.out.print("解密key的长度必须为16位"); return null; } byte[] raw = key.getBytes("utf-8"); SecretKeySpec keySpec = new SecretKeySpec(raw,"AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(cipher.DECRYPT_MODE,keySpec); byte[] encrypted1 = new BASE64Decoder().decodeBuffer(input);//base64位解密 try { byte[] encrypted = cipher.doFinal(encrypted1); String str = new String(encrypted,"utf-8"); return str; } catch (Exception e) { e.getStackTrace(); } } catch (Exception e) { e.getStackTrace(); } return null; } }