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

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签