package com.bcxin.ins.third.gzzrx.zhonghua.util;

import com.bcxin.ins.third.gzzrx.zhonghua.security.codec.Base64;
import com.bcxin.ins.util.ConstProp;
import com.bcxin.ins.util.GlobalResources;
import com.bcxin.mybatisplus.toolkit.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;


/**
 * 加解密签名工具
 * 
 */
public class RSAUtils {

    /**
     * 获取太保公钥的key
     */
    private static String ZH_PUBLIC_KEY = ConstProp.BLANK_CHAR;

    /**
     * 获取公钥的key
     */
    private static String PUBLIC_KEY = ConstProp.BLANK_CHAR;

    /**
     * 获取私钥的key
     */
    private static String PRIVATE_KEY = ConstProp.BLANK_CHAR;

    public static void setKeys(boolean isTest){
        if(isTest){//测试环境
            ZH_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLiqOx+fIsiSHiElgFR7MhDBELTY38g/totoPqOOJECV9BE64dmfPmr/3chZbwiFJ/ick0XMqkjmZt6iNsTFQoalBjLCe8H8T0zs7+gKPMGmsYAVVVUixnMxBzi8nruXcklCmEt/vhVsVhZW5eqj3U529P7PqCg/ztMvg9ap6nhQIDAQAB";
            PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCDpx0Ed6ZBObuFQSpWjANtu+/7SScdUYs+Bai5YTYlcmWMqzZhgi/5BgsNRdl9nDg7HuJ8tlgKGAveInLqrXOh/wluLDDHUDsCxT7x9EKnSNDo6r2vD4AwkdbNdDkAmAG6ji45PFmq4jswfBLTPTrH9ie0NrmhBbkjpx2qnGP+vwIDAQAB";
            PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIOnHQR3pkE5u4VBKlaMA2277/tJJx1Riz4FqLlhNiVyZYyrNmGCL/kGCw1F2X2cODse4ny2WAoYC94icuqtc6H/CW4sMMdQOwLFPvH0QqdI0Ojqva8PgDCR1s10OQCYAbqOLjk8WariOzB8EtM9Osf2J7Q2uaEFuSOnHaqcY/6/AgMBAAECgYBfM1YBaAA6RGQ8nP3frcnA7VlBTgnR4qidBfyXaRt9gra6PQjLBBhqrBsL0wnOEkx7O/8tjbImQnM6g5Cz8Q+e9p/OkznOkk5XF3hhTmjwCMHAcKMv4AZMCa+8uyZu2dLLUwNFj0DhYcxMgb2elt7fK4qRjR9TA/B8fcEwbIbCcQJBANCTcwo4snMbwNPEVdoSk7+Ft5EYI9wGVM9/aBJTc6leBqJJyCwzYDdIHlTp8txcmOLsDSaqCqfprldzeKza7VkCQQChljUcXCAOSlc7qmmBDeVy/vdHKdlWhbyfc7G0vohmDLmkoK7TQeAmaTPnVJu1CY+IuZ+p8dKDzIy6HEKNqdHXAkAIZHZzidZ8VTiMDNFbhfXV9f5t4yfGOab99mVsn1j/Dp57tzZLuQF9F/TIe+VewocQUmkxDhSBryBpuma3pdN5AkAtXJugUIR3PotqppZjsmD9fXIxuh/XWEfxg2sZ0Hs+Gshq3Oj3SYEBh3sYr/neQsbWnTWjYc21TlEbVh+g4VGVAkAOu9i7UUnQfQfO4hOsJbrOxs4iUCVmNfXv6XrFMbkwWNzzz+hkEEfj7R/YBwRy/auEtxJZjFxyAj9mUEtbJFFz";
        }else{//生产环境
            ZH_PUBLIC_KEY = "";
            PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLiqOx+fIsiSHiElgFR7MhDBELTY38g/totoPqOOJECV9BE64dmfPmr/3chZbwiFJ/ick0XMqkjmZt6iNsTFQoalBjLCe8H8T0zs7+gKPMGmsYAVVVUixnMxBzi8nruXcklCmEt/vhVsVhZW5eqj3U529P7PqCg/ztMvg9ap6nhQIDAQAB";
            PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIuKo7H58iyJIeISWAVHsyEMEQtNjfyD+2i2g+o44kQJX0ETrh2Z8+av/dyFlvCIUn+JyTRcyqSOZm3qI2xMVChqUGMsJ7wfxPTOzv6Ao8waaxgBVVVSLGczEHOLyeu5dySUKYS3++FWxWFlbl6qPdTnb0/s+oKD/O0y+D1qnqeFAgMBAAECgYBBQx8aLxb3g09oh0HJfmYlz5ntGmGMt0GPEMWTdvVD8sotCoJFb+pleuwVBhm491j2Mz/6EGfqHHuFbpsQnIZDB3JR6N+R/lXQ9ytB3kKm9uPbTBApP7YGV9SynBQbsDg//Ti7KaYd1iwSepkBJphRr1TncMeFpFujAwdITqDPYQJBAOxfUB/icTahlQy/swZKMNwkYTe5R/74Yqji3AOCIo8LDYUb5sLHmseJzaDDKJ7oQtH1xAJYpQ1/pyo0umFmk1kCQQCXIPIUoq2xEnmqlqgqRi+Y0qBwITk7qAchTxJgdcH2pesQe9YdN3y0JlUYzcHpaZi8LXpDSkGn0qVEgPfKaQwNAkAs3+N8SicoMgD0DKjWNtuTz6xNQfWzKEDCw171o40gDv8gGC31KKPv4e+R57scgAe/iKMPtviO2X9I+f2wZCLxAkBq8hR9R9WULdfNQQnFyJ8cDUkdsFUZA0Hwb00413wn96fNpWSSjlaAAXRMtiZX77L2z1Pq1Fx7z1PGk2FGamntAkAdEIEzw0YbJbyD+Un2ToDW5cuntiKeb2K6vFCXwsMbe0de+vFwa98COf5Kc9fhCKjPA4eFGB4jX9zUYW6eFzm6";
        }
    }

