package cn.iocoder.yudao.module.system.util;

import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;

/**
 * @author Administrator
 */
public class SignUtil {
    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    private static final String KEY_MAC_SHA1 = "HmacSHA1";
    private static final String CHARSET_UTF8 = "UTF-8";

    public static void main(String[] args) throws Exception {
        String hmac = genHMAC("CWCATboBm3xGSsDOxc5j", "m^I311Mc@q1yfofEqzsZLS42t8989TB8w");
        System.out.println(hmac);
    }

    /**
     * 使用 HmacSha1 加密
     *
     * @param encryptText
     * @param encryptKey
     * @return
     * @throws Exception
     */
    public static String hmacSha1Encrypt(String encryptText, String encryptKey) throws Exception {
        byte[] text = encryptText.getBytes(CHARSET_UTF8);
        byte[] keyData = encryptKey.getBytes(CHARSET_UTF8);
        SecretKeySpec secretKey = new SecretKeySpec(keyData, KEY_MAC_SHA1);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        String byte2hex = byte2hex(mac.doFinal(text));
        byte[] bytes = Base64.decodeBase64(byte2hex);
        return new String(Base64.encodeBase64(bytes));
    }

    /**
     * 二行制转字符串
     *
     * @param b
     * @return
     */
    public static String byte2hex(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1) {
                hs.append('0');
                hs.append(stmp);
            }
        }
        return hs.toString().toUpperCase();
    }

    public static String genHMAC(String data, String key) {
        byte[] result = null;
        try {
            //根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
            SecretKeySpec signinKey = new SecretKeySpec(key.getBytes("UTF-8"), HMAC_SHA1_ALGORITHM);
            //生成一个指定 Mac 算法 的 Mac 对象
            Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            //用给定密钥初始化 Mac 对象
            mac.init(signinKey);
            //完成 Mac 操作
            byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
            result = Base64.encodeBase64(rawHmac);
            BASE64Encoder encoder = new BASE64Encoder();
            String encode = encoder.encode(rawHmac);
        } catch (NoSuchAlgorithmException e) {
            System.err.println(e.getMessage());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        if (null != result) {
            return new String(result);
        } else {
            return null;
        }
    }

    public static boolean checkWxSignature(String signature, String timestamp, String nonce, String token) {
        String[] arr = new String[]{token, timestamp, nonce};
        // 将 token, timestamp, nonce 三个参数进行字典排序
        Arrays.sort(arr);
        return checkSignature(signature, arr);
    }

    /**
     * 验证签名
     *
     * @param signature
     * @param arr
     * @return
     */
    public static boolean checkSignature(String signature, String[] arr) {
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行 shal 加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        content = null;
        System.out.println("tmpStr:" + tmpStr);
        // 将sha1加密后的字符串可与signature对比，标识该请求来源于微信
        return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
    }

    public static boolean checkWxaSignature(String signature, Map<String, Object> map) {
        String[] array = StringUtil.sortStringArray(map);
        return checkSignature(signature, array);
    }

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param digest
     * @return
     */
    private static String byteToStr(byte[] digest) {
        String strDigest = "";
        for (int i = 0; i < digest.length; i++) {
            strDigest += byteToHexStr(digest[i]);
        }
        return strDigest;
    }

    /**
     * 将字节转换为十六进制字符串
     *
     * @param b
     * @return
     */
    private static String byteToHexStr(byte b) {
        char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] tempArr = new char[2];
        tempArr[0] = digit[(b >>> 4) & 0X0F];
        tempArr[1] = digit[b & 0X0F];
        String s = new String(tempArr);
        return s;
    }
}
