1. AES

1.1. 概念

密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
这个标准用来替代原先的DES(Data Encryption Standard),已经被多方分析且广为全世界所使用,已然成为对称密钥加密中最流行的算法之一。详见 百科 高级加密标准 AES

1.2. JAVA实现AES加解密

import lombok.extern.slf4j.Slf4j;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Arrays;@Slf4j
class AESCryptoUtil {public static void main(String[] args) {String content = "美好的世界,美好的中国,美好的未来!!!";byte[] encryptResult = encrypt(content);byte[] decryptResult = decrypt(encryptResult);String encryptStr = parseByte2HexStr(encryptResult);printMsg("========\n加密前:%s\n加密后:%s\n解密后:%s", content, encryptStr, new String(decryptResult));//解密时,不能new String(encryptResult,"utf-8").getBytes("utf-8")decryptResult = decrypt(parseHexStr2Byte(encryptStr));printMsg("========\n加密前:%s\n加密后:%s\n解密后:%s", content, encryptStr,new String(decryptResult));//采用AES/ECB/NoPadding时,要求密钥长度16、24、32中的一个、待加密内容的byte[]长度必须是16的倍数encryptResult = encrypt2(content);decryptResult = decrypt2(encryptResult);encryptStr = parseByte2HexStr(encryptResult);printMsg("========\n加密前:%s\n加密后:%s\n解密后:%s", content, encryptStr, new String(decryptResult));}private static void printMsg(String template, Object... args) {System.out.println(String.format(template, args));}private static String password = "科技兴国@!##";private static SecretKeySpec key;static {init();}private static void init() {try {KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(256, new SecureRandom(password.getBytes()));SecretKey secretKey = kgen.generateKey();byte[] enCodeFormat = secretKey.getEncoded();key = new SecretKeySpec(enCodeFormat, "AES");} catch (Exception e) {log.error("初始化AES算法失败" + e.getMessage(), e);}}/*** 加密** @param content 需要加密的内容* @return*/public static byte[] encrypt(String content) {try {Cipher cipher = Cipher.getInstance("AES");byte[] byteContent = content.getBytes("utf-8");cipher.init(Cipher.ENCRYPT_MODE, key);byte[] result = cipher.doFinal(byteContent);return result;} catch (Exception e) {log.error("AES加密失败" + e.getMessage(), e);}return null;}public static byte[] decrypt(byte[] content) {try {Cipher cipher = Cipher.getInstance("AES");cipher.init(Cipher.DECRYPT_MODE, key);byte[] result = cipher.doFinal(content);return result;} catch (Exception e) {log.error("AES解密失败" + e.getMessage(), e);}return null;}/*** 加密** @param content 需要加密的内容* @return*/public static byte[] encrypt2(String content) {try {byte[] bytePassword = password.getBytes();bytePassword = checkByteLength(bytePassword);SecretKeySpec key = new SecretKeySpec(bytePassword, "AES");Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");byte[] byteContent = content.getBytes("utf-8");byteContent = checkByteLength(byteContent);cipher.init(Cipher.ENCRYPT_MODE, key);byte[] result = cipher.doFinal(byteContent);return result;} catch (Exception e) {log.error("AES加密失败" + e.getMessage(), e);}return null;}public static byte[] decrypt2(byte[] content) {try {byte[] bytePassword = password.getBytes();bytePassword = checkByteLength(bytePassword);SecretKeySpec key = new SecretKeySpec(bytePassword, "AES");Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");cipher.init(Cipher.DECRYPT_MODE, key);byte[] result = cipher.doFinal(content);return result;} catch (Exception e) {log.error("AES解密失败" + e.getMessage(), e);}return null;}private static byte[] checkByteLength(byte[] byteContent) {int length = byteContent.length;int remainder = length % 16;if(remainder == 0){return byteContent;}else{return Arrays.copyOf(byteContent,length+(16-remainder));}}/**将二进制转换成16进制* @param buf* @return*/public static String parseByte2HexStr(byte buf[]) {StringBuffer sb = new StringBuffer();for (int i = 0; i < buf.length; i++) {String hex = Integer.toHexString(buf[i] & 0xFF);if (hex.length() == 1) {hex = '0' + hex;}sb.append(hex.toUpperCase());}return sb.toString();}public static byte[] parseHexStr2Byte(String hexStr) {if (hexStr.length() < 1) {return null;}byte[] result = new byte[hexStr.length()/2];for (int i = 0;i< hexStr.length()/2; i++) {int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);result[i] = (byte) (high * 16 + low);}return result;}
}

2. 使用中的各种坑

2.1. 加密后的byte数组不能强制转换成字符串

加密后返回byte[],此byte数组强制转换为字符串是乱码,然后再用转换后的字符串转换为byte数组去解码(见下面的代码)会报错:Input length must be multiple of 16 when decrypting with padded cipher

在这种情况下: 字符串和byte数组不是互逆的;要避免这种情况,需要做一些修订,可以考虑将二进制数据转换成十六进制表示。详见parseByte2HexStr、parseHexStr2Byte两个方法

decryptResult = decrypt(new String(encryptResult,"utf-8").getBytes("utf-8"));

2021-07-02 11:27:39.067 [main] ERROR pattern.chain.AESCryptoUtil:decrypt:87 - AES解密失败Input length must be multiple of 16 when decrypting with padded cipher
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) ~[sunjce_provider.jar:1.8.0_171]
at javax.crypto.Cipher.doFinal(Cipher.java:2164) ~[?:1.8.0_171]
at org.apache.design.pattern.chain.AESCryptoUtil.decrypt(AESCryptoUtil.java:84) [classes/:?]
at org.apache.design.pattern.chain.AESCryptoUtil.main(AESCryptoUtil.java:26) [classes/:?]

