AES加密 — 详解-程序员宅基地

技术标签: DES  加密算法  非对称加密  对称加密  AES  

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120285594
本文出自【赵彦军的博客】

推荐

AES加密 — 详解
RSA 加密 — 详解

AES 简介

  • DES 全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS)
  • AES 密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES(Data Encryption Standard),已经被多方分析且广为全世界所使用。

为什么 DES 被废弃?

我们知道数据加密标准(Data Encryption Standard: DES)的密钥长度是56比特,因此算法的理论安全强度是2的56次方。但二十世纪中后期正是计算机飞速发展的阶段,元器件制造工艺的进步使得计算机的处理能力越来越强,DES将不能提供足够的安全性。

简单来说,DES标准的秘钥长度要求太短,安全性不够。

为什么AES算法被称为 Rijndael 算法?

1997年1月2号,美国国家标准技术研究所(National Institute of Standards and Technology: NIST)宣布希望征集高级加密标准(Advanced Encryption Standard: AES)[3],用以取代DES。AES得到了全世界很多密码工作者的响应,先后有很多人提交了自己设计的算法。最终有5个候选算法进入最后一轮:Rijndael,Serpent,Twofish,RC6和MARS,下图分别为其中的5位作者。最终经过安全性分析、软硬件性能评估等严格的步骤,Rijndael算法获胜。

为什么AES算法安全性高?

AES的区块长度固定为128位,密钥长度则可以是128 bit,192 bit 或256位 bit 。换算成字节长度,就是密码必须是 16个字节,24个字节,32个字节。AES密码的长度更长了,破解难度就增大了,所以就更安全。

对称加密

  • 对称加密 : 也就是加密秘钥和解密秘钥是一样的。
  • 非对称加密 : 也就是加密秘钥和解密秘钥是不一样的。

AES 是对称加密算法,优点:加密速度快;缺点:如果秘钥丢失,就容易解密密文,安全性相对比较差

RSA 是非对称加密算法 , 优点:安全 ;缺点:加密速度慢

AES加密需要:明文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)
AES解密需要:密文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)

AES的算法模式一般为 AES/CBC/PKCS5Padding

加密模式

AES的加密模式有以下几种

  • 电码本模式(ECB)
  • 密码分组链接模式(CBC)
  • 计算器模式(CTR)
  • 密码反馈模式(CFB)
  • 输出反馈模式(OFB)

密码分组链接模式(CBC):将整段明文切成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

电码本模式 ECB (Electronic codebook,ECB):需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密。

在这里插入图片描述
根据图示,在 CBC 模式下,使用 AES 加解密方式进行分组加解密时,需要用到的两个参数

  • 1、初始化向量,也就是偏移量

  • 2、加解密秘钥

填充模式

如电子密码本(ECB)和密文块链接(CBC)。 为对称密钥加密设计的块密码工作模式要求输入明文长度必须是块长度的整数倍,因此信息必须填充至满足要求。

常见填充模式

算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
AES/CBC/NoPadding 16 不支持
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Padding 32 16
AES/CFB/NoPadding 16 原始数据长度
AES/CFB/PKCS5Padding 32 16
AES/CFB/ISO10126Padding 32 16
AES/ECB/NoPadding 16 不支持
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/OFB/NoPadding 16 不支持
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支持
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16

PKCS5Padding到底是什么?

为什么 JAVA 里指定算法时,写的是 AES/CBC/PKCS5Padding,每个都是什么含义,又有什么作用。

  • AES,加解密算法

  • CBC,数据分组模式

  • PKCS5Padding,数据按照一定的大小进行分组,最后分剩下那一组,不够长度,就需要进行补齐, 也可以叫 补齐模式

简单的说:拿到一个原始数据以后,首先需要对数据进行分组,分组以后如果长度不满足分组条件,需要进行补齐,最后形成多个分组,在使用加解密算法,对这多个分组进行加解密。所以这个过程中,AES,CBC,PKCS5Padding 缺一不可。

在对数据进行加解密时,通常将数据按照固定的大小(block size)分成多个组,那么随之就产生了一个问题,如果分到最后一组,不够一个 block size 了,要怎么办?此时就需要进行补齐操作。

补齐规则:The value of each added byte is the number of bytes that are added, i.e. N bytes, each of value N are added.

举例:

36 位的 UUID,如果按照 block size=16 字节(即 128 比特),那么就需要补齐到 48 位,差 12 个字节。那么最后填充的 12 个字节的内容,都是字节表示的 0x0c(即 12)。

偏移量

偏移量 的添加一般是为了增加 AES 加密的复杂度,增加数据的安全性。一般在 AES_256 中会使用到 偏移量 ,而在 AES_128 加密中不会使用到。

字符集

AES 加密中,特别也要注意到字符集的问题。一般用到的字符集是 utf-8gbk

实际工作中的加密流程

在实际的工作中,客户端跟服务器交互一般都是字符串格式,所以一个比较好的加密流程是:

  • 加密流程 :明文通过 密钥 (有时也需要 偏移量 ),利用 AES 加密算法,然后通过 Base64 转码,最后生成加密后的字符串。
  • 解密流程 :加密后的字符串通过 密钥 (有时也需要 偏移量 ),利用 AES 解密算法,然后通过 Base64 转码,最后生成解密后的字符串。

AES 加密/解密 注意的问题

AES 加密/解密的时候,通常是用在服务端和客户端通讯的过程中,一端加密传输,另一端解密使用。虽然 AES 加密看似简单,但在使用过程过程中,仍然会出现在一端加密ok,但是另一端解密失败的情况。一旦出现 AES 解密失败,我们可以通过以下几个方面进行排查:

