Java加密算法 AES

x33g5p2x  于2021-12-25 转载在 其他  
字(5.8k)|赞(0)|评价(0)|浏览(436)
  1. package com.stone.security;
  2. import java.util.Arrays;
  3. import javax.crypto.Cipher;
  4. import javax.crypto.KeyGenerator;
  5. import javax.crypto.SecretKey;
  6. import javax.crypto.spec.IvParameterSpec;
  7. /**
  8. * AES 算法 对称加密,密码学中的高级加密标准 2005年成为有效标准
  9. * @author stone
  10. * @date 2014-03-10 06:49:19
  11. */
  12. public class AES {
  13. static Cipher cipher;
  14. static final String KEY_ALGORITHM = "AES";
  15. static final String CIPHER_ALGORITHM_ECB = "AES/ECB/PKCS5Padding";
  16. /*
  17. *
  18. */
  19. static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
  20. /*
  21. * AES/CBC/NoPadding 要求
  22. * 密钥必须是16字节长度的;Initialization vector (IV) 必须是16字节
  23. * 待加密内容的字节长度必须是16的倍数,如果不是16的倍数,就会出如下异常:
  24. * javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
  25. *
  26. * 由于固定了位数,所以对于被加密数据有中文的, 加、解密不完整
  27. *
  28. * 可 以看到,在原始数据长度为16的整数n倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,
  29. * 其它情况下加密数据长 度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],
  30. * 除了NoPadding填充之外的任何方 式,加密数据长度都等于16*(n+1).
  31. */
  32. static final String CIPHER_ALGORITHM_CBC_NoPadding = "AES/CBC/NoPadding";
  33. static SecretKey secretKey;
  34. public static void main(String[] args) throws Exception {
  35. method1("a*jal)k32J8czx囙国为国宽");
  36. method2("a*jal)k32J8czx囙国为国宽");
  37. method3("a*jal)k32J8czx囙国为国宽");
  38. method4("123456781234囙为国宽");// length = 16
  39. method4("12345678abcdefgh");// length = 16
  40. }
  41. /**
  42. * 使用AES 算法 加密,默认模式 AES/ECB
  43. */
  44. static void method1(String str) throws Exception {
  45. cipher = Cipher.getInstance(KEY_ALGORITHM);
  46. //KeyGenerator 生成aes算法密钥
  47. secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
  48. System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
  49. cipher.init(Cipher.ENCRYPT_MODE, secretKey);//使用加密模式初始化 密钥
  50. byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
  51. System.out.println("method1-加密:" + Arrays.toString(encrypt));
  52. cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
  53. byte[] decrypt = cipher.doFinal(encrypt);
  54. System.out.println("method1-解密后:" + new String(decrypt));
  55. }
  56. /**
  57. * 使用AES 算法 加密,默认模式 AES/ECB/PKCS5Padding
  58. */
  59. static void method2(String str) throws Exception {
  60. cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
  61. //KeyGenerator 生成aes算法密钥
  62. secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
  63. System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
  64. cipher.init(Cipher.ENCRYPT_MODE, secretKey);//使用加密模式初始化 密钥
  65. byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
  66. System.out.println("method2-加密:" + Arrays.toString(encrypt));
  67. cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
  68. byte[] decrypt = cipher.doFinal(encrypt);
  69. System.out.println("method2-解密后:" + new String(decrypt));
  70. }
  71. static byte[] getIV() {
  72. String iv = "1234567812345678"; //IV length: must be 16 bytes long
  73. return iv.getBytes();
  74. }
  75. /**
  76. * 使用AES 算法 加密,默认模式 AES/CBC/PKCS5Padding
  77. */
  78. static void method3(String str) throws Exception {
  79. cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
  80. //KeyGenerator 生成aes算法密钥
  81. secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
  82. System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
  83. cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用加密模式初始化 密钥
  84. byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
  85. System.out.println("method3-加密:" + Arrays.toString(encrypt));
  86. cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用解密模式初始化 密钥
  87. byte[] decrypt = cipher.doFinal(encrypt);
  88. System.out.println("method3-解密后:" + new String(decrypt));
  89. }
  90. /**
  91. * 使用AES 算法 加密,默认模式 AES/CBC/NoPadding 参见上面对于这种mode的数据限制
  92. */
  93. static void method4(String str) throws Exception {
  94. cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC_NoPadding);
  95. //KeyGenerator 生成aes算法密钥
  96. secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
  97. System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
  98. cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用加密模式初始化 密钥
  99. byte[] encrypt = cipher.doFinal(str.getBytes(), 0, str.getBytes().length); //按单部分操作加密或解密数据,或者结束一个多部分操作。
  100. System.out.println("method4-加密:" + Arrays.toString(encrypt));
  101. cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用解密模式初始化 密钥
  102. byte[] decrypt = cipher.doFinal(encrypt);
  103. System.out.println("method4-解密后:" + new String(decrypt));
  104. }
  105. }

——————————————updated on 2017/08/29——————————————————— 

**使用32字节(256位)密钥 **

前文,method1-4,打印出来的 密钥长度都是16字节长度;都是通过KeyGenerator自动生成的。

jdk中 AES中的Cipher,对应的密钥长度为128位的,按8bits一个字节算,整好就是16个字节。

如果要指定为32字节长度(256位)密钥,如下实现:

  1. /**
  2. * 指定密钥长度为32位字节。 默认只能16位,是因为美国的出口政策限制。
  3. * @param key_32
  4. * @param str
  5. * @throws Exception
  6. */
  7. static void method5(String key_32, String str) throws Exception {
  8. System.out.println();
  9. cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
  10. secretKey = new SecretKeySpec(key_32.getBytes("utf-8"), "AES");
  11. System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
  12. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
  13. byte[] encrypt = cipher.doFinal(str.getBytes(), 0, str.getBytes().length);
  14. System.out.println("method5-加密:" + Arrays.toString(encrypt));
  15. cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
  16. byte[] decrypt = cipher.doFinal(encrypt);
  17. System.out.println("method5-解密后:" + new String(decrypt, "utf-8"));
  18. }

调用代码:

  1. String key_32 = "0123456789abcdef0123456789ABCDEF";
  2. method5(key_32, "123456781234囙为国宽");

如上,通过SecretkeySpec,可以指定32位字节密钥。

但是,运行后,发现报错了:"java.security.InvalidKeyException: Illegal key size or default parameters…"

网络搜索后发现如下解决方法:http://blog.csdn.net/l1028386804/article/details/42706039

因我的环境是jdk8,在oracle搜索到jce-jdk8的下载页面:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

最后,大功告成:

  1. 密钥的长度为:32
  2. method5-加密:[-9, -118, -4, 2, 76, 48, 60, 4, -100, 19, 24, -14, 93, -20, -19, 77, -79, 105, -78, 53, 17, 29, -65, 36, 104, -60, 83, 31, -76, -26, 54, -120]
  3. method5-解密后:123456781234囙为国宽

如果像安卓app,要在客户端实现32位字节密钥加、解密,估计还是不行的;只能考虑自己实现一套AES的算法了,同时服务端也使用相应算法

使用"AES/CBC/NoPadding"引发的问题

不考虑上面的256位密钥问题,在默认情况下,这种padding,要求 密钥、IV长度都是16字节,内容字节长度必须是16的倍数,如果不是16的倍数,则引发异常。

如果一定要使用这种padding,而无法保证内容为16字节的倍数,可以考虑自实现一套规则,补全内容长度为16*n

相关文章