zoukankan      html  css  js  c++  java
  • JAVA实现AES加密

    1. 因子

           上次介绍了《JAVA实现DES加密》,中间提到近些年DES使用越来越少,原因就在于其使用56位密钥,比较容易被破解,近些年来逐渐被AES替代,AES已经变成目前对称加密中最流行算法之一;AES可以使用128、192、和256位密钥,并且用128位分组加密和解密数据。本文就简单介绍如何通过JAVA实现AES加密。

    2. JAVA实现

    闲话少许,掠过AES加密原理及算法,关于这些直接搜索专业网站吧,我们直接看JAVA的具体实现。

    2.1 加密

    代码有详细解释,不多废话。
    1. /** 
    2.  * 加密 
    3.  *  
    4.  * @param content 需要加密的内容 
    5.  * @param password  加密密码 
    6.  * @return 
    7.  */  
    8. public static byte[] encrypt(String content, String password) {  
    9.         try {             
    10.                 KeyGenerator kgen = KeyGenerator.getInstance("AES");  
    11.                 kgen.init(128, new SecureRandom(password.getBytes()));  
    12.                 SecretKey secretKey = kgen.generateKey();  
    13.                 byte[] enCodeFormat = secretKey.getEncoded();  
    14.                 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");  
    15.                 Cipher cipher = Cipher.getInstance("AES");// 创建密码器   
    16.                 byte[] byteContent = content.getBytes("utf-8");  
    17.                 cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化   
    18.                 byte[] result = cipher.doFinal(byteContent);  
    19.                 return result; // 加密   
    20.         } catch (NoSuchAlgorithmException e) {  
    21.                 e.printStackTrace();  
    22.         } catch (NoSuchPaddingException e) {  
    23.                 e.printStackTrace();  
    24.         } catch (InvalidKeyException e) {  
    25.                 e.printStackTrace();  
    26.         } catch (UnsupportedEncodingException e) {  
    27.                 e.printStackTrace();  
    28.         } catch (IllegalBlockSizeException e) {  
    29.                 e.printStackTrace();  
    30.         } catch (BadPaddingException e) {  
    31.                 e.printStackTrace();  
    32.         }  
    33.         return null;  
    34. }  

    2.2 解密

    代码有详细注释,不多废话
    注意:解密的时候要传入byte数组
    1. /**解密 
    2.  * @param content  待解密内容 
    3.  * @param password 解密密钥 
    4.  * @return 
    5.  */  
    6. public static byte[] decrypt(byte[] content, String password) {  
    7.         try {  
    8.                  KeyGenerator kgen = KeyGenerator.getInstance("AES");  
    9.                  kgen.init(128, new SecureRandom(password.getBytes()));  
    10.                  SecretKey secretKey = kgen.generateKey();  
    11.                  byte[] enCodeFormat = secretKey.getEncoded();  
    12.                  SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");              
    13.                  Cipher cipher = Cipher.getInstance("AES");// 创建密码器   
    14.                 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化   
    15.                 byte[] result = cipher.doFinal(content);  
    16.                 return result; // 加密   
    17.         } catch (NoSuchAlgorithmException e) {  
    18.                 e.printStackTrace();  
    19.         } catch (NoSuchPaddingException e) {  
    20.                 e.printStackTrace();  
    21.         } catch (InvalidKeyException e) {  
    22.                 e.printStackTrace();  
    23.         } catch (IllegalBlockSizeException e) {  
    24.                 e.printStackTrace();  
    25.         } catch (BadPaddingException e) {  
    26.                 e.printStackTrace();  
    27.         }  
    28.         return null;  
    29. }  

    2.3 测试代码

    1. String content = "test";  
    2. String password = "12345678";  
    3. //加密   
    4. System.out.println("加密前:" + content);  
    5. byte[] encryptResult = encrypt(content, password);  
    6. //解密   
    7. byte[] decryptResult = decrypt(encryptResult,password);  
    8. System.out.println("解密后:" + new String(decryptResult));  
    输出结果如下:
    加密前:test
    解密后:test

    2.4 容易出错的地方

    但是如果我们将测试代码修改一下,如下:
    1. String content = "test";  
    2. String password = "12345678";  
    3. //加密   
    4. System.out.println("加密前:" + content);  
    5. byte[] encryptResult = encrypt(content, password);  
    6. try {  
    7.         String encryptResultStr = new String(encryptResult,"utf-8");  
    8.         //解密   
    9.         byte[] decryptResult = decrypt(encryptResultStr.getBytes("utf-8"),password);  
    10.         System.out.println("解密后:" + new String(decryptResult));  
    11. catch (UnsupportedEncodingException e) {  
    12.         e.printStackTrace();  
    13. }  
    则,系统会报出如下异常:
    javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
            at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
            at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
            at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
            at javax.crypto.Cipher.doFinal(DashoA13*..)
    这主要是因为加密后的byte数组是不能强制转换成字符串的,换言之:字符串和byte数组在这种情况下不是互逆的;要避免这种情况,我们需要做一些修订,可以考虑将二进制数据转换成十六进制表示,主要有如下两个方法:

    2.4.1将二进制转换成16进制

    1. /**将二进制转换成16进制 
    2.  * @param buf 
    3.  * @return 
    4.  */  
    5. public static String parseByte2HexStr(byte buf[]) {  
    6.         StringBuffer sb = new StringBuffer();  
    7.         for (int i = 0; i < buf.length; i++) {  
    8.                 String hex = Integer.toHexString(buf[i] & 0xFF);  
    9.                 if (hex.length() == 1) {  
    10.                         hex = '0' + hex;  
    11.                 }  
    12.                 sb.append(hex.toUpperCase());  
    13.         }  
    14.         return sb.toString();  
    15. }  

    2.4.2 将16进制转换为二进制

    1. /**将16进制转换为二进制 
    2.  * @param hexStr 
    3.  * @return 
    4.  */  
    5. public static byte[] parseHexStr2Byte(String hexStr) {  
    6.         if (hexStr.length() < 1)  
    7.                 return null;  
    8.         byte[] result = new byte[hexStr.length()/2];  
    9.         for (int i = 0;i< hexStr.length()/2; i++) {  
    10.                 int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);  
    11.                 int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);  
    12.                 result[i] = (byte) (high * 16 + low);  
    13.         }  
    14.         return result;  
    15. }  
    然后,我们再修订以上测试代码,如下:
    1. String content = "test";  
    2. String password = "12345678";  
    3. //加密   
    4. System.out.println("加密前:" + content);  
    5. byte[] encryptResult = encrypt(content, password);  
    6. String encryptResultStr = parseByte2HexStr(encryptResult);  
    7. System.out.println("加密后:" + encryptResultStr);  
    8. //解密   
    9. byte[] decryptFrom = parseHexStr2Byte(encryptResultStr);  
    10. byte[] decryptResult = decrypt(decryptFrom,password);  
    11. System.out.println("解密后:" + new String(decryptResult));  
    测试结果如下:
    加密前:test
    加密后:73C58BAFE578C59366D8C995CD0B9D6D
    解密后:test
     

    2.5 另外一种加密方式

    还有一种加密方式,大家可以参考如下:
    1. /** 
    2.       * 加密 
    3.       * 
    4.       * @param content 需要加密的内容 
    5.       * @param password  加密密码 
    6.       * @return 
    7.       */  
    8.      public static byte[] encrypt2(String content, String password) {  
    9.              try {  
    10.                      SecretKeySpec key = new SecretKeySpec(password.getBytes(), "AES");  
    11.                      Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");  
    12.                      byte[] byteContent = content.getBytes("utf-8");  
    13.                      cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化   
    14.                      byte[] result = cipher.doFinal(byteContent);  
    15.                      return result; // 加密   
    16.              } catch (NoSuchAlgorithmException e) {  
    17.                      e.printStackTrace();  
    18.              } catch (NoSuchPaddingException e) {  
    19.                      e.printStackTrace();  
    20.              } catch (InvalidKeyException e) {  
    21.                      e.printStackTrace();  
    22.              } catch (UnsupportedEncodingException e) {  
    23.                      e.printStackTrace();  
    24.              } catch (IllegalBlockSizeException e) {  
    25.                      e.printStackTrace();  
    26.              } catch (BadPaddingException e) {  
    27.                      e.printStackTrace();  
    28.              }  
    29.              return null;  
    30.      }  
    这种加密方式有两种限制
    • 密钥必须是16位的
    • 待加密内容的长度必须是16的倍数,如果不是16的倍数,就会出如下异常:
    javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
            at com.sun.crypto.provider.SunJCE_f.a(DashoA13*..)
            at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
            at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
            at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
            at javax.crypto.Cipher.doFinal(DashoA13*..)
    要解决如上异常,可以通过补全传入加密内容等方式进行避免。
  • 相关阅读:
    Linux系统工程师必学的系统管理命令(1)
    学习中LINUX中常见问题(精华)
    "Visual Studio .NET已检测到指定的Web服务器运行的不是ASP.NET 1.1 版..."的解决办法
    今天,我决定离开
    Web Standards for Business 恐怕我没有时间翻译完了,有兴趣的可以继续
    了解到的关于深圳的一些事情
    谢谢大家,在深圳找到公司实习,发贴庆祝
    微软状告Google挖墙角,呵呵,微软坐不住了
    每个人都是生活的导演
    闷啊,一个人的孤单,一个人的所谓的项目
  • 原文地址:https://www.cnblogs.com/kenshinobiy/p/4424182.html
Copyright © 2011-2022 走看看