package com.tzy.framework.pay.util;

import com.tzy.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Enumeration;
import java.util.Map;

/**
 * rsa2
 */
@Slf4j
public class RSA2EncriptUtil {
    /** 编码 */
    public final static String ENCODE = "UTF-8";

    public final static String KEY_X509 = "X509";
    public final static String KEY_PKCS12 = "PKCS12";
    public final static String KEY_ALGORITHM = "RSA";
    public final static String CER_ALGORITHM = "MD5WithRSA";

    public final static String RSA_CHIPER = "RSA/ECB/PKCS1Padding";

    public final static int KEY_SIZE = 1024;

    //备注：1024位的证书，加密时最大支持117个字节，解密时为128； 2048位的证书，加密时最大支持245个字节，解密时为256。
    /** 1024bit 加密块 大小 */
    public final static int ENCRYPT_KEYSIZE = 117;
    /** 1024bit 解密块 大小 */
    public final static int DECRYPT_KEYSIZE = 128;

    /**
     * 请求参数明文拼接
     * @param method 请求方法
     * @param version 版本号
     * @param format 请求格式
     * @param merchantNo 商户号
     * @param signType 加密类型
     * @param signContent 请求报文明文
     * @param key 签名混淆key，平台分配
     * @return
     */
    public static String reqSingStr(String method,String version,String format,String merchantNo,String signType,String signContent,String key){
        StringBuilder signStr = new StringBuilder();
        signStr.append("method=");
        signStr.append(method);
        signStr.append("&version=");
        signStr.append(version);
        signStr.append("&format=");
        signStr.append(format);
        signStr.append("&merchantNo=");
        signStr.append(merchantNo);
        signStr.append("&signType=");
        signStr.append(signType);
        signStr.append("&signContent=");
        signStr.append(signContent);
        signStr.append("&key=");
        signStr.append(key);
        return signStr.toString();
    }

    /**
     *
     * 响应明文拼接
     */
    public static String resSingStr(Map<String,Object> respMap, String key){

        StringBuilder signStr = new StringBuilder();
        if(respMap.get("success").toString().equals("true") ){
            signStr.append("result=");
            signStr.append(respMap.get("result"));
            signStr.append("&success=");
            signStr.append(respMap.get("success"));
            signStr.append("&key=");
            signStr.append(key);
        }
        else {
            signStr.append("errorCode=" + respMap.get("errorCode"));
            signStr.append("&errorMsg=" + respMap.get("errorMsg"));
            signStr.append("&success=" + respMap.get("success"));
            signStr.append("&key=" + key);
        }
        return signStr.toString();
    }

