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

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签