2.2. 采用AES/ECB/NoPadding加密的限制

当采用如下方式获取Cipher实例时,则要求密钥长度16、24、32中的一个、待加密内容的byte[]长度必须是16的倍数,否则报如下错误:

Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");

2021-07-02 11:37:15.432 [main] ERROR pattern.chain.AESCryptoUtil:encrypt2:111 - AES加密失败Input length not multiple of 16 bytes
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1042) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:1010) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) ~[sunjce_provider.jar:1.8.0_171]
at javax.crypto.Cipher.doFinal(Cipher.java:2164) ~[?:1.8.0_171]


2021-07-02 11:36:39.064 [main] ERROR pattern.chain.AESCryptoUtil:encrypt2:111 - AES加密失败Invalid AES key length: 18 bytes
java.security.InvalidKeyException: Invalid AES key length: 18 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:94) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:592) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:468) ~[sunjce_provider.jar:1.8.0_171]
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313) ~[sunjce_provider.jar:1.8.0_171]
at javax.crypto.Cipher.implInit(Cipher.java:801) ~[?:1.8.0_171]
at javax.crypto.Cipher.chooseProvider(Cipher.java:863) ~[?:1.8.0_171]

2.3 美国的出口管制限制引起的坑

2.3.1. 背景

因为美国的出口管制限制,Java发布的运行环境包中的加解密有一定的限制。
比如默认不允许256位密钥的AES加解密,如果使用,会报如下错误:
2021-07-02 11:40:34.048 [main] ERROR pattern.chain.AESCryptoUtil:encrypt:75 - AES加密失败Illegal key size or default parameters
java.security.InvalidKeyException: Illegal key size or default parameters
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1025) ~[?:1.8.0_171]
at javax.crypto.Cipher.implInit(Cipher.java:800) ~[?:1.8.0_171]
at javax.crypto.Cipher.chooseProvider(Cipher.java:863) ~[?:1.8.0_171]
at javax.crypto.Cipher.init(Cipher.java:1248) ~[?:1.8.0_171]
at javax.crypto.Cipher.init(Cipher.java:1185) ~[?:1.8.0_171]

从Java 1.8.0_151

2.3.2. 解决方案

方案1
将%JDK_HOME%\jre\lib\security\java.security, 找到定义java安全性属性crypto.policy的行,将值修改为unlimited。
默认情况下,您应该能找到一条注释掉的行:
#crypto.policy=unlimited
您可以通过取消注释该行来启用无限制,删除#:

方案2
如果%JDK_HOME%\jre\lib\security目录下有jar包 且 crypto.policy 未配置,则使用jar包内置的规则
将%JDK_HOME%\jre\lib\security\policy\unlimited下的2个JAR【local_policy.jar、US_export_policy.jar】复制到%JDK_HOME%\jre\lib\security目录下,如果有,直接覆盖。

方案3
如果%JDK_HOME%\jre\lib\security目录下没有jar包 且 crypto.policy 未配置,则默认启用无限制的。

升级版本
https://www.oracle.com/technetwork/java/javase/8u161-relnotes-4021379.html
在官方文档写到,
security-libs/javax.crypto
Unlimited cryptography enabled by default
The JDK uses the Java Cryptography Extension (JCE) Jurisdiction Policy files to configure cryptographic algorithm restrictions. Previously, the Policy files in the JDK placed limits on various algorithms. This release ships with both the limited and unlimited jurisdiction policy files, with unlimited being the default. The behavior can be controlled via the new ‘crypto.policy’ Security property found in the /lib/java.security file. Please refer to that file for more information on this property.
也就是从 1.8.0_161-b12 版本后,默认将采用无限制的加密算法,也就是使用 unlimited 下的jar包。我们也可以通过 设置 java.security 文件的 crypto.policy的值来改变这个默认的值。

