JAVA AES加密与解密

清疚 2022-06-08 01:47 314阅读 0赞

一、AES加密简介

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

AES 是一个新的可以用于保护电子数据的加密算法。明确地说,AES 是一个迭代的、对称密钥分组的密码,它可以使用128、192 和 256 位密钥,并且用 128 位(16字节)分组加密和解密数据。与公共密钥密码使用密钥对不同,对称密钥密码使用相同的密钥加密和解密数据。通过分组密码返回的加密数据 的位数与输入数据相同。迭代加密使用一个循环结构,在该循环中重复置换(permutations )和替换(substitutions)输入数据。

AES加密有很多轮的重复和变换。大致步骤如下:

1、密钥扩展(KeyExpansion)。
2、初始轮(Initial Round)。
3、重复轮(Rounds),每一轮又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey。
4、最终轮(Final Round),最终轮没有MixColumns。

二、AES在JAVA中的使用

JCE,Java Cryptography Extension,在早期JDK版本中,由于受美国的密码出口条例约束,Java中涉及加解密功能的API被限制出口,所以Java中安全组件被分成了两部分: 不含加密功能的JCA(Java Cryptography Architecture )和含加密功能的JCE(Java Cryptography Extension)。

JCA和JCE的API体系架构

JCE的API都在javax.crypto包下,核心功能包括:加解密、密钥生成(对称)、MAC生成、密钥协商。

(1). 加解密

加解密功能由Cipher组件提供,其也是JCE中最核心的组件。

1. Cipher的几个知识点:

a. Cipher在使用时需以参数方式指定transformation
b. transformation的格式为algorithm/mode/padding,其中algorithm为必输项,如: AES/DES/CBC/PKCS5Padding
c. 缺省的mode为ECB,缺省的padding为PKCS5Padding
d. 在block算法与流加密模式组合时, 需在mode后面指定每次处理的bit数, 如DES/CFB8/NoPadding, 如未指定则使用缺省值, SunJCE缺省值为64bits
e. Cipher有4种操作模式: ENCRYPT_MODE(加密), DECRYPT_MODE(解密), WRAP_MODE(导出Key), UNWRAP_MODE(导入Key),初始化时需指定某种操作模式

  1. 算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
  2. AES/CBC/NoPadding 16 不支持
  3. AES/CBC/PKCS5Padding 32 16
  4. AES/CBC/ISO10126Padding 32 16
  5. AES/CFB/NoPadding 16 原始数据长度
  6. AES/CFB/PKCS5Padding 32 16
  7. AES/CFB/ISO10126Padding 32 16
  8. AES/ECB/NoPadding 16 不支持
  9. AES/ECB/PKCS5Padding 32 16
  10. AES/ECB/ISO10126Padding 32 16
  11. AES/OFB/NoPadding 16 原始数据长度
  12. AES/OFB/PKCS5Padding 32 16
  13. AES/OFB/ISO10126Padding 32 16
  14. AES/PCBC/NoPadding 16 不支持
  15. AES/PCBC/PKCS5Padding 32 16
  16. AES/PCBC/ISO10126Padding 32 16

可以看到,在原始数据长度为16的整数倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,其它情况下加密数据长 度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],除了NoPadding填充之外的任何方 式,加密数据长度都等于16*(n+1);NoPadding填充情况下,CBC、ECB和PCBC三种模式是不支持的,CFB、OFB两种模式下则加密 数据长度等于原始数据长度。

2. 对称加密的算法与密钥长度选择














































算法名称 密钥长 块长 速度 说明
DES 56 64 64 不安全, 不要使用
3DES 112/168 64 很慢 中等安全, 适合加密较小的数据
AES 128, 192, 256 128 安全
Blowfish (4至56)*8 64 应该安全, 在安全界尚未被充分分析、论证
RC4 40-1024 64 很快 安全性不明确

推荐使用AES算法。一般认为128bits的密钥已足够安全,如果可以请选择256bits的密钥。注意:

a. 密钥长度是在生成密钥时指定的,如:

  1. KeyGenerator generator = KeyGenerator.getInstance("AES/CBC/PKCS5PADDING");
  2. generator.init(256);
  3. SecretKey key = generator.generateKey();

3. 加密示例代码

  1. /**
  2. * 根据密钥对指定的明文plainText进行加密.
  3. *
  4. * @param plainText 明文
  5. * @return 加密后的密文.
  6. */
  7. public static final String encrypt(String plainText) {
  8. Key secretKey = getKey("fendo888");
  9. try {
  10. Cipher cipher = Cipher.getInstance("AES");
  11. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
  12. byte[] p = plainText.getBytes("UTF-8");
  13. byte[] result = cipher.doFinal(p);
  14. BASE64Encoder encoder = new BASE64Encoder();
  15. String encoded = encoder.encode(result);
  16. return encoded;
  17. } catch (Exception e) {
  18. throw new RuntimeException(e);
  19. }
  20. }