    public static Map<String,String> es(String xml){
        try {
            if(StringUtils.isEmpty(ZH_PUBLIC_KEY)){
                //初始化key
                setKeys(GlobalResources.IS_TEST_ENVI);
            }
            return encryptAndSign(xml, ZH_PUBLIC_KEY, PRIVATE_KEY, "UTF-8", true, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String cd(String content,String sign){
        try {
            return checkSignAndDecrypt(content,sign,ZH_PUBLIC_KEY,PRIVATE_KEY, "UTF-8", true, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 加密并签名<br>
     * 对于<b>合作方</b>，publicKey是指保险公司的公钥，privateKey是指合作方的私钥<br>
     * 对于<b>保险公司</b>，publicKey是指合作方的公钥，privateKey是指保险公司自己的私钥<br>
     * @param bizContent 需要加密数据
     * @param publicKey  公钥
     * @param privateKey 私钥
     * @param charset    字符编码
     * @param isEncrypt  是否加密
     * @param isSign     是否加签
	 * @return CONTENT   密文  GW_CH_SIGN 签名
     * @throws Exception
     */
    public static Map<String,String> encryptAndSign(String bizContent, String publicKey, String privateKey, String charset,
                                        boolean isEncrypt, boolean isSign) throws Exception {
        if (isEmpty(charset)) {
            charset = SignConstants.CHARSET_UTF_8;
        }
        bizContent = URLEncoder.encode(bizContent,SignConstants.CHARSET_UTF_8);
        Map<String,String> map = new HashMap<String, String>();
        if (isEncrypt) {
            //加密
            String encrypted = rsaEncrypt(bizContent, publicKey, charset);
            map.put(SignConstants.CONTENT, encrypted);
            //加签
            if (isSign) {
                String sign = rsaSign(encrypted, privateKey, charset);
                map.put(SignConstants.GW_CH_SIGN, sign);
            }
        } else if (isSign) {
            // 不加密，但需要签名
        	map.put(SignConstants.CONTENT, bizContent);
            String sign = rsaSign(bizContent, privateKey, charset);
            map.put(SignConstants.GW_CH_SIGN, sign);
        }
        return map;
    }
    /**
     * 验签并解密
     * <p>
     * 对于<b>合作方</b>，publicKey是指保险公司的公钥，privateKey是指合作方的私钥<br>
     * 对于<b>保险公司</b>，publicKey是指合作方的公钥，privateKey是指保险公司自己的私钥<br>
     * 
     * @param publicKey 公钥
     * @param privateKey 私钥
     * @param isCheckSign 是否验签
     * @param isDecrypt 是否解密
     * @return 解密后明文，验签失败则异常抛出
     * @throws Exception
     */
    public static String checkSignAndDecrypt(String content,String sign, String publicKey, String privateKey, String charset,
                                        boolean isDecrypt, boolean isCheckSign) throws Exception {
        if (isEmpty(charset)) {
            charset = SignConstants.CHARSET_UTF_8;
        }
        //验签
        if (isCheckSign) {
            checkRSASign(sign, content, publicKey, charset);
        }
        //解密
        if (isDecrypt) {
            return URLDecoder.decode(rsaDecrypt(content, privateKey, charset),SignConstants.CHARSET_UTF_8);
        }
        return content;
    }

    /**
     * 公钥加密<br>
     * 对于<b>保险公司</b>，publicKey是指合作方的公钥，privateKey是指保险公司自己的私钥<br>
     * 对于<b>合作方</b>，publicKey是指保险公司的公钥，privateKey是指合作方的私钥<br>
     * 
     * @param content 待加密内容
     * @param publicKey 公钥
     * @param charset 字符集，如UTF-8, GBK, GB2312
     * @return 密文内容
     * @throws Exception
     */
    public static String rsaEncrypt(String content, String publicKey, String charset) throws Exception {
        PublicKey pubKey = getPublicKeyFromX509(SignConstants.SIGN_TYPE_RSA,
                new ByteArrayInputStream(publicKey.getBytes()));
        Cipher cipher = Cipher.getInstance(SignConstants.SIGN_TYPE_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        byte[] data = isEmpty(charset) ? content.getBytes() : content.getBytes(charset);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密  
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > SignConstants.MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, SignConstants.MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * SignConstants.MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
        out.close();

        return isEmpty(charset) ? new String(encryptedData) : new String(encryptedData, charset);
    }

    /**
     * 私钥解密<br>
     * 
     * @param content 待解密内容
     * @param privateKey 私钥
     * @param charset 字符集，如UTF-8, GBK, GB2312
     * @return 明文内容
     * @throws Exception
     */
    public static String rsaDecrypt(String content, String privateKey, String charset) throws Exception {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SignConstants.SIGN_TYPE_RSA,
                    new ByteArrayInputStream(privateKey.getBytes()));
            Cipher cipher = Cipher.getInstance(SignConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] encryptedData = isEmpty(charset) ? Base64.decodeBase64(content.getBytes()) : Base64
                    .decodeBase64(content.getBytes(charset));
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密  
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > SignConstants.MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, SignConstants.MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * SignConstants.MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();

            return isEmpty(charset) ? new String(decryptedData) : new String(decryptedData, charset);
        } catch (Exception e) {
            throw new Exception("EncodeContent = " + content + ",charset = " + charset, e);
        }
    }


    /**
     * RSA签名
     * 
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws Exception
     */
    public static String rsaSign(String content, String privateKey, String charset) throws Exception {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SignConstants.SIGN_TYPE_RSA,
                    new ByteArrayInputStream(privateKey.getBytes()));

            java.security.Signature signature = java.security.Signature.getInstance(SignConstants.SIGN_ALGORITHMS);

            signature.initSign(priKey);

            if (isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            throw new Exception("RSAcontent = " + content + "; charset = " + charset, e);
        }
    }
    /**
     * RSA签名校验<br>
     * 对于<b>保险公司</b>，publicKey是指合作方的公钥<br>
     * 对于<b>合作方</b>，publicKey是指保险公司的公钥<br>
     * 
     * @param sign
     * @param content
     * @param publicKey
     * @param charset
     * @throws Exception
     */
    public static void checkRSASign(String sign, String content, String publicKey, String charset) throws Exception {
        if (!rsaCheckContent(content, sign, publicKey, charset)) {
            throw new Exception("rsaCheck failure: sign=" + sign + ", content=" + content + ", charset=" + charset);
        }
    }


    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
        if (ins == null || isEmpty(algorithm)) {
            return null;
        }

        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        byte[] encodedKey = StreamUtils.readText(ins).getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }
    /**
     * RSA签名校验<br>
     * 对于<b>保险公司</b>，publicKey是指合作方的公钥<br>
     * 对于<b>合作方</b>，publicKey是指保险公司的公钥<br>
     * 
     * @param sign
     * @param content
     * @param publicKey
     * @param charset
     * @throws Exception
     */
    public static boolean rsaCheckContent(String content, String sign, String publicKey, String charset)
            throws Exception {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));

            java.security.Signature signature = java.security.Signature.getInstance(SignConstants.SIGN_ALGORITHMS);

            signature.initVerify(pubKey);

            if (isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new Exception("RSAcontent=" + content + ",sign=" + sign + ",charset=" + charset, e);
        }
    }

    public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        StringWriter writer = new StringWriter();
        StreamUtils.io(new InputStreamReader(ins), writer);

        byte[] encodedKey = writer.toString().getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }
    /**
     * 判断字符串是否为空
     *
     * @param value 字符串
     * @return 空或空串返回true 否则false
     */
    public static boolean isEmpty(String value) {
        return (null == value || value.trim().equals("")) ? true : false;
    }
    
}
