package com.jeesharp.business.modules.pay.paytype.pufapay.baseutil;

import org.apache.commons.codec.binary.Base64;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
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.Arrays;

@Component
public class RSASign implements ApplicationRunner {
    private static RSAPublicKey publicKey = null;
    private static RSAPrivateKey privateKey = null;

    public RSAPublicKey getPublicKey() {
        return publicKey;
    }

    public RSAPrivateKey getPrivateKey() {
        return privateKey;
    }

    @Override
    public void run(ApplicationArguments args) {
        try {
            // 加载私钥
            loadPrivateKey(readPemFile(String.format("%s%s%s", "pfakey", File.separator, "privateKey.pem")));
            loadPublicKey(readPemFile(String.format("%s%s%s", "pfakey", File.separator, "publicKey.pem")));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从字符串中加载公钥
     *
     * @param publicKeyStr 公钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    private static void loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decodeBase64(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 从字符串中加载私钥
     *
     * @param privateKeyStr 私钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    private static void loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decodeBase64(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            privateKey = (RSAPrivateKey) keyFactory
                    .generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * 加密过程
     *
     * @param publicKey 公钥
     * @param data      明文数据
     * @return
     * @throws Exception 加密过程中的异常信息
     */
    public String encrypt(RSAPublicKey publicKey, String data) throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            String encrypted = Base64.encodeBase64String(cipher.doFinal(data.getBytes("UTF-8")));
            return encrypted;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    public static String decrypt(String encryptData) throws Exception {
        return decrypt(privateKey, encryptData);
    }

    /**
     * 解密过程
     *
     * @param privateKey  私钥
     * @param encryptData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    private static String decrypt(RSAPrivateKey privateKey, String encryptData)
            throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            String decrypted = new String(getMaxResultDecrypt(encryptData, cipher));
            //String decrypted = new String(cipher.doFinal(Base64.decodeBase64(encryptData)),"UTF-8");
            return decrypted;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密私钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

    //长度过长分割解密
    private static byte[] getMaxResultDecrypt(String str, Cipher cipher) throws IllegalBlockSizeException, BadPaddingException {
        byte[] inputArray = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        int inputLength = inputArray.length;
        // 最大解密字节数，超出最大字节数需要分组加密
        int MAX_ENCRYPT_BLOCK = 256;
        // 标识
        int offSet = 0;
        byte[] resultBytes = {};
        byte[] cache = {};
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
                offSet += MAX_ENCRYPT_BLOCK;
            } else {
                cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return resultBytes;
    }

    /**
     * @param data       报文原文
     * @param privateKey 私钥
     * @return signature 签名
     * @throws Exception
     */

    public static String sign(String data, String privateKey) {
        try {
            RSASign rsaEncrypt = new RSASign();
            // 加载私钥
            rsaEncrypt.loadPrivateKey(privateKey);

            // SHA1withRSA算法进行签名
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initSign(rsaEncrypt.getPrivateKey());
            byte[] datas = data.getBytes();

            // 更新用于签名的数据
            sign.update(datas);
            byte[] signature = sign.sign();

            System.out.println("X-SPDB-SIGNATURE:" + Base64.encodeBase64String(signature));
            return Base64.encodeBase64String(signature);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * @param signature 签名
     * @param publicKey 公钥
     * @param data      报文原文
     * @throws Exception
     */

    public static boolean verifySign(String signature, String publicKey,
                                     String data) {
        try {
            RSASign rsaEncrypt = new RSASign();
            // 加载公钥
            rsaEncrypt.loadPublicKey(publicKey);

            Signature verifySign = Signature.getInstance("SHA1withRSA");
            verifySign.initVerify(rsaEncrypt.getPublicKey());
            // 用于验签的数据
            verifySign.update(data.getBytes());
            byte[] signatureBytes = Base64.decodeBase64(signature);

            boolean flag = verifySign.verify(signatureBytes);
            System.out.println("验签结果：" + flag);
            return flag;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    //调用参考：
    public static void main(String[] args) throws Exception {
        String en = "oV6Impr+Q9agNc9wLp5vTAjmtD5dd5WaPf3LobfKoEm3xyOecRsz4B3fkw+HrcQNvh/O+/gZx5tLL/8OEI2Dq1lGcW+9zinndg9k4N1j4TPiFhuNuri6j/ZwwRjk6wtXIVGT+pdoTeEb/fDd3M+GjiGUuVuq35UE4UqAvg7eGVeXtikILqXZMydzFBNqPVev1T7w+nMjABt0xut1YFd1mJqruf2eMp+8bvSycxU8n6f6JavC7cbk5CbqXUGhGUJLDHTzc0oY9lOHk18JGEUBfA7hkAV8akKUZj7e2SThFEmJLYoWGP4F/nhgAwnrYlEWo542mSIyQ9xNnJ3FVzum9kKSH0JVvHA+MoaUrrmgBfAC1P0p0Pp2dMqWrP/RYtUx98dhqc78ngq3fwPZ1koLmS1tqSL7Wb4RfGV89LHCpgIwyE88DS69uxeKU4KIgeKfdDXvMEhdcidBB4nw1IMDfqKhUh7JPomzy6NdksIVqXkI+V+3XB8kGbQXyIjHJxURFANc0zqecp5ZBMLoONEBUnqqgoLBwGO98u2B6rFJ9txdVn5Atm38uldpXh777qLEXPI1WxK5QZZg3IsRS191z/TCVlpJGzlp2fN6gYEfmnDuQwYJBkA/wW9oDoTI8f0b0BPeCXtI0MFun6W8wHOvJ9iEArOfwWpapvUIdcQ9Tv0=";// 加密
        //-----------------解密

        String data = RSASign.decrypt(en);
        System.out.println("解密结果：" + data);
        String sig = "ACOsaXoNEGe22kqjQXHzh0jbIftCPjzgyu7YMxqQNQBxSWT7H5uIgQron2JiZ4MYbUBzwZrt7N9wPm0fMUmB9auqb4fEmC1QJbfkfJZ9WgpcUbdLJ+X2DlDrCyq3rE+50RxY9rwvyZDx6vCuorWSbx2V7zG+YzppByvGc/A3vLg=";

        //-----------------验签
//        boolean flag = verifySign(sig, readPemFile( + "\\publicKey.pem"), data);

    }

    private static String readPemFile(String filePath) throws IOException {
        ClassPathResource classPathResource = new ClassPathResource(filePath);
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(classPathResource.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        }
        return content.toString().replace("-----BEGIN PRIVATE KEY-----", "").replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace("-----END PUBLIC KEY-----", "");
    }
}