在项目中运用到了Java的AES加密,本地Windows调试一切正常,部署到Linux服务器后一直报空指针异常。
经过一番调试,找到真正原因:javax.crypto.BadPaddingException: Given final block not properly padded
1 package com.daredo.utils; 2 3 import javax.crypto.Cipher; 4 import javax.crypto.KeyGenerator; 5 import javax.crypto.SecretKey; 6 import javax.crypto.spec.SecretKeySpec; 7 import java.security.SecureRandom; 8 9 /** 10 * Created by IntelliJ IDEA 11 * Author: d-arlin@qq.com 12 * Date: 2018/3/14 13 * Time: 15:38 14 */ 15 public class SecurityUtils { 16 17 /** 18 * 编码格式 19 */ 20 private static String ENCODING = "UTF-8"; 21 /** 22 * 加密算法 23 */ 24 public static final String KEY_ALGORITHM = "AES"; 25 26 /** 27 * 加密 28 * 29 * @param content 待加密内容 30 * @param key 加密的密钥 31 * @return 32 */ 33 public static String encrypt(String content, String key) { 34 try { 35 KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM); 36 kgen.init(128, new SecureRandom(key.getBytes(ENCODING))); 37 SecretKey secretKey = kgen.generateKey(); 38 byte[] enCodeFormat = secretKey.getEncoded(); 39 SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM); 40 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); 41 byte[] byteContent = content.getBytes(ENCODING); 42 cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 43 byte[] byteRresult = cipher.doFinal(byteContent); 44 StringBuffer sb = new StringBuffer(); 45 for (int i = 0; i < byteRresult.length; i++) { 46 String hex = Integer.toHexString(byteRresult[i] & 0xFF); 47 if (hex.length() == 1) hex = '0' + hex; 48 sb.append(hex.toUpperCase()); 49 } 50 return sb.toString(); 51 } catch (Exception e) { 52 e.toString(); 53 } 54 return null; 55 } 56 57 /** 58 * 解密 59 * 60 * @param content 待解密内容 61 * @param key 解密的密钥 62 * @return 63 */ 64 public static String decrypt(String content, String key) { 65 if (content.length() < 1) return null; 66 byte[] byteRresult = new byte[content.length() / 2]; 67 for (int i = 0; i < content.length() / 2; i++) { 68 int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16); 69 int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16); 70 byteRresult[i] = (byte) (high * 16 + low); 71 } 72 try { 73 KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM); 74 kgen.init(128, new SecureRandom(key.getBytes(ENCODING))); 75 SecretKey secretKey = kgen.generateKey(); 76 byte[] enCodeFormat = secretKey.getEncoded(); 77 SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM); 78 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); 79 cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); 80 byte[] result = cipher.doFinal(byteRresult); 81 return new String(result, ENCODING); 82 } catch (Exception e) { 83 e.toString(); 84 } 85 return null; 86 } 87 88 }
那么为什么在Windows正常,在Linux就出现异常呢?
原因分析
SecureRandom 实现完全随操作系统本身的內部状态,除非调用方在调用 getInstance 方法之后又调用了 setSeed 方法;
该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。
解决方法
1 package com.daredo.utils; 2 3 import javax.crypto.Cipher; 4 import javax.crypto.KeyGenerator; 5 import javax.crypto.SecretKey; 6 import javax.crypto.spec.SecretKeySpec; 7 import java.security.SecureRandom; 8 9 /** 10 * Created by IntelliJ IDEA 11 * Author: d-arlin@qq.com 12 * Date: 2018/3/14 13 * Time: 15:38 14 */ 15 public class SecurityUtils { 16 17 /** 18 * 编码格式 19 */ 20 private static final String ENCODING = "UTF-8"; 21 /** 22 * 加密算法 23 */ 24 public static final String KEY_ALGORITHM = "AES"; 25 /** 26 * 签名算法 27 */ 28 public static final String SIGN_ALGORITHMS = "SHA1PRNG"; 29 30 /** 31 * 加密 32 * 33 * @param content 待加密内容 34 * @param key 加密的密钥 35 * @return 36 */ 37 public static String encrypt(String content, String key) { 38 try { 39 KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM); 40 SecureRandom random = SecureRandom.getInstance(SIGN_ALGORITHMS); 41 random.setSeed(key.getBytes(ENCODING)); 42 kgen.init(128, random); 43 SecretKey secretKey = kgen.generateKey(); 44 byte[] enCodeFormat = secretKey.getEncoded(); 45 SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM); 46 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); 47 byte[] byteContent = content.getBytes(ENCODING); 48 cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 49 byte[] byteRresult = cipher.doFinal(byteContent); 50 StringBuffer sb = new StringBuffer(); 51 for (int i = 0; i < byteRresult.length; i++) { 52 String hex = Integer.toHexString(byteRresult[i] & 0xFF); 53 if (hex.length() == 1) hex = '0' + hex; 54 sb.append(hex.toUpperCase()); 55 } 56 return sb.toString(); 57 } catch (Exception e) { 58 e.toString(); 59 } 60 return null; 61 } 62 63 /** 64 * 解密 65 * 66 * @param content 待解密内容 67 * @param key 解密的密钥 68 * @return 69 */ 70 public static String decrypt(String content, String key) { 71 if (content.length() < 1) return null; 72 byte[] byteRresult = new byte[content.length() / 2]; 73 for (int i = 0; i < content.length() / 2; i++) { 74 int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16); 75 int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16); 76 byteRresult[i] = (byte) (high * 16 + low); 77 } 78 try { 79 KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM); 80 SecureRandom random = SecureRandom.getInstance(SIGN_ALGORITHMS); 81 random.setSeed(key.getBytes(ENCODING)); 82 kgen.init(128, random); 83 SecretKey secretKey = kgen.generateKey(); 84 byte[] enCodeFormat = secretKey.getEncoded(); 85 SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, KEY_ALGORITHM); 86 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); 87 cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); 88 byte[] result = cipher.doFinal(byteRresult); 89 return new String(result, ENCODING); 90 } catch (Exception e) { 91 e.toString(); 92 } 93 return null; 94 } 95 96 }
改动的地方如代码中已标红。