公司最近做agent項目,需要對一些遠程重要的請求參數進行加密。加密之前選型,選擇了AES,而DES算法加密,容易被破解。網上有很多關於加密的算法的Demo案列,我發現這些Demo在Window平台運行正常,然后再MAC下就一直報錯,現在選擇網上常見的AES加密算法如下:
一·AES加密
1 /** 2 * AES加密字符串 3 * 4 * @param content 5 * 需要被加密的字符串 6 * @param password 7 * 加密需要的密碼 8 * @return 密文 9 */ 10 public static byte[] encrypt(String content, String password) { 11 try { 12 KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創建AES的Key生產者 13 14 kgen.init(128, new SecureRandom(password.getBytes()));// 利用用戶密碼作為隨機數初始化出 15 // 128位的key生產者 16 //加密沒關系,SecureRandom是生成安全隨機數序列,password.getBytes()是種子,只要種子相同,序列就一樣,所以解密只要有password就行 17 18 SecretKey secretKey = kgen.generateKey();// 根據用戶密碼,生成一個密鑰 19 20 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰,如果此密鑰不支持編碼,則返回 21 // null。 22 23 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉換為AES專用密鑰 24 25 Cipher cipher = Cipher.getInstance("AES");// 創建密碼器 26 27 byte[] byteContent = content.getBytes("utf-8"); 28 29 cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化為加密模式的密碼器 30 31 byte[] result = cipher.doFinal(byteContent);// 加密 32 33 return result; 34 35 } catch (NoSuchPaddingException e) { 36 e.printStackTrace(); 37 } catch (NoSuchAlgorithmException e) { 38 e.printStackTrace(); 39 } catch (UnsupportedEncodingException e) { 40 e.printStackTrace(); 41 } catch (InvalidKeyException e) { 42 e.printStackTrace(); 43 } catch (IllegalBlockSizeException e) { 44 e.printStackTrace(); 45 } catch (BadPaddingException e) { 46 e.printStackTrace(); 47 } 48 return null; 49 }
二.AES解密
1 /** 2 * 解密AES加密過的字符串 3 * 4 * @param content 5 * AES加密過過的內容 6 * @param password 7 * 加密時的密碼 8 * @return 明文 9 */ 10 public static byte[] decrypt(byte[] content, String password) { 11 try { 12 KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創建AES的Key生產者 13 kgen.init(128, new SecureRandom(password.getBytes())); 14 SecretKey secretKey = kgen.generateKey();// 根據用戶密碼,生成一個密鑰 15 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰 16 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉換為AES專用密鑰 17 Cipher cipher = Cipher.getInstance("AES");// 創建密碼器 18 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化為解密模式的密碼器 19 byte[] result = cipher.doFinal(content); 20 return result; // 明文 21 22 } catch (NoSuchAlgorithmException e) { 23 e.printStackTrace(); 24 } catch (NoSuchPaddingException e) { 25 e.printStackTrace(); 26 } catch (InvalidKeyException e) { 27 e.printStackTrace(); 28 } catch (IllegalBlockSizeException e) { 29 e.printStackTrace(); 30 } catch (BadPaddingException e) { 31 e.printStackTrace(); 32 } 33 return null; 34 }
三.測試
1 public static void main(String[] args) { 2 String content = "對面的樓上的男生,你們有女朋友嗎?"; 3 String password = "123456789"; 4 System.out.println("加密之前:" + content); 5 6 // 加密 7 byte[] encrypt = AesTest.encrypt(content, password); 8 System.out.println("加密后的內容:" + new String(encrypt)); 9 10 // 解密 11 byte[] decrypt = AesTest.decrypt(encrypt, password); 12 System.out.println("解密后的內容:" + new String(decrypt)); 13 }
以上代碼在Window平台上運行正常,但是在MAC下運行加密正常,解密一直以下報 Given final block not properly padded的錯!!!
網上查了很多資料,最后分析了下錯誤原因:
SecureRandom 實現完全隨操作系統本身的內部狀態,除非調用方在調用 getInstance 方法之后又調用了 setSeed 方法;該實現在 windows 上每次生成的 key 都相同,但是在 MAC 或部分 linux 系統上則不同。兩次加密,解密產生的密鑰都不相同,所以解密就報錯。為了規避這個錯誤,采用了另一種AES加密的方法,而且需要添加2個依賴jar包。兩個依賴的maven坐標是:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>org.apache.directory.studio</groupId> <artifactId>org.apache.commons.codec</artifactId> <version>1.8</version> </dependency>
下面是改進后的代碼:
1 package com.corearchi.utils; 2 3 import javax.crypto.Cipher; 4 import javax.crypto.KeyGenerator; 5 import javax.crypto.spec.SecretKeySpec; 6 import org.apache.commons.codec.binary.Base64; 7 import org.apache.commons.lang3.StringUtils; 8 9 import sun.misc.BASE64Decoder; 10 11 /** 12 * AES算法進行加密 13 * 14 * @author CodeGeek 15 * @create 2018-01-23 上午11:00 16 **/ 17 public class EncryptUtils { 18 19 /** 20 * 密鑰 21 */ 22 private static final String KEY = "1234567887654321";// AES加密要求key必須要128個比特位(這里需要長度為16,否則會報錯) 23 24 /** 25 * 算法 26 */ 27 private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding"; 28 29 public static void main(String[] args) throws Exception { 30 String content = "url:findNames.action"; 31 System.out.println("加密前:" + content); 32 33 System.out.println("加密密鑰和解密密鑰:" + KEY); 34 35 String encrypt = aesEncrypt(content, KEY); 36 System.out.println("加密后:" + encrypt); 37 38 String decrypt = aesDecrypt(encrypt, KEY); 39 40 System.out.println("解密后:" + decrypt); 41 } 42 43 /** 44 * base 64 encode 45 * @param bytes 待編碼的byte[] 46 * @return 編碼后的base 64 code 47 */ 48 private static String base64Encode(byte[] bytes){ 49 return Base64.encodeBase64String(bytes); 50 } 51 52 /** 53 * base 64 decode 54 * @param base64Code 待解碼的base 64 code 55 * @return 解碼后的byte[] 56 * @throws Exception 拋出異常 57 */ 58 private static byte[] base64Decode(String base64Code) throws Exception{ 59 return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code); 60 } 61 62 63 /** 64 * AES加密 65 * @param content 待加密的內容 66 * @param encryptKey 加密密鑰 67 * @return 加密后的byte[] 68 */ 69 private static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { 70 KeyGenerator kgen = KeyGenerator.getInstance("AES"); 71 kgen.init(128); 72 Cipher cipher = Cipher.getInstance(ALGORITHMSTR); 73 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); 74 75 return cipher.doFinal(content.getBytes("utf-8")); 76 } 77 78 79 /** 80 * AES加密為base 64 code 81 * 82 * @param content 待加密的內容 83 * @param encryptKey 加密密鑰 84 * @return 加密后的base 64 code 85 */ 86 private static String aesEncrypt(String content, String encryptKey) throws Exception { 87 return base64Encode(aesEncryptToBytes(content, encryptKey)); 88 } 89 90 /** 91 * AES解密 92 * 93 * @param encryptBytes 待解密的byte[] 94 * @param decryptKey 解密密鑰 95 * @return 解密后的String 96 */ 97 private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { 98 KeyGenerator kgen = KeyGenerator.getInstance("AES"); 99 kgen.init(128); 100 101 Cipher cipher = Cipher.getInstance(ALGORITHMSTR); 102 cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); 103 byte[] decryptBytes = cipher.doFinal(encryptBytes); 104 105 return new String(decryptBytes); 106 } 107 108 109 /** 110 * 將base 64 code AES解密 111 * 112 * @param encryptStr 待解密的base 64 code 113 * @param decryptKey 解密密鑰 114 * @return 解密后的string 115 */ 116 private static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { 117 return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); 118 } 119 120 }
這個時候在MAC下就可以正常運行了,在Window以及Linux上都可以正常運行。