4. 解密示例代码

  1. /**
  2. * 根据密钥对指定的密文cipherText进行解密.
  3. *
  4. * @param cipherText 密文
  5. * @return 解密后的明文.
  6. */
  7. public static final String decrypt(String cipherText) {
  8. Key secretKey = getKey("fendo888");
  9. try {
  10. Cipher cipher = Cipher.getInstance("AES");
  11. cipher.init(Cipher.DECRYPT_MODE, secretKey);
  12. BASE64Decoder decoder = new BASE64Decoder();
  13. byte[] c = decoder.decodeBuffer(cipherText);
  14. byte[] result = cipher.doFinal(c);
  15. String plainText = new String(result, "UTF-8");
  16. return plainText;
  17. } catch (Exception e) {
  18. throw new RuntimeException(e);
  19. }
  20. }

其中的getKey()

  1. public static Key getKey(String keySeed) {
  2. if (keySeed == null) {
  3. keySeed = System.getenv("AES_SYS_KEY");
  4. }
  5. if (keySeed == null) {
  6. keySeed = System.getProperty("AES_SYS_KEY");
  7. }
  8. if (keySeed == null || keySeed.trim().length() == 0) {
  9. keySeed = "abcd1234!@#$";// 默认种子
  10. }
  11. try {
  12. SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
  13. secureRandom.setSeed(keySeed.getBytes());
  14. KeyGenerator generator = KeyGenerator.getInstance("AES");
  15. generator.init(secureRandom);
  16. return generator.generateKey();
  17. } catch (Exception e) {
  18. throw new RuntimeException(e);
  19. }
  20. }

