package com.kxs.product.biz.util;

import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.stream.Collectors;


/**
 * rsa验签工具。
 *
 * @author 没秃顶的码农
 * @date 2024-04-23
 */
public class GdRsaUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(GdRsaUtils.class);

    public static final String KEY_ALGORITHM = "RSA";
    public static final String DEFAULT_CHARSET = "UTF-8";
    public static final String DEFAULT_PRIVATE_KEY = "";
    public static final String DEFAULT_PUBLIC_KEY = "";

    /**
     * 获取公钥
     *
     * @param publicKeyStr
     * @return
     * @throws Exception
     */
    public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.getDecoder().decode(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
            return restorePublicKey(publicKey.getEncoded());

        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (IOException e) {
            throw new Exception("公钥数据内容读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 还原公钥
     *
     * @param keyBytes
     * @return
     * @throws Exception
     */
    private static PublicKey restorePublicKey(byte[] keyBytes) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        try {
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            return factory.generatePublic(x509EncodedKeySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new Exception(e);
        }
    }

    /**
     * 通过私钥KEY获取解密私钥
     *
     * @param privateKeyStr
     * @return
     * @throws Exception
     */
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.getDecoder().decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            return restorePrivateKey(privateKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (IOException e) {
            throw new Exception("公钥数据内容读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }


    /**
     * 还原私钥
     *
     * @param keyBytes
     * @return
     * @throws Exception
     */
    private static PrivateKey restorePrivateKey(byte[] keyBytes) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        try {
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            return factory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new Exception(e);
        }
    }

    /**
     * 根据请求参数生成待签名的字符串
     *
     * @param params
     * @return
     */
    public static String getSignContent(Map<String, String> params) {
        params.remove("sign");
        Map<String, String> sortedParams = new TreeMap<>(params);

        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<>(sortedParams.keySet());
        Collections.sort(keys);
        int index = 0;
        for (String key : keys) {
            String value = sortedParams.get(key);
            if (!StrUtil.isAllBlank(key, value)) {
                content.append(index == 0 ? "" : "&").append(key).append("=").append(value);
                index++;
            }
        }
        return content.toString();
    }

    /**
     * sha256WithRsa 验签
     *
     * @param content
     * @param sign
     * @param publicKey
     * @param charset
     * @return
     */
    public static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) throws Exception {
        try {
            PublicKey pubKey = loadPublicKey(publicKey);
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(pubKey);
            if (StrUtil.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }
            return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
        } catch (Exception e) {
            throw new Exception("签名失败，RSAcontent=" + content + ";charset=" + charset + ";sign=" + sign, e);
        }
    }

    /**
     * sha256WithRsa 签名
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     */
    public static String rsa256Sign(String content, String privateKey, String charset) throws Exception {
        try {
            PrivateKey priKey = loadPrivateKey(privateKey);
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(priKey);
            if (StrUtil.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }
            byte[] signed = signature.sign();
            return new String(Base64.getEncoder().encode(signed));
        } catch (Exception e) {
            throw new Exception("签名失败，RSAcontent=" + content + ";charset=" + charset, e);
        }
    }

    /**
     * 生成接口签名
     *
     * @param reqParams
     * @return
     * @throws Exception
     */
    public static String rsa256Sign(Map<String, Object> reqParams, String privateKey) throws Exception {
        Map<String, String> params = reqParams.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> String.valueOf(v.getValue())));
        String checkContent = GdRsaUtils.getSignContent(params);
        String sign = null;
        try {
            sign = GdRsaUtils.rsa256Sign(checkContent, privateKey, DEFAULT_CHARSET);
        } catch (Exception e) {
            throw new Exception(e);
        }
        return sign;
    }

    /**
     * 校验接口请求签名
     *
     * @param reqParams
     * @return
     * @throws Exception
     */
    public static boolean checkSign(Map<String, Object> reqParams, String publicKey) throws Exception {
        Map<String, String> params = reqParams.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v -> String.valueOf(v.getValue())));
        String checkContent = GdRsaUtils.getSignContent(params);
        String sign = (String) reqParams.get("sign");
        boolean isSuccess;
        try {
            isSuccess = GdRsaUtils.rsa256CheckContent(checkContent, sign, publicKey, DEFAULT_CHARSET);
        } catch (Exception e) {
            throw new Exception(e);
        }
        return isSuccess;
    }

    /**
     * 生成RSA密钥对
     */
    public static void generateKeyPair() {
        try {
            // 指定生成密钥的算法和密钥长度（密钥长度，常用值：1024、2048、4096）
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(1024);

            // 获取密钥对
            KeyPair keyPair = kpg.genKeyPair();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

            // 获取公钥字符串
            String publicKeyString = new String(Base64.getDecoder().decode(publicKey.getEncoded()));
            // 得到私钥字符串
            String privateKeyString = new String(Base64.getDecoder().decode((privateKey.getEncoded())));

            System.out.println("公钥：" + publicKeyString);
            System.out.println("私钥：" + privateKeyString);
        } catch (Exception e) {
            LOGGER.info("生成公钥私钥异常：" + e.getMessage());
        }
    }

    public static void main(String[] args) throws Exception {
        // 生成签名
        Map<String, Object> reqParams = new HashMap<>(8);
        reqParams.put("pageNo", 1);
        reqParams.put("pageSize", 10);
        reqParams.put("brhNo", "test");
        reqParams.put("queryBeginTime", "2024-01-16");
        reqParams.put("queryEndTime", "2024-01-16");
        reqParams.put("timestamp", System.currentTimeMillis() / 1000);
        String sign = GdRsaUtils.rsa256Sign(reqParams, DEFAULT_PRIVATE_KEY);
        System.out.println("生成签名：" + sign);

        // 验签
        reqParams.put("sign", sign);
        final boolean result = GdRsaUtils.checkSign(reqParams, DEFAULT_PUBLIC_KEY);
        assert result;
    }

}