1 AES加密原理
AES加密是一种经典的对称加密算法,使用加密函数和密钥来完成对明文的加密,然后使用相同的密钥和对应的函数来完成解密,如下图所示:
使用AES加密的时候会把要加密的数据按照128bit分割,然后对分割后的数据分别加密,明文被分割的长度是128位,即16字节。密钥长度可以是16字节、24字节和32字节,即128位密钥、192位密钥和256位密钥,密钥越长,安全性越高。明文分割加密的过程如下图所示:
AES的Key的三种长度:AES-128,AES-192,AES-256,分别对应了AES加密的轮数。
AES-128:10轮
AES-192:12轮
AES-256:14轮
AES的处理单位是字节,每一个明文块会被分成4X4的矩阵,经过对应的轮数加密后成为一个4X4的密文矩阵,如下图所示
同样的128位密钥也会被分成4X4的矩阵,并且通过密钥编排函数把密钥矩阵扩展成一个44个字节的序列,该序列的前4个元素W(0-3)是原始密钥,用于加密运算中的初始密钥加,后面40个字分为10组,每组4个字分别用于10轮加密运算中的轮密钥加。加密流图如下图所示:
由图可以看出,AES加密的第一轮到第九轮的轮函数一样,包括4个操作:
字节替代(SubBytes)
行移位(ShiftRows)
列混淆(MixColumns)
轮密钥加(AddRoundKey)
最后一轮迭代不执行列混合。另外在第一轮之前还有一个初始轮,将明文和原始密钥进行一次异或加密操作。
解密是加密的逆操作,需要经过10轮操作,第一轮到第九轮都需要执行逆向行移位、逆字节替换、轮密钥加和逆向列混淆。和加密一样,最后一轮不执行逆列混淆,并且在第一轮之前也要执行密钥加操作。
字节替换
16字节的明文块在每一个处理步骤中都被排列成4X4的矩阵,字节替换就是把明文块的每一个字节都替代成另外一个字节,替代的依据是AES设定的S盒(16X16的矩阵)。
行移位
行移位是简单的左循环移位操作,当密钥长度为128位时,矩阵的第0行左移0字节,第1行左移1字节,第2行左移2字节,第3行左移3字节,如下图所示:
列混淆
列混淆就是把行移位之后的矩阵和固定矩阵相乘,如下图所示:
轮密钥加
这一步是唯一使用密钥的一步,128位的秘钥也被排列为4X4的矩阵。列混淆后矩阵中每个字节和密钥对应字节异或一次,就得到最终字节。如下图所示:
解密是每一个操作的逆操作,这里不再赘述。原理梳理清楚之后,我们怎么用代码实现加解密 呢。
2 Java实现AES加解密示例
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
/**
* @ClassName AESUtils
* @Description AES加密算法工具类
* @Author boy
* @Date 2019/8/21 8:52 PM
*/
public class AESUtils {
public static String AES = "AES";//指定算法类型
public static int KEY_LEN = 128;//指定密钥长度
public static String UTF_8 = "UTF-8";//编码格式
/*
* @Author boy
* @Description 生成AES密钥
* @Date 2019/8/22 10:49 AM
* @Param []
* @return java.lang.String
*/
public static String genAESKey() throws Exception{
//构造密钥生成器,指定为AES算法
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
//生成一个指定位数的随机源,KEY_LEN=128就是128位
keyGenerator.init(KEY_LEN);
//生成对称密钥
SecretKey sKey = keyGenerator.generateKey();
return Base64.getEncoder().encodeToString(sKey.getEncoded());
}
/*
* @Author boy
* @Description 使用AES密钥解密数据
* @Date 2019/8/22 10:51 AM
* @Param [key, data]
* @return java.lang.String
*/
public static String encrypt(String key,String data) throws Exception{
//获取key
SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(key.getBytes()),AES);
//根据指定算法生成密码器
Cipher cipher=Cipher.getInstance(AES);
//初始化密码器,第一个参数为加密或者解密解密操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
//将加密内容转化为字节数组
byte [] byte_data=data.getBytes(UTF_8);
//将字节数组加密
byte [] AES_data=cipher.doFinal(byte_data);
return new String(Base64.getEncoder().encode(AES_data));
}
/*
* @Author boy
* @Description 使用AES密钥加密数据
* @Date 2019/8/22 8:06 PM
* @Param [key, data]
* @return java.lang.String
*/
public static String decrypt(String key,String data) throws Exception{
//获取key
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(key.getBytes()),AES);
//根据指定算法生成密码器
Cipher cipher=Cipher.getInstance(AES);
//初始化密码器,第一个参数为加密或者解密解密操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, secretKey);
//将加密内容转化为字节数组,因为数据是用Base64转换过的,所以需要使用base64解密
byte [] dataByte = Base64.getDecoder().decode(data.getBytes(UTF_8));
//解密字节数组
byte [] decryptData=cipher.doFinal(dataByte);
return new String(decryptData);
}
public static void main(String[] args) throws Exception{
String data = "你好!AES!";
String key = genAESKey();
System.out.println("密钥:"+key);
System.out.println("加密前数据:"+data);
String AESData = encrypt(key,data);
System.out.println("加密后数据:" + AESData);
System.out.println("解密后数据:" + decrypt(key,AESData));
}
}