1. AES 加密/解密 使用相同的密钥
2. 若涉及到偏移量,则AES 加密/解密 使用的偏移量要一样
3. AES 加密/解密 要使用相同加密数位,如都使用`AES_256`
4. AES 加密/解密 使用相同的字符集
5. AES 加密/解密 使用相同的加密,填充模式,如都使用`AES/CBC/PKCS5Padding`模式
6. 由于不同开发语言(CJava)及不同开发环境(JavaAndroid)的影响,可能相同的加解密算法在实现上出现差异,若你们注意到这个差异,就可能导致加解密出现问题

最后,当我们需要验证自己的 AES 解密算法是否与别人的加密方法为一套的时候。可以让加密方发你一份加密后的密文和加密前的明文,然后你用密文解密,看解密结果和加密方发你的是否一致。需要注意的是,加密方给你的明文要尽量简洁,如就 中国 二字,这样既能看出加密方和解密方的字符集是否一致,而且能避免复制粘贴等环节出现空格,回车等转义字符对验证结果的干扰。

实战

AES加解密

首先定义加密、解密工具类

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;

/**
 * AES加解密工具类
 */
public class AES {
    

    /**
     * AES加密
     *
     * @param key
     * @param iv
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static byte[] encryptAes(byte[] data, byte[] key, byte[] iv)
            throws GeneralSecurityException, IOException {
    
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
        return cipher.doFinal(data);
    }

    /**
     * AES解密
     *
     * @param key
     * @param iv
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static byte[] decryptAesToByteString(byte[] data, byte[] key, byte[] iv)
            throws GeneralSecurityException, IOException {
    
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
        return cipher.doFinal(data);
    }
}

加密和解密的代码很像,唯一的不同点是,加密 Cipher.ENCRYPT_MODE , 解密用的是 Cipher.DECRYPT_MODE

下面我们写一个测试代码:

        try {
    
            //加密密码
            String key = "zhaoyanjunzhaoy1";
            //偏移量
            String iv = "1234567890123456";

            String message = "今天是周二,我好开心";

            //加密
            byte[] encryResult = AES.encryptAes(message.getBytes(), key.getBytes(), iv.getBytes());

            //解密
            byte[] decryResult = AES.decryptAesToByteString(encryResult, key.getBytes(), iv.getBytes());
            System.out.println("解密数据 = " + new String(decryResult));

        } catch (IOException | GeneralSecurityException e) {
    
            e.printStackTrace();
        }

输出结果:

解密数据 = 今天是周二,我好开心

可以看到数据已经正常解密了。

AES默认实现类

不带模式和填充来获取AES算法的时候,其默认使用 AES/ECB/PKCS5Padding(输入可以不是16字节,也不需要填充向量), 所以不需要偏移量参数

Cipher cipher = Cipher.getInstance("AES");

我下面封装一个工具类

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;

/**
 * AES加解密工具类
 */
public class AES {
    

    /**
     * AES加密
     *
     * @param key
     * @throws GeneralSecurityException
     */
    public static byte[] encryptAes(byte[] data, byte[] key) throws GeneralSecurityException {
    
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
        return cipher.doFinal(data);
    }

    /**
     * AES解密
     *
     * @param key
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static byte[] decryptAesToByteString(byte[] data, byte[] key)
            throws GeneralSecurityException, IOException {
    
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
        return cipher.doFinal(data);
    }
}

测试代码

public class T2 {
    

    public static void main(String[] args) {
    
        try {
    
            //加密密码
            String key = "zhaoyanjunzhaoy1";

            //加密正文
            String message = "今天是周二,我好开心";

            //加密
            byte[] encryResult = AES.encryptAes(message.getBytes(), key.getBytes());

            //解密
            byte[] decryResult = AES.decryptAesToByteString(encryResult, key.getBytes());
            System.out.println("解密数据 = " + new String(decryResult));

        } catch (IOException | GeneralSecurityException e) {
    
            e.printStackTrace();
        }
    }
}

测试结果

解密数据 = 今天是周二,我好开心

AES随机加密

在上面的例子中,我们在 AES 加密中,需要指定规定长度的密码,偏移量。在 Java 中还给我们提供了 KeyGenerator 类来随机生成一个密码和偏移量,解决了我们动脑想密码的问题。

我们来看看随机加密怎么用。

 /**
     * AES加密/解密
     *
     * @throws GeneralSecurityException
     */
    public void encryptAes() throws GeneralSecurityException {
    
        //原始数据
        String message = "今天是周四,好开心哦";
        byte[] data = message.getBytes();

        //指定加密类型
        KeyGenerator keygen = KeyGenerator.getInstance("AES");
        //指定秘钥长度
        keygen.init(256);
        SecretKey secretKey = keygen.generateKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        //获取秘钥
        byte[] key = secretKey.getEncoded();
        //获取偏移量
        byte[] iv = cipher.getIV();

        //解密数据
        byte[] ciphertext = cipher.doFinal(data);

        //解密数据
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
        byte[] decryMessage = cipher.doFinal(ciphertext);

        System.out.println("解密数据 = " + new String(decryMessage));
    }

这种加密 key , iv 都是随机产生的,每次加密后的密文都不一样,适合一定的特殊场景。

随机是怎么发生的,我们就看

cipher.init(Cipher.ENCRYPT_MODE, secretKey);

在 init 方法的时候,JceSecurity.RANDOM 产生随机数,源码如下:

   public final void init(int opmode, Key key) throws InvalidKeyException {
    
        init(opmode, key, JceSecurity.RANDOM);
    }

具体细节就不看了,知道原理就行。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhaoyanjun6/article/details/120285594

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法