JAVA加密--AES加密算法JAVA实现及使用中的各种坑,超实用相关推荐

  1. AES加密算法 Java与Python跨平台实现

    AES加密算法 Java与Python跨平台实现 什么是AES Java的实现 Python的实现 结果 什么是AES Note: 网上有很多实现代码但是鱼龙混杂,笔者摸索了半天,实现了AES加密文本 ...

  2. JAVA:实现AES 加密算法(附完整源码)

    JAVA:实现AES 加密算法 package com.thealgorithms.ciphers;import javax.crypto.*; import java.security.Invali ...

  3. JAVA加密狗(JAVA程序加密保护,防拷贝和防止反编译)

    JAVA加密狗(JAVA程序加密保护,防拷贝和防止反编译)      众所周知,java为开发语言提供了很方便的开发平台,但开发出来的程序很容易在不同的平台上面被移植,现在越来越多的人使用它开发软件. ...

  4. Java 加密 AES 对称加密算法

    版权声明:本文为博主原创文章,未经博主允许不得转载. [AES] 一种对称加密算法,DES的取代者. 加密相关文章见:Java 加密解密 对称加密算法 非对称加密算法 MD5 BASE64 AES R ...

  5. Java实现AES加密算法

    最近恶补了一些关于加密算法的知识,然后用编程语言来实现 AES简介 高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个 ...

  6. AES加密算法java实现

    转载自: https://blog.csdn.net/zyhlwzy/article/details/77948165 AES加密算法是密码学中的高级加密标准,该加密算法采用对称分组密码体制,密钥长度 ...

  7. aes加密算法 java实现,AES加密算法的java实现

    AES加密算法的java实现 package com.encryp; import java.security.InvalidKeyException; import java.security.No ...

  8. java 基于口令加密算法,Java安全编程:基于口令加密(PBE)

    安全 PBE 在之前的文章中曾讲到过DES加密算法,类似这种加密算法都有一个密钥,密钥的长度决定了加密的安全性,但是这种密钥比较难记忆,是需要存储的. PBE算法是一种基于口令的加密算法,它并不是构建 ...

  9. java delphi aes加密算法_Delphi AES,又一个加密算法例子

    /// //AES DEMO V1.0// //作者:ksaiy// //欢迎使用由ksaiy制作的AES加密算法演示程序,此算法为标准的AES算法,你可以根据的 //的自己需要进行变形.具体怎么操作 ...

最新文章

  1. js调试控制台使用详解图解
  2. sql 2005学习笔记1
  3. 互联网思维与非摩擦经济
  4. [云炬学英语]每日一句2020.8.26
  5. 利用MyBatis Generator进行数据层代码自动生成
  6. 天下第一 txdy (LCT+双指针+线段树)
  7. 2021牛客暑期多校训练营4 H-Convolution(数学)
  8. 大数据翻页_大数据量下的分页解决方法
  9. 炒菜机器人放食材的顺序_如果给你个做饭机器人,你会让它做些什么饭菜?
  10. C#常用的文件操作 (转)
  11. torch学习笔记--tensor介绍2,对tensor的结构
  12. 如何有效的为Windows XP减肥
  13. 工科学生考研能选择计算机专业么,考研应该如何选择学校和专业
  14. WPF 凭证分录控件
  15. linux获取文件名最后一位,获取出文件最后一位是1 或者0 若果都是1 代表是正确的 如果有0代表错误...
  16. Linux内核移植操作步骤
  17. Xcode打包后,找不到dSYM文件
  18. 微信小程序 谈谈在大学初次写项目的体验
  19. c++的一些小知识点
  20. JS写一个简单的五星评价

热门文章

  1. 为什么程序下在其他盘,c盘空间还是增多了?appdata文件夹有什么用途?
  2. android 切换到阿拉伯语电话号码+号显示在右侧及顺序错乱的处理
  3. TypeScript中type和interface区别
  4. 单模单纤FC接口光纤传输距离
  5. 2021年T电梯修理最新解析及T电梯修理模拟试题
  6. linux远程获取文件,Linux下实现获取远程机器文件
  7. 什么是网站内链接及内链优化的好处作用?
  8. 顺丰无人机的前世今生
  9. 标准对比-UHAST/稳态湿热
  10. 计算机网络实践之元气骑士公网异地联机(二) 两种方案可行性分析