单向加密
和双向加密
。
单向加密
包括MD5、SHA等摘要算法,它们是不可逆的。双向加密
包括对称加密和非对称加密。双向加密是可逆的,存在密文的密钥。
对称加密
是指加密和解密使用相同的密钥,包括AES加密、DES加密等。非对称加密
是指加密和解密使用不同的密钥,包括RSA加密等。AES: 高级加密标准(Advanced Encryption Standard)是美国联邦政府采用的一种区块加密标准,是目前最流行的一种
对称加密算法
。
- 是用来替代DES的新一代分组加密算法。
- AES支持三种长度的密钥:128位、192位、256位。
AES的加解密过程和DES一样,都是通过分组加密
、分组解密。所谓分组加密,就是将待加解密的内容按照128位进行分组,将密钥按照128位、192位、256位进行分组,分别将分组后的明文与相应分组后的密钥进行加解密。
加密: 明文与密钥分组后,对每组:明文组与密钥组处理 -> 轮密钥加 -> 10轮加密 -> 密文组
解密: 对每组:密文组 -> 轮密钥加 -> 10轮解密 -> 明文组
明文分组: 每组长度相等,都是128位(16字节);
密钥分组: 有128位、192位、256位,推荐加密轮数分别为 10、12、14
密钥组处理: 以密钥分组每组128位为例(则推荐加密轮数为10,前9次执行操作一样,第十次有所不同)
类似地,128位密钥也是用字节为单位的矩阵表示,通过密钥编排函数,形成具有44个元素的序列W[0],W[1], … ,W[43](每个元素4个字节);其中,W[0],W[1],W[2],W[3]为原始密钥,其余40个元素分为10组,每组4个元素(4*4=16字节),分别用于10轮加密。
AES加密算法涉及4种操作: 字节替代
(SubBytes)、行移位
(ShiftRows)、列混淆
(MixColumns)和轮密钥加
(AddRoundKey)。下图给出了AES加解密的流程:
假如一段明文长度是192bit,如果按每128bit一个明文块来拆分的话,第二个明文块只有64bit,不足128bit。这时候怎么办呢?就需要对明文块进行填充(Padding)。
填充涉及以下三种填充模式
:
NoPadding
:不做任何填充,但是要求明文必须是16字节的整数倍。
PKCS5Padding
(默认):如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。
比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}
ISO10126Padding
:如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字节,最后一个字符值等于缺少的字符数,其他字符填充随机数。
比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则可能补全为{1,2,3,4,5,a,b,c,d,e,5,c,3,G,$,6}
注意:AES加密包含Base64加密
加密: AES加密 -> Base64加密 -> 密文
解密: Base64解密 -> AES解密 -> 明文
生成16个大小写字母和数字的随机数即可:
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 数字和26个字母组成
private static final Random RANDOM = new SecureRandom();
/**
* 获取长度为 6 的随机字母+数字
* @return 随机数字
*/
public static String getRandomNumber() {
char[] nonceChars = new char[16]; //指定长度为6位/自己可以要求设置
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* AES加密工具类
*
* @author ACGkaka
* @since 2021-06-18 19:11:03
*/
public class AESUtil {
/**
* 日志相关
*/
private static final Logger LOGGER = LoggerFactory.getLogger(AESUtil.class);
/**
* 编码
*/
private static final String ENCODING = "UTF-8";
/**
* 算法定义
*/
private static final String AES_ALGORITHM = "AES";
/**
* 指定填充方式
*/
private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";
private static final String CIPHER_CBC_PADDING = "AES/CBC/PKCS5Padding";
/**
* 偏移量(CBC中使用,增强加密算法强度)
*/
private static final String IV_SEED = "1234567812345678";
/**
* AES加密
* @param content 待加密内容
* @param aesKey 密码
* @return
*/
public static String encrypt(String content, String aesKey){
if(StringUtils.isBlank(content)){
LOGGER.info("AES encrypt: the content is null!");
return null;
}
//判断秘钥是否为16位
if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
try {
//对密码进行编码
byte[] bytes = aesKey.getBytes(ENCODING);
//设置加密算法,生成秘钥
SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
//选择加密
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
//根据待加密内容生成字节数组
byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
//返回base64字符串
return Base64Utils.encodeToString(encrypted);
} catch (Exception e) {
LOGGER.info("AES encrypt exception:" + e.getMessage());
throw new RuntimeException(e);
}
}else {
LOGGER.info("AES encrypt: the aesKey is null or error!");
return null;
}
}
/**
* 解密
*
* @param content 待解密内容
* @param aesKey 密码
* @return
*/
public static String decrypt(String content, String aesKey){
if(StringUtils.isBlank(content)){
LOGGER.info("AES decrypt: the content is null!");
return null;
}
//判断秘钥是否为16位
if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
try {
//对密码进行编码
byte[] bytes = aesKey.getBytes(ENCODING);
//设置解密算法,生成秘钥
SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(CIPHER_PADDING);
//选择解密
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
//先进行Base64解码
byte[] decodeBase64 = Base64Utils.decodeFromString(content);
//根据待解密内容进行解密
byte[] decrypted = cipher.doFinal(decodeBase64);
//将字节数组转成字符串
return new String(decrypted, ENCODING);
} catch (Exception e) {
LOGGER.info("AES decrypt exception:" + e.getMessage());
throw new RuntimeException(e);
}
}else {
LOGGER.info("AES decrypt: the aesKey is null or error!");
return null;
}
}
/**
* AES_CBC加密
*
* @param content 待加密内容
* @param aesKey 密码
* @return
*/
public static String encryptCBC(String content, String aesKey){
if(StringUtils.isBlank(content)){
LOGGER.info("AES_CBC encrypt: the content is null!");
return null;
}
//判断秘钥是否为16位
if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
try {
//对密码进行编码
byte[] bytes = aesKey.getBytes(ENCODING);
//设置加密算法,生成秘钥
SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
//偏移
IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));
//选择加密
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
//根据待加密内容生成字节数组
byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
//返回base64字符串
return Base64Utils.encodeToString(encrypted);
} catch (Exception e) {
LOGGER.info("AES_CBC encrypt exception:" + e.getMessage());
throw new RuntimeException(e);
}
}else {
LOGGER.info("AES_CBC encrypt: the aesKey is null or error!");
return null;
}
}
/**
* AES_CBC解密
*
* @param content 待解密内容
* @param aesKey 密码
* @return
*/
public static String decryptCBC(String content, String aesKey){
if(StringUtils.isBlank(content)){
LOGGER.info("AES_CBC decrypt: the content is null!");
return null;
}
//判断秘钥是否为16位
if(StringUtils.isNotBlank(aesKey) && aesKey.length() == 16){
try {
//对密码进行编码
byte[] bytes = aesKey.getBytes(ENCODING);
//设置解密算法,生成秘钥
SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
//偏移
IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
//选择解密
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先进行Base64解码
byte[] decodeBase64 = Base64Utils.decodeFromString(content);
//根据待解密内容进行解密
byte[] decrypted = cipher.doFinal(decodeBase64);
//将字节数组转成字符串
return new String(decrypted, ENCODING);
} catch (Exception e) {
LOGGER.info("AES_CBC decrypt exception:" + e.getMessage());
throw new RuntimeException(e);
}
}else {
LOGGER.info("AES_CBC decrypt: the aesKey is null or error!");
return null;
}
}
public static void main(String[] args) {
// AES支持三种长度的密钥:128位、192位、256位。
// 代码中这种就是128位的加密密钥,16字节 * 8位/字节 = 128位。
String random = RandomStringUtils.random(16, "abcdefghijklmnopqrstuvwxyz1234567890");
System.out.println("随机key:" + random);
System.out.println();
System.out.println("---------加密---------");
String aesResult = encrypt("测试AES加密12", random);
System.out.println("aes加密结果:" + aesResult);
System.out.println();
System.out.println("---------解密---------");
String decrypt = decrypt(aesResult, random);
System.out.println("aes解密结果:" + decrypt);
System.out.println();
System.out.println("--------AES_CBC加密解密---------");
String cbcResult = encryptCBC("测试AES加密12456", random);
System.out.println("aes_cbc加密结果:" + cbcResult);
System.out.println();
System.out.println("---------解密CBC---------");
String cbcDecrypt = decryptCBC(cbcResult, random);
System.out.println("aes解密结果:" + cbcDecrypt);
System.out.println();
}
}
随机key:golrtt58318fx7ol
---------加密---------
aes加密结果:Xy8W9lCeVue9Ao36z+duM7D7WeS5tdBihIMb1q9KpNg=
---------解密---------
aes解密结果:测试AES加密12
--------AES_CBC加密解密---------
aes_cbc加密结果:xs3ypQXyd62P9jB0+RvOqxFnHIHBIlVdqoZLuqYNBLw=
---------解密CBC---------
aes解密结果:测试AES加密12456
验证地址:http://tool.chacuo.net/cryptaes
验证完毕,完结撒花 ~
参考:
1.什么是AES加密?详解AES加密算法原理流程:https://blog.csdn.net/m0_69916115/article/details/126750612
2.AES加密:https://blog.csdn.net/weixin_52553215/article/details/124276828
文章浏览阅读2k次。js 异步编程_js异步解决方案
文章浏览阅读2.8k次。 Hbase数据导出实例 需求:根据时间范围、区域等条件查询,将hbase中终端采集数据最大时间、最小时间的日志数据导出参考文档:http://blog.csdn.net/qq_27078095/article/details/56482010https://www.cnblogs.com/szw-blog/p/5512349.htmlhttp://bl..._hbase shell 导出查询
文章浏览阅读294次。先跑起来开始阅读一份项目源码的第一步,是先让这个项目能够通过你自己编译通过并且顺利跑起来。这一点尤其重要。有的项目比较复杂,依赖的组件多,搭建起一个调试环境并不容易,所以并不见得是所有项目都能顺利的跑起来。如果能自己编译跑起来,那么后面讲到的情景分析、加上调试代码、调试等等才有展开的基础。就我的经验而言,一个项目代码,是否能顺利的搭建调试环境,效率大不一样。跑起来之后,又要尽量的精简自己的..._如何阅读一套战斗系统的源码
文章浏览阅读1k次。系统:android4.0.4在android/packages/apps/Browser/res/res/values/string.xml中红色部分 http://www.google" and the user signs up the device with a Google sites account, the site's_yandex设置主页
文章浏览阅读384次。web服务器在调用doFilter方法时,会传递一filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个Java类称之为过滤器Filter。_过滤器java
文章浏览阅读178次,点赞2次,收藏2次。今天来写一下管理员的Service层,创建ManageService接口,该接口继承BaseService接口,然后再创建接口的实例ManageServiceImpl@Servicepublic class ManageServiceImpl extends BaseServiceImpl<Manage> { @Autowired ManageMapper manageMapper; @Override public BaseDao<Manage>_gin 商城项目
文章浏览阅读692次。工作日志记录,方便以后使用:具体效果如下:具体代码如下(两种实现方式都在MainActivity.java中):MainActivity.java代码如下:package com.xuganwen.testdrag;import androidx.annotation.NonNull;import androidx.appcompat.app.AppCompatA..._baserecyclerviewadapterhelper拖动顺序
文章浏览阅读52次。ocket 模块Socket 模块提供了 UNIX 程序员所熟悉的基本网络服务(也称为 BSD API)。这个模块中提供了在构建 socket 服务器和客户机时所需要的所有功能。这个 API 与标准的 C API 之间的区别在于它是面向对象的。在C中,socket描述符是从socket调用中获得的,然后会作为一个参数传递给BSD API函数。在Python中,socket 方法会向应用so...
文章浏览阅读2.6k次。话不多说,直接上代码。如果有什么错误,直接喷!#include <stdio.h>#include <stdlib.h>#define LEN 100#define LENINC 10int main(){ char string[LEN] ; char string1[] = {"asdfghjkl"}; char stri..._c语言中字符串基本操作
文章浏览阅读262次。转载请注明出处:http://blog.csdn.NET/singwhatiwanna/article/details/17428923说明:博主虚心接受大家的抨击,批评,指正前言我一直想介绍下工厂模式,我曾经搞过J2EE,用的是轻量级SSH框架,其中spring有IOC概念,可以称之为控制反转或者依赖注入,在系统开发中,IOC可以很好的替代工厂模式。若干年前,我只用过IOC,_工厂模式 很扯淡
文章浏览阅读562次。30.1 概述pkcs12(个人数字标准)用于存放用户证书、crl、用户私钥以及证书链,pkcs12中的私钥是加密存放的。30.2 openssl实现openssl的pkcs12实现在crypto/pkcs12目录,有如下源码:目录架构用途p12_add.c处理PCKS12_SAFEBAG, PKCS12_SAFEBAG用于存放正数和私钥相关的信息p12_attr.c属性处理p12_ct..._openssl生成pck12
文章浏览阅读2w次。昨天在测试号上用OAuth2.0网页授权方式静默获取用户openid,然后暂存在session中,最后连同注册表单信息一起填入数据库,以达到自动绑定的目的。然而今天偶然发现订阅号无法开通网页授权接口!!!!!!欲哭无泪。。。解决方案如下,虽然用户体验照比网页授权差一点,但总比日后让用户手动绑定体验好得多:如果是view类型自定义菜单的话,修改为click类型。click事件中是包含fromUserN_订阅号 订阅号无法开通此接口