    /**
     * base  64解密
     *
     * @param str 待解密字符串
     * @return 返回机密后的字符串
     */
    public static String base64Decode(String str) {
        String message = null;
        try {
            message = new String(new BASE64Decoder().decodeBuffer(str), ENCODE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return message;
    }
    /**
     * base  64加密
     *
     * @param str 待加密字符串
     * @return 返回加密后的字符串
     */
    public static String base64Encode(String str) {
        String message = null;
        try {
            message = new BASE64Encoder().encodeBuffer(str.getBytes(ENCODE)).replaceAll("[\\s*\t\n\r]", "");;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return message;
    }

    /**
     * 指定Cer公钥路径解密
     *
     * @param src
     * @param pubCerPath
     * @return
     */
    public static String decryptByPubCerFile(String src, String pubCerPath) {
        PublicKey publicKey = getPublicKeyFromFile(pubCerPath);
        if (publicKey == null) {
            return null;
        }
        return decryptByPublicKey(src, publicKey);
    }

    /**
     * 根据公钥解密
     *
     * @param src
     * @param publicKey
     * @return
     */
    public static String decryptByPublicKey(String src, PublicKey publicKey) {

        try {
            byte[] destBytes = rsaByPublicKey(hex2Bytes(src), publicKey, Cipher.DECRYPT_MODE);

            if (destBytes == null) {
                return null;
            }
            return new String(destBytes, ENCODE);
        } catch (UnsupportedEncodingException e) {
            log.error("解密内容不是正确的UTF8格式:", e);
        }
        return null;
    }
    /**
     * 证书模式+私钥签名
     *
     * @param content    原文
     * @param priKeyPath     私钥路径
     * @param priKeyPass 私钥密码
     * @return 返回签名
     */
    public static String sign(String content, String priKeyPath, String priKeyPass) throws Exception {
        PrivateKey privateKey = getPrivateKeyFromFile(priKeyPath, priKeyPass);
        return doSign(content, privateKey,"UTF-8");
    }

    /**
     * 签名
     *
     * @param content    原文
     * @param privateKey 私钥对象
     * @param charset    编码
     * @return 签名
     * @throws Exception 抛出异常
     */
    protected static String doSign(String content, PrivateKey privateKey, String charset) throws Exception {
        Signature signature;
        signature = Signature.getInstance("SHA256WithRSA");
        signature.initSign(privateKey);
        if (StringUtils.isEmpty(charset)) {
            signature.update(content.getBytes());
        } else {
            signature.update(content.getBytes(charset));
        }

        return byte2Hex(signature.sign());
    }


    /**
     * 公钥验签
     *
     * @param content 原文
     * @param sign    签名
     * @param pubKeyPath  公钥路径
     * @return 验签结果
     * @throws Exception 抛出异常
     */
    public static boolean verify(String content, String sign, String pubKeyPath) {
        try {
            Signature signature;
            PublicKey publicKey = getPublicKeyFromFile(pubKeyPath);
            signature = Signature.getInstance("SHA256WithRSA");
            signature.initVerify(publicKey);
            signature.update(content.getBytes("UTF-8"));
            return signature.verify(hex2Bytes(sign));
        } catch (Exception var6) {
            var6.printStackTrace();
        }
        return false;
    }

    private static String byte2Hex(byte[] srcBytes) {
        StringBuilder hexRetSb = new StringBuilder();
        byte[] arr = srcBytes;
        int len = srcBytes.length;

        for (int i = 0; i < len; ++i) {
            byte b = arr[i];
            String hexString = Integer.toHexString(255 & b);
            hexRetSb.append(hexString.length() == 1 ? 0 : "").append(hexString);
        }

        return hexRetSb.toString();
    }

    private static byte[] hex2Bytes(String source) {
        byte[] sourceBytes = new byte[source.length() / 2];

        for (int i = 0; i < sourceBytes.length; ++i) {
            sourceBytes[i] = (byte) Integer.parseInt(source.substring(i * 2, i * 2 + 2), 16);
        }

        return sourceBytes;
    }

    protected static PrivateKey getPrivateKeyFromFile(String pfxPath, String priKeyPass) {
        FileInputStream priKeyStream = null;

        try {
            priKeyStream = new FileInputStream(pfxPath);
            byte[] reads = new byte[priKeyStream.available()];
            priKeyStream.read(reads);
            PrivateKey var4 = getPrivateKeyByStream(reads, priKeyPass);
            return var4;
        } catch (Exception var14) {
            log.error("解析文件，读取私钥失败:", var14);
        } finally {
            if (priKeyStream != null) {
                try {
                    priKeyStream.close();
                } catch (Exception var13) {
                }
            }

        }

        return null;
    }

    protected static PrivateKey getPrivateKeyByStream(byte[] pfxBytes, String priKeyPass) {
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            char[] charPriKeyPass = priKeyPass.toCharArray();
            ks.load(new ByteArrayInputStream(pfxBytes), charPriKeyPass);
            PrivateKey prikey = null;
            Enumeration<String> aliasEnum = ks.aliases();
            String keyAlias;

            while (aliasEnum.hasMoreElements()) {
                keyAlias = aliasEnum.nextElement();
                prikey = (PrivateKey) ks.getKey(keyAlias, charPriKeyPass);
                if (prikey != null) {
                    break;
                }
            }

            return prikey;
        } catch (IOException var7) {
            log.error("解析文件，读取私钥失败:", var7);
        } catch (KeyStoreException var8) {
            log.error("私钥存储异常:", var8);
        } catch (NoSuchAlgorithmException var9) {
            log.error("不存在的解密算法:", var9);
        } catch (CertificateException var10) {
            log.error("证书异常:", var10);
        } catch (UnrecoverableKeyException var11) {
            log.error("不可恢复的秘钥异常", var11);
        }

        return null;
    }

    protected static PublicKey getPublicKeyFromFile(String pubCerPath) {
        FileInputStream pubKeyStream = null;
        try {
            pubKeyStream = new FileInputStream(pubCerPath);
            byte[] e = new byte[pubKeyStream.available()];
            pubKeyStream.read(e);
            PublicKey var3 = getPublicKeyByText(new String(e));
            return var3;
        } catch (FileNotFoundException var15) {
            log.error("公钥文件路径不存在:", var15);
        } catch (IOException var16) {
            log.error("公钥读取异常:", var16);
        } finally {
            if (pubKeyStream != null) {
                try {
                    pubKeyStream.close();
                } catch (IOException var14) {
                    log.error("公钥读取异常:", var14);
                }
            }

        }
        return null;
    }

    /**
     * 获取公钥对象
     *
     * @param pubKeyText 公钥（字符串）
     * @return 返回公钥对象
     */
    protected static PublicKey getPublicKeyByText(String pubKeyText) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
            BufferedReader br = new BufferedReader(new StringReader(pubKeyText));
            String line;
            StringBuilder keyBuffer = new StringBuilder();

            while ((line = br.readLine()) != null) {
                if (!line.startsWith("-")) {
                    keyBuffer.append(line);
                }
            }

            Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decodeBase64(keyBuffer.toString())));
            return certificate.getPublicKey();
        } catch (Exception var6) {
            log.error("解析公钥内容失败:", var6);
            return null;
        }
    }


    // ======================================================================================
    // 公私钥算法
    // ======================================================================================
    /**
     * 公钥算法
     *
     * @param srcData
     *            源字节
     * @param publicKey
     *            公钥
     * @param mode
     *            加密 OR 解密
     * @return
     */
    public static byte[] rsaByPublicKey(byte[] srcData, PublicKey publicKey, int mode) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_CHIPER);
            cipher.init(mode, publicKey);
            // 分段加密
            int blockSize = (mode == Cipher.ENCRYPT_MODE) ? ENCRYPT_KEYSIZE : DECRYPT_KEYSIZE;
            byte[] encryptedData = null;
            for (int i = 0; i < srcData.length; i += blockSize) {
                // 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(srcData, i, i + blockSize));
                encryptedData = ArrayUtils.addAll(encryptedData, doFinal);
            }
            return encryptedData;

        } catch (NoSuchAlgorithmException e) {
            log.error("公钥算法-不存在的解密算法:", e);
        } catch (NoSuchPaddingException e) {
            log.error("公钥算法-无效的补位算法:", e);
        } catch (IllegalBlockSizeException e) {
            log.error("公钥算法-无效的块大小:", e);
        } catch (BadPaddingException e) {
            log.error("公钥算法-补位算法异常:", e);
        } catch (InvalidKeyException e) {
            log.error("公钥算法-无效的私钥:", e);
        }
        return null;
    }

}
