AES加密原理与Java示例

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));
    }
}
发布了32 篇原创文章 · 获赞 38 · 访问量 5487

猜你喜欢

转载自blog.csdn.net/u010482601/article/details/100666605