| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /*
- * FileName:HashUtil.java
- * <p>
- * Copyright (c) 2017-2020, <a href="http://www.webcsn.com">hermit (794890569@qq.com)</a>.
- * <p>
- * Licensed under the GNU General Public License, Version 3 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * <p>
- * http://www.gnu.org/licenses/gpl-3.0.html
- * <p>
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- package cn.com.lzt.common.util;
- import org.apache.commons.lang3.Validate;
- import javax.crypto.Cipher;
- import javax.crypto.KeyGenerator;
- import javax.crypto.Mac;
- import javax.crypto.SecretKey;
- import javax.crypto.spec.IvParameterSpec;
- import javax.crypto.spec.SecretKeySpec;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.nio.charset.StandardCharsets;
- import java.security.GeneralSecurityException;
- import java.security.MessageDigest;
- import java.security.SecureRandom;
- import java.util.Arrays;
- /**
- * 功能:数据加密解密工具类
- * 支持HMAC-SHA1消息签名 及 AES/Blowfish对称加密的工具类
- * 支持Hex与Base64两种编码方式
- * 支持MD5 SHA 不可逆加密
- * @author xiongliang
- *
- * mobile enterprise application platform
- * Version 0.1
- */
- public class HashUtil {
-
- public static final String SHA1 = "SHA-1";
- public static final String MD5 = "MD5";
-
- private static final String AES = "AES";
- private static final String AES_CBC = "AES/CBC/PKCS5Padding";
- private static final String HMACSHA1 = "HmacSHA1";
- private static final int DEFAULT_HMACSHA1_KEYSIZE = 160; //RFC2401
- private static final int DEFAULT_AES_KEYSIZE = 128;
- private static final int DEFAULT_IVSIZE = 16;
- private static SecureRandom random = new SecureRandom();
- /**
- * SHA加密
- * @param input
- * @return
- */
- public static String SHAHashing(String input){
- String output = "";
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA");
- byte[] original = input.getBytes("utf-8");
- byte[] bytes = md.digest(original);
- for (int i = 0; i<bytes.length; i++) {
- output += Integer.toHexString((bytes[i] & 0xff) + 0x100).substring(1);
- }
- }catch(Exception e){
- e.printStackTrace();
- }
- return output;
- }
- /**
- * MD5加密
- * @param input
- * @return
- * @throws UnsupportedEncodingException
- */
- public static String MD5Hashing(String input){
- String output = "";
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("MD5");
- byte[] original = input.getBytes("utf-8");
- byte[] bytes = md.digest(original);
- for (int i = 0; i<bytes.length; i++) {
- output += Integer.toHexString((bytes[i] & 0xff) + 0x100).substring(1);
- }
- }catch(Exception e){
- e.printStackTrace();
- }
- return output;
- }
- /**
- * 加密[Blowfish对称加密解密] 密钥是配置中的site.secret
- * @param paramString
- * @return
- */
- public static String encrypt(String key, String paramString){
- if(ValidateUtil.isNull(paramString))return paramString;
- try {
- Cipher localCipher = Cipher.getInstance("Blowfish");
- localCipher.init(1, new SecretKeySpec(key.getBytes(), "Blowfish"));
- byte[] arrayOfByte = localCipher.doFinal(paramString.getBytes("utf-8"));
- return ConvertUtil.convertEncodeBase64(arrayOfByte);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return paramString;
- }
- /**
- * 解密[Blowfish对称加密解密] 密钥是配置中的site.secret
- * @param paramString
- * @return
- */
- public static String decrypt(String key, String paramString){
- if(ValidateUtil.isNull(paramString))return paramString;
- try {
- Cipher localCipher = Cipher.getInstance("Blowfish");
- localCipher.init(2, new SecretKeySpec(key.getBytes(), "Blowfish"));
- byte[] arrayOfByte = localCipher.doFinal(ConvertUtil.convertDecodeBase64(paramString));
- return new String(arrayOfByte, StandardCharsets.UTF_8);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return paramString;
- }
- //-- HMAC-SHA1 funciton --//
- /**
- * 使用HMAC-SHA1进行消息签名, 返回字节数组,长度为20字节.
- *
- * @param input 原始输入字符数组
- * @param key HMAC-SHA1密钥
- */
- public static byte[] hmacSha1(byte[] input, byte[] key) {
- try {
- SecretKey secretKey = new SecretKeySpec(key, HMACSHA1);
- Mac mac = Mac.getInstance(HMACSHA1);
- mac.init(secretKey);
- return mac.doFinal(input);
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- /**
- * 校验HMAC-SHA1签名是否正确.
- *
- * @param expected 已存在的签名
- * @param input 原始输入字符串
- * @param key 密钥
- */
- public static boolean isMacValid(byte[] expected, byte[] input, byte[] key) {
- byte[] actual = hmacSha1(input, key);
- return Arrays.equals(expected, actual);
- }
- /**
- * 生成HMAC-SHA1密钥,返回字节数组,长度为160位(20字节).
- * HMAC-SHA1算法对密钥无特殊要求, RFC2401建议最少长度为160位(20字节).
- */
- public static byte[] generateHmacSha1Key() {
- try {
- KeyGenerator keyGenerator = KeyGenerator.getInstance(HMACSHA1);
- keyGenerator.init(DEFAULT_HMACSHA1_KEYSIZE);
- SecretKey secretKey = keyGenerator.generateKey();
- return secretKey.getEncoded();
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- //-- AES funciton --//
- /**
- * 使用AES加密原始字符串.
- *
- * @param input 原始输入字符数组
- * @param key 符合AES要求的密钥
- */
- public static byte[] aesEncrypt(byte[] input, byte[] key) {
- return aes(input, key, Cipher.ENCRYPT_MODE);
- }
- /**
- * 使用AES加密原始字符串.
- *
- * @param input 原始输入字符数组
- * @param key 符合AES要求的密钥
- * @param iv 初始向量 通过generateIV获得
- */
- public static byte[] aesEncrypt(byte[] input, byte[] key, byte[] iv) {
- return aes(input, key, iv, Cipher.ENCRYPT_MODE);
- }
- /**
- * 使用AES解密字符串, 返回原始字符串.
- *
- * @param input Hex编码的加密字符串
- * @param key 符合AES要求的密钥
- */
- public static String aesDecrypt(byte[] input, byte[] key) {
- byte[] decryptResult = aes(input, key, Cipher.DECRYPT_MODE);
- return new String(decryptResult);
- }
- /**
- * 使用AES解密字符串, 返回原始字符串.
- *
- * @param input Hex编码的加密字符串
- * @param key 符合AES要求的密钥
- * @param iv 初始向量 通过generateIV获得
- */
- public static String aesDecrypt(byte[] input, byte[] key, byte[] iv) {
- byte[] decryptResult = aes(input, key, iv, Cipher.DECRYPT_MODE);
- return new String(decryptResult);
- }
- /**
- * @param input 原始字节数组
- * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
- *
- * @param key 符合AES要求的密钥
- * @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
- */
- private static byte[] aes(byte[] input, byte[] key, int mode) {
- try {
- SecretKey secretKey = new SecretKeySpec(key, AES);
- Cipher cipher = Cipher.getInstance(AES);
- cipher.init(mode, secretKey);
- return cipher.doFinal(input);
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- /**
- * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
- *
- * @param input 原始字节数组(需要加密的内容)
- * @param key 符合AES要求的密钥
- * @param iv 初始向量 通过generateIV获得
- * @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
- */
- private static byte[] aes(byte[] input, byte[] key, byte[] iv, int mode) {
- try {
- SecretKey secretKey = new SecretKeySpec(key, AES);
- IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance(AES_CBC);
- cipher.init(mode, secretKey, ivSpec);
- return cipher.doFinal(input);
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- /**
- * 生成AES密钥,返回字节数组, 默认长度为128位(16字节).
- */
- public static byte[] generateAesKey() {
- return generateAesKey(DEFAULT_AES_KEYSIZE);
- }
- /**
- * 生成AES密钥,可选长度为128,192,256位.
- */
- public static byte[] generateAesKey(int keysize) {
- try {
- KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
- keyGenerator.init(keysize);
- SecretKey secretKey = keyGenerator.generateKey();
- return secretKey.getEncoded();
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- /**
- * 生成随机向量,默认大小为cipher.getBlockSize(), 16字节.
- */
- public static byte[] generateIV() {
- byte[] bytes = new byte[DEFAULT_IVSIZE];
- random.nextBytes(bytes);
- return bytes;
- }
-
- /**
- * 对输入字符串进行sha1散列.
- */
- public static byte[] sha1(byte[] input) {
- return digest(input, SHA1, null, 1);
- }
- /**
- * SHA加盐算法
- * @param input
- * @param salt
- * @return
- */
- public static byte[] sha1(byte[] input, byte[] salt) {
- return digest(input, SHA1, salt, 1);
- }
- /**
- * SHA加盐多次循环算法
- * @param input
- * @param salt
- * @param iterations
- * @return
- */
- public static byte[] sha1(byte[] input, byte[] salt, int iterations) {
- return digest(input, SHA1, salt, iterations);
- }
- /**
- * 对字符串进行散列, 支持md5与sha1算法.
- * @param input
- * @param algorithm
- * @param salt
- * @param iterations
- * @return
- */
- private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
- try {
- MessageDigest digest = MessageDigest.getInstance(algorithm);
- if (salt != null) {
- digest.update(salt);
- }
- byte[] result = digest.digest(input);
- for (int i = 1; i < iterations; i++) {
- digest.reset();
- result = digest.digest(result);
- }
- return result;
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- /**
- * 生成随机的Byte[]作为salt.
- *
- * @param numBytes byte数组的大小
- */
- public static byte[] generateSalt(int numBytes) {
- Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
- byte[] bytes = new byte[numBytes];
- random.nextBytes(bytes);
- return bytes;
- }
- /**
- * 对文件进行消息摘要签名
- * @param input InputStream输入流
- * @param algorithm 加密算法 HashUtil.SHA1 HashUtil.MD5
- * @return
- * @throws IOException
- */
- public static byte[] digest(InputStream input, String algorithm) throws IOException {
- try {
- MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
- int bufferLength = 8 * 1024;
- byte[] buffer = new byte[bufferLength];
- int read = input.read(buffer, 0, bufferLength);
- while (read > -1) {
- messageDigest.update(buffer, 0, read);
- read = input.read(buffer, 0, bufferLength);
- }
- return messageDigest.digest();
- } catch (GeneralSecurityException e) {
- throw ExcptUtil.unchecked(e);
- }
- }
- }
|