HashUtil.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. * FileName:HashUtil.java
  3. * <p>
  4. * Copyright (c) 2017-2020, <a href="http://www.webcsn.com">hermit (794890569@qq.com)</a>.
  5. * <p>
  6. * Licensed under the GNU General Public License, Version 3 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. * <p>
  10. * http://www.gnu.org/licenses/gpl-3.0.html
  11. * <p>
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. */
  19. package cn.com.lzt.common.util;
  20. import org.apache.commons.lang3.Validate;
  21. import javax.crypto.Cipher;
  22. import javax.crypto.KeyGenerator;
  23. import javax.crypto.Mac;
  24. import javax.crypto.SecretKey;
  25. import javax.crypto.spec.IvParameterSpec;
  26. import javax.crypto.spec.SecretKeySpec;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.UnsupportedEncodingException;
  30. import java.nio.charset.StandardCharsets;
  31. import java.security.GeneralSecurityException;
  32. import java.security.MessageDigest;
  33. import java.security.SecureRandom;
  34. import java.util.Arrays;
  35. /**
  36. * 功能:数据加密解密工具类
  37. * 支持HMAC-SHA1消息签名 及 AES/Blowfish对称加密的工具类
  38. * 支持Hex与Base64两种编码方式
  39. * 支持MD5 SHA 不可逆加密
  40. * @author xiongliang
  41. *
  42. * mobile enterprise application platform
  43. * Version 0.1
  44. */
  45. public class HashUtil {
  46. public static final String SHA1 = "SHA-1";
  47. public static final String MD5 = "MD5";
  48. private static final String AES = "AES";
  49. private static final String AES_CBC = "AES/CBC/PKCS5Padding";
  50. private static final String HMACSHA1 = "HmacSHA1";
  51. private static final int DEFAULT_HMACSHA1_KEYSIZE = 160; //RFC2401
  52. private static final int DEFAULT_AES_KEYSIZE = 128;
  53. private static final int DEFAULT_IVSIZE = 16;
  54. private static SecureRandom random = new SecureRandom();
  55. /**
  56. * SHA加密
  57. * @param input
  58. * @return
  59. */
  60. public static String SHAHashing(String input){
  61. String output = "";
  62. MessageDigest md;
  63. try {
  64. md = MessageDigest.getInstance("SHA");
  65. byte[] original = input.getBytes("utf-8");
  66. byte[] bytes = md.digest(original);
  67. for (int i = 0; i<bytes.length; i++) {
  68. output += Integer.toHexString((bytes[i] & 0xff) + 0x100).substring(1);
  69. }
  70. }catch(Exception e){
  71. e.printStackTrace();
  72. }
  73. return output;
  74. }
  75. /**
  76. * MD5加密
  77. * @param input
  78. * @return
  79. * @throws UnsupportedEncodingException
  80. */
  81. public static String MD5Hashing(String input){
  82. String output = "";
  83. MessageDigest md;
  84. try {
  85. md = MessageDigest.getInstance("MD5");
  86. byte[] original = input.getBytes("utf-8");
  87. byte[] bytes = md.digest(original);
  88. for (int i = 0; i<bytes.length; i++) {
  89. output += Integer.toHexString((bytes[i] & 0xff) + 0x100).substring(1);
  90. }
  91. }catch(Exception e){
  92. e.printStackTrace();
  93. }
  94. return output;
  95. }
  96. /**
  97. * 加密[Blowfish对称加密解密] 密钥是配置中的site.secret
  98. * @param paramString
  99. * @return
  100. */
  101. public static String encrypt(String key, String paramString){
  102. if(ValidateUtil.isNull(paramString))return paramString;
  103. try {
  104. Cipher localCipher = Cipher.getInstance("Blowfish");
  105. localCipher.init(1, new SecretKeySpec(key.getBytes(), "Blowfish"));
  106. byte[] arrayOfByte = localCipher.doFinal(paramString.getBytes("utf-8"));
  107. return ConvertUtil.convertEncodeBase64(arrayOfByte);
  108. } catch (Exception e) {
  109. e.printStackTrace();
  110. }
  111. return paramString;
  112. }
  113. /**
  114. * 解密[Blowfish对称加密解密] 密钥是配置中的site.secret
  115. * @param paramString
  116. * @return
  117. */
  118. public static String decrypt(String key, String paramString){
  119. if(ValidateUtil.isNull(paramString))return paramString;
  120. try {
  121. Cipher localCipher = Cipher.getInstance("Blowfish");
  122. localCipher.init(2, new SecretKeySpec(key.getBytes(), "Blowfish"));
  123. byte[] arrayOfByte = localCipher.doFinal(ConvertUtil.convertDecodeBase64(paramString));
  124. return new String(arrayOfByte, StandardCharsets.UTF_8);
  125. } catch (Exception e) {
  126. e.printStackTrace();
  127. }
  128. return paramString;
  129. }
  130. //-- HMAC-SHA1 funciton --//
  131. /**
  132. * 使用HMAC-SHA1进行消息签名, 返回字节数组,长度为20字节.
  133. *
  134. * @param input 原始输入字符数组
  135. * @param key HMAC-SHA1密钥
  136. */
  137. public static byte[] hmacSha1(byte[] input, byte[] key) {
  138. try {
  139. SecretKey secretKey = new SecretKeySpec(key, HMACSHA1);
  140. Mac mac = Mac.getInstance(HMACSHA1);
  141. mac.init(secretKey);
  142. return mac.doFinal(input);
  143. } catch (GeneralSecurityException e) {
  144. throw ExcptUtil.unchecked(e);
  145. }
  146. }
  147. /**
  148. * 校验HMAC-SHA1签名是否正确.
  149. *
  150. * @param expected 已存在的签名
  151. * @param input 原始输入字符串
  152. * @param key 密钥
  153. */
  154. public static boolean isMacValid(byte[] expected, byte[] input, byte[] key) {
  155. byte[] actual = hmacSha1(input, key);
  156. return Arrays.equals(expected, actual);
  157. }
  158. /**
  159. * 生成HMAC-SHA1密钥,返回字节数组,长度为160位(20字节).
  160. * HMAC-SHA1算法对密钥无特殊要求, RFC2401建议最少长度为160位(20字节).
  161. */
  162. public static byte[] generateHmacSha1Key() {
  163. try {
  164. KeyGenerator keyGenerator = KeyGenerator.getInstance(HMACSHA1);
  165. keyGenerator.init(DEFAULT_HMACSHA1_KEYSIZE);
  166. SecretKey secretKey = keyGenerator.generateKey();
  167. return secretKey.getEncoded();
  168. } catch (GeneralSecurityException e) {
  169. throw ExcptUtil.unchecked(e);
  170. }
  171. }
  172. //-- AES funciton --//
  173. /**
  174. * 使用AES加密原始字符串.
  175. *
  176. * @param input 原始输入字符数组
  177. * @param key 符合AES要求的密钥
  178. */
  179. public static byte[] aesEncrypt(byte[] input, byte[] key) {
  180. return aes(input, key, Cipher.ENCRYPT_MODE);
  181. }
  182. /**
  183. * 使用AES加密原始字符串.
  184. *
  185. * @param input 原始输入字符数组
  186. * @param key 符合AES要求的密钥
  187. * @param iv 初始向量 通过generateIV获得
  188. */
  189. public static byte[] aesEncrypt(byte[] input, byte[] key, byte[] iv) {
  190. return aes(input, key, iv, Cipher.ENCRYPT_MODE);
  191. }
  192. /**
  193. * 使用AES解密字符串, 返回原始字符串.
  194. *
  195. * @param input Hex编码的加密字符串
  196. * @param key 符合AES要求的密钥
  197. */
  198. public static String aesDecrypt(byte[] input, byte[] key) {
  199. byte[] decryptResult = aes(input, key, Cipher.DECRYPT_MODE);
  200. return new String(decryptResult);
  201. }
  202. /**
  203. * 使用AES解密字符串, 返回原始字符串.
  204. *
  205. * @param input Hex编码的加密字符串
  206. * @param key 符合AES要求的密钥
  207. * @param iv 初始向量 通过generateIV获得
  208. */
  209. public static String aesDecrypt(byte[] input, byte[] key, byte[] iv) {
  210. byte[] decryptResult = aes(input, key, iv, Cipher.DECRYPT_MODE);
  211. return new String(decryptResult);
  212. }
  213. /**
  214. * @param input 原始字节数组
  215. * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
  216. *
  217. * @param key 符合AES要求的密钥
  218. * @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
  219. */
  220. private static byte[] aes(byte[] input, byte[] key, int mode) {
  221. try {
  222. SecretKey secretKey = new SecretKeySpec(key, AES);
  223. Cipher cipher = Cipher.getInstance(AES);
  224. cipher.init(mode, secretKey);
  225. return cipher.doFinal(input);
  226. } catch (GeneralSecurityException e) {
  227. throw ExcptUtil.unchecked(e);
  228. }
  229. }
  230. /**
  231. * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
  232. *
  233. * @param input 原始字节数组(需要加密的内容)
  234. * @param key 符合AES要求的密钥
  235. * @param iv 初始向量 通过generateIV获得
  236. * @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
  237. */
  238. private static byte[] aes(byte[] input, byte[] key, byte[] iv, int mode) {
  239. try {
  240. SecretKey secretKey = new SecretKeySpec(key, AES);
  241. IvParameterSpec ivSpec = new IvParameterSpec(iv);
  242. Cipher cipher = Cipher.getInstance(AES_CBC);
  243. cipher.init(mode, secretKey, ivSpec);
  244. return cipher.doFinal(input);
  245. } catch (GeneralSecurityException e) {
  246. throw ExcptUtil.unchecked(e);
  247. }
  248. }
  249. /**
  250. * 生成AES密钥,返回字节数组, 默认长度为128位(16字节).
  251. */
  252. public static byte[] generateAesKey() {
  253. return generateAesKey(DEFAULT_AES_KEYSIZE);
  254. }
  255. /**
  256. * 生成AES密钥,可选长度为128,192,256位.
  257. */
  258. public static byte[] generateAesKey(int keysize) {
  259. try {
  260. KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
  261. keyGenerator.init(keysize);
  262. SecretKey secretKey = keyGenerator.generateKey();
  263. return secretKey.getEncoded();
  264. } catch (GeneralSecurityException e) {
  265. throw ExcptUtil.unchecked(e);
  266. }
  267. }
  268. /**
  269. * 生成随机向量,默认大小为cipher.getBlockSize(), 16字节.
  270. */
  271. public static byte[] generateIV() {
  272. byte[] bytes = new byte[DEFAULT_IVSIZE];
  273. random.nextBytes(bytes);
  274. return bytes;
  275. }
  276. /**
  277. * 对输入字符串进行sha1散列.
  278. */
  279. public static byte[] sha1(byte[] input) {
  280. return digest(input, SHA1, null, 1);
  281. }
  282. /**
  283. * SHA加盐算法
  284. * @param input
  285. * @param salt
  286. * @return
  287. */
  288. public static byte[] sha1(byte[] input, byte[] salt) {
  289. return digest(input, SHA1, salt, 1);
  290. }
  291. /**
  292. * SHA加盐多次循环算法
  293. * @param input
  294. * @param salt
  295. * @param iterations
  296. * @return
  297. */
  298. public static byte[] sha1(byte[] input, byte[] salt, int iterations) {
  299. return digest(input, SHA1, salt, iterations);
  300. }
  301. /**
  302. * 对字符串进行散列, 支持md5与sha1算法.
  303. * @param input
  304. * @param algorithm
  305. * @param salt
  306. * @param iterations
  307. * @return
  308. */
  309. private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
  310. try {
  311. MessageDigest digest = MessageDigest.getInstance(algorithm);
  312. if (salt != null) {
  313. digest.update(salt);
  314. }
  315. byte[] result = digest.digest(input);
  316. for (int i = 1; i < iterations; i++) {
  317. digest.reset();
  318. result = digest.digest(result);
  319. }
  320. return result;
  321. } catch (GeneralSecurityException e) {
  322. throw ExcptUtil.unchecked(e);
  323. }
  324. }
  325. /**
  326. * 生成随机的Byte[]作为salt.
  327. *
  328. * @param numBytes byte数组的大小
  329. */
  330. public static byte[] generateSalt(int numBytes) {
  331. Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
  332. byte[] bytes = new byte[numBytes];
  333. random.nextBytes(bytes);
  334. return bytes;
  335. }
  336. /**
  337. * 对文件进行消息摘要签名
  338. * @param input InputStream输入流
  339. * @param algorithm 加密算法 HashUtil.SHA1 HashUtil.MD5
  340. * @return
  341. * @throws IOException
  342. */
  343. public static byte[] digest(InputStream input, String algorithm) throws IOException {
  344. try {
  345. MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
  346. int bufferLength = 8 * 1024;
  347. byte[] buffer = new byte[bufferLength];
  348. int read = input.read(buffer, 0, bufferLength);
  349. while (read > -1) {
  350. messageDigest.update(buffer, 0, read);
  351. read = input.read(buffer, 0, bufferLength);
  352. }
  353. return messageDigest.digest();
  354. } catch (GeneralSecurityException e) {
  355. throw ExcptUtil.unchecked(e);
  356. }
  357. }
  358. }