三.AESUtils帮助类

  1. package com.fendo.MD5;
  2. import java.io.IOException;
  3. import java.io.UnsupportedEncodingException;
  4. import java.security.InvalidKeyException;
  5. import java.security.NoSuchAlgorithmException;
  6. import java.security.SecureRandom;
  7. import javax.crypto.BadPaddingException;
  8. import javax.crypto.Cipher;
  9. import javax.crypto.IllegalBlockSizeException;
  10. import javax.crypto.KeyGenerator;
  11. import javax.crypto.NoSuchPaddingException;
  12. import javax.crypto.SecretKey;
  13. import javax.crypto.spec.SecretKeySpec;
  14. import org.apache.commons.lang3.StringUtils;
  15. import sun.misc.BASE64Decoder;
  16. import sun.misc.BASE64Encoder;
  17. /**
  18. * @Title: AESUtils.java
  19. * @Package com.fendo.MD5
  20. * @Description: TODO
  21. * @author fendo
  22. * @date 2017年9月11日 下午5:48:17
  23. * @version V1.0
  24. */
  25. public class AESUtils {
  26. private static final String encodeRules = "fendo";
  27. /**
  28. * 加密
  29. * 1.构造密钥生成器
  30. * 2.根据ecnodeRules规则初始化密钥生成器
  31. * 3.产生密钥
  32. * 4.创建和初始化密码器
  33. * 5.内容加密
  34. * 6.返回字符串
  35. */
  36. public static String AESEncode(String content) {
  37. try {
  38. //1.构造密钥生成器,指定为AES算法,不区分大小写
  39. KeyGenerator keygen = KeyGenerator.getInstance("AES");
  40. //2.根据ecnodeRules规则初始化密钥生成器
  41. //生成一个128位的随机源,根据传入的字节数组
  42. SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
  43. random.setSeed(encodeRules.getBytes());
  44. keygen.init(128, random);
  45. //3.产生原始对称密钥
  46. SecretKey original_key = keygen.generateKey();
  47. //4.获得原始对称密钥的字节数组
  48. byte[] raw = original_key.getEncoded();
  49. //5.根据字节数组生成AES密钥
  50. SecretKey key = new SecretKeySpec(raw, "AES");
  51. //6.根据指定算法AES自成密码器
  52. Cipher cipher = Cipher.getInstance("AES");
  53. //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
  54. cipher.init(Cipher.ENCRYPT_MODE, key);
  55. //8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
  56. byte[] byte_encode = content.getBytes("utf-8");
  57. //9.根据密码器的初始化方式--加密:将数据加密
  58. byte[] byte_AES = cipher.doFinal(byte_encode);
  59. //10.将加密后的数据转换为字符串
  60. //这里用Base64Encoder中会找不到包
  61. //解决办法:
  62. //在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
  63. String AES_encode = new String(new BASE64Encoder().encode(byte_AES));
  64. //11.将字符串返回
  65. return AES_encode;
  66. } catch (NoSuchAlgorithmException e) {
  67. e.printStackTrace();
  68. } catch (NoSuchPaddingException e) {
  69. e.printStackTrace();
  70. } catch (InvalidKeyException e) {
  71. e.printStackTrace();
  72. } catch (IllegalBlockSizeException e) {
  73. e.printStackTrace();
  74. } catch (BadPaddingException e) {
  75. e.printStackTrace();
  76. } catch (UnsupportedEncodingException e) {
  77. e.printStackTrace();
  78. }
  79. //如果有错就返加nulll
  80. return null;
  81. }
  82. /**
  83. * AES加密
  84. * @param content 待加密的内容
  85. * @param encryptKey 加密密钥
  86. * @return 加密后的byte[]
  87. * @throws Exception
  88. */
  89. public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
  90. KeyGenerator kgen = KeyGenerator.getInstance("AES");
  91. kgen.init(128, new SecureRandom(encryptKey.getBytes()));
  92. Cipher cipher = Cipher.getInstance("AES");
  93. cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
  94. return cipher.doFinal(content.getBytes("utf-8"));
  95. }
  96. /**
  97. * 解密
  98. * 解密过程:
  99. * 1.同加密1-4步
  100. * 2.将加密后的字符串反纺成byte[]数组
  101. * 3.将加密内容解密
  102. */
  103. public static String AESDecode(String content) {
  104. try {
  105. //1.构造密钥生成器,指定为AES算法,不区分大小写
  106. KeyGenerator keygen = KeyGenerator.getInstance("AES");
  107. //2.根据ecnodeRules规则初始化密钥生成器
  108. //生成一个128位的随机源,根据传入的字节数组
  109. SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
  110. random.setSeed(encodeRules.getBytes());
  111. keygen.init(128, random);
  112. //3.产生原始对称密钥
  113. SecretKey original_key = keygen.generateKey();
  114. //4.获得原始对称密钥的字节数组
  115. byte[] raw = original_key.getEncoded();
  116. //5.根据字节数组生成AES密钥
  117. SecretKey key = new SecretKeySpec(raw, "AES");
  118. //6.根据指定算法AES自成密码器
  119. Cipher cipher = Cipher.getInstance("AES");
  120. //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
  121. cipher.init(Cipher.DECRYPT_MODE, key);
  122. //8.将加密并编码后的内容解码成字节数组
  123. byte[] byte_content = new BASE64Decoder().decodeBuffer(content);
  124. /*
  125. * 解密
  126. */
  127. byte[] byte_decode = cipher.doFinal(byte_content);
  128. String AES_decode = new String(byte_decode, "utf-8");
  129. return AES_decode;
  130. } catch (NoSuchAlgorithmException e) {
  131. e.printStackTrace();
  132. } catch (NoSuchPaddingException e) {
  133. e.printStackTrace();
  134. } catch (InvalidKeyException e) {
  135. e.printStackTrace();
  136. } catch (IOException e) {
  137. e.printStackTrace();
  138. } catch (IllegalBlockSizeException e) {
  139. e.printStackTrace();
  140. } catch (BadPaddingException e) {
  141. e.printStackTrace();
  142. }
  143. //如果有错就返加nulll
  144. return null;
  145. }
  146. /**
  147. * AES解密
  148. * @param encryptBytes 待解密的byte[]
  149. * @param decryptKey 解密密钥
  150. * @return 解密后的String
  151. * @throws Exception
  152. */
  153. public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
  154. KeyGenerator kgen = KeyGenerator.getInstance("AES");
  155. kgen.init(128, new SecureRandom(decryptKey.getBytes()));
  156. Cipher cipher = Cipher.getInstance("AES");
  157. cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
  158. byte[] decryptBytes = cipher.doFinal(encryptBytes);
  159. return new String(decryptBytes);
  160. }
  161. /**
  162. * base 64 加密
  163. * @param bytes 待编码的byte[]
  164. * @return 编码后的base 64 code
  165. */
  166. public static String base64Encode(byte[] bytes){
  167. return new BASE64Encoder().encode(bytes);
  168. }
  169. /**
  170. * base 64 解密
  171. * @param base64Code 待解码的base 64 code
  172. * @return 解码后的byte[]
  173. * @throws Exception
  174. */
  175. public static byte[] base64Decode(String base64Code) throws Exception{
  176. return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
  177. }
  178. /**
  179. * 将base 64 code AES解密
  180. * @param encryptStr 待解密的base 64 code
  181. * @param decryptKey 解密密钥
  182. * @return 解密后的string
  183. * @throws Exception
  184. */
  185. public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
  186. return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
  187. }
  188. /**
  189. * AES加密为base 64 code
  190. * @param content 待加密的内容
  191. * @param encryptKey 加密密钥
  192. * @return 加密后的base 64 code
  193. * @throws Exception
  194. */
  195. public static String aesEncrypt(String content, String encryptKey) throws Exception {
  196. return base64Encode(aesEncryptToBytes(content, encryptKey));
  197. }
  198. public static void main(String[] args) {
  199. String[] keys = {
  200. "", "root"
  201. };
  202. System.out.println("key | AESEncode | AESDecode");
  203. for (String key : keys) {
  204. System.out.print("key:"+key + " | ");
  205. String encryptString = AESEncode(key);
  206. System.out.print(encryptString + " | ");
  207. String decryptString = AESDecode(encryptString);
  208. System.out.println(decryptString);
  209. }
  210. }
  211. }

发表评论

表情:
评论列表 (有 0 条评论,314人围观)

还没有评论,来说两句吧...

相关阅读

    相关 AES 加密解密

    java AES 加密与解密 近些年DES使用越来越少,原因就在于其使用56位密钥,比较容易被破解,近些年来逐渐被AES替代,AES已经变成目前对称加密中最流行算法之一;A