package cn.freemud.util;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
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.HashMap;
import java.util.Map;

/**
 * All rights Reserved, Designed By www.freemud.com
 *
 * @version V1.0
 * @Title:
 * @Package: com.freemud.base.util
 * @Descripttion:
 * @author: genyou.cui
 * @date: 2018/5/24 14:57
 * @Copyright: 2017 www.freemud.cn Inc. All rights reserved.
 * 注意：本内容仅限于上海非码科技内部传阅，禁止外泄以及用于其他的商业目.
 */
public class RSACrypt {
    public static final String CHARSET = "UTF-8";
    public static final String RSA_ALGORITHM = "RSA";

    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    public static Map<String, String> createKeys(int keySize) {
        //为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
        }

        //初始化KeyPairGenerator对象,密钥长度
        kpg.initialize(keySize);
        //生成密匙对
        KeyPair keyPair = kpg.generateKeyPair();
        //得到公钥
        Key publicKey = keyPair.getPublic();
        String publicKeyStr = (new BASE64Encoder()).encodeBuffer(publicKey.getEncoded());
        //得到私钥
        Key privateKey = keyPair.getPrivate();
        String privateKeyStr = (new BASE64Encoder()).encodeBuffer(privateKey.getEncoded());
        Map<String, String> keyPairMap = new HashMap<String, String>();
        keyPairMap.put(PUBLIC_KEY, publicKeyStr);
        keyPairMap.put(PRIVATE_KEY, privateKeyStr);

        return keyPairMap;
    }

    //map对象中存放公私钥
    public static Map<String, String> initKey() throws Exception {
        return createKeys(2048);
    }

    public static RSAPublicKey getPublicKey(Map<String, String> keyMap) throws Exception {
        RSAPublicKey key = getPublicKey(keyMap.get(PUBLIC_KEY));
        return key;
    }

    //获得私钥
    public static RSAPrivateKey getPrivateKey(Map<String, String> keyMap) throws Exception {
        RSAPrivateKey key = getPrivateKey(keyMap.get(PRIVATE_KEY));
        return key;
    }

    /**
     * 得到公钥
     *
     * @param publicKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        //通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec((new BASE64Decoder()).decodeBuffer(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /**
     * 得到私钥
     *
     * @param privateKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        //通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec((new BASE64Decoder()).decodeBuffer(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    /**
     * 公钥加密
     *
     * @param data
     * @param publicKey
     * @return
     */
    public static String publicEncrypt(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return (new BASE64Encoder()).encodeBuffer(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 私钥解密
     *
     * @param data
     * @param privateKey
     * @return
     */

    public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, (new BASE64Decoder()).decodeBuffer(data), privateKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 私钥加密
     *
     * @param data
     * @param privateKey
     * @return
     */

    public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return (new BASE64Encoder()).encodeBuffer(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 公钥解密
     *
     * @param data
     * @param publicKey
     * @return
     */

    public static String publicDecrypt(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, (new BASE64Decoder()).decodeBuffer(data), publicKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {

            }
        }
        return resultDatas;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
        String testData = "18072015420000000014";

        String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsJAzaJuhZFsqq4lsRz+RqRLeCXFeTjpB\n" +
                "pGBnFxcu4gov4VZfYAO+rhJWFOU5G0WjxdXRmjl4fQ4cZuohhuf8JhMSHVKvKBd9iNkYc7bJu/OO\n" +
                "zcHbjK9nFmBRd1G08+ongSDaUAKgTsyTxBz4EzBKc47Oj5eLxIhM12gW6vfkTmbrE6fvX3FoUwl/\n" +
                "EnYZfhHOTAI2wq+21DCKNdPrZdq4iX9bfHtIbjs1fUqsdBl9zk1uC9Ub44IgO7m99sOmA01E3pB6\n" +
                "Z9zRpu8Bs6UuFi2CXsF1+d9W+r/lYqAFLAfp5Mcc5WGXE3pRndKRnJMj4JdlA+RamA3hVF1hds4o\n" +
                "lP/QMQIDAQAB";
        /*String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCP2ShTLxG96nCLSO0Rx/7XnMxaeW8mDDXoOGfwaY9O2EF4m5xxUPh95zdhTvPZGZJCReikZOQAZRObkuTvc48inzdXGHBq/aix6v20ZTkK+R/mSlYmYKH5MiP3qbmPAovF7y17dJmXG45DrGOTq8i6HPYLm6C7GCvhO5r5pc5LqwIDAQAB";
*/
        String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwkDNom6FkWyqriWxHP5GpEt4J\n" +
                "cV5OOkGkYGcXFy7iCi/hVl9gA76uElYU5TkbRaPF1dGaOXh9Dhxm6iGG5/wmExIdUq8oF32I2Rhz\n" +
                "tsm7847NwduMr2cWYFF3UbTz6ieBINpQAqBOzJPEHPgTMEpzjs6Pl4vEiEzXaBbq9+ROZusTp+9f\n" +
                "cWhTCX8Sdhl+Ec5MAjbCr7bUMIo10+tl2riJf1t8e0huOzV9Sqx0GX3OTW4L1RvjgiA7ub32w6YD\n" +
                "TUTekHpn3NGm7wGzpS4WLYJewXX531b6v+VioAUsB+nkxxzlYZcTelGd0pGckyPgl2UD5FqYDeFU\n" +
                "XWF2ziiU/9AxAgMBAAECggEBAKSwDFORfquF4boYt3eJksr6f0KdjRI4govZI69E/4T1bY2oq9N6\n" +
                "CryA++wHlwmaHlVkyForKviyVcmkr1sPPYEiHq5dNmWV65DetlEQeQCoAFZpcOi7ntMfSj2aZ8dv\n" +
                "bCOkyh0BJhf5+AZf4wC69WbuGDuVZlgNrmbpfx7x0/FPvZwUyL7IO5vNmgSOxEBqVLB+ou1cFW7V\n" +
                "fR8+AlQ/z3GcbfbM5u+OLmjlEXF8pcNqZEYsNeAsXJ3znE/NxTgzXZ1SQ+jGw1Ts0pQtF2rbKl9z\n" +
                "Puh61DOJNHqzy8BwOkJ/Nm9kq3/oBrfKqGxmN5y9PYY/by4xhxIGaQPH0KnqwnECgYEA6fiAyjMH\n" +
                "bOXY+EN/EojcSotJv41Wg419kSR2eOHPXBRhYoN/dJJKEMdkCEUr3VBrMwxlRNRFaYUhuH/kFy9m\n" +
                "j31VePWhpNSTSICqE8+HeAzZaawPX9qohuacTakjVjM1p1hPjpG6VOQ5QDLsmWLwRX2vzEZGrJb/\n" +
                "OU/E466OOcUCgYEAwS/7UNRhh4XklJX7SGjDKiEJMYBbdEjk1XWklY35JS4anq5nDxg2ljONOHtO\n" +
                "XqdvYBiB8aroUQn5KGOwNbbbHPOCSU0qbWdyFaNNAJIx+yYfuFR9Kgkj3IXWjZGgOU7e7TVYx38G\n" +
                "kAvlsgW2fQKhdtfm/8MLW7Sa0nSVZ+L/330CgYA73cMOecofmhxPQe4EzxIW3NcM9Vw6ovyLI9pQ\n" +
                "ie7l760PaPg22quq6Wn2F8Tmhwo28KgE2Y+9NTxwmgDB0fYbJwW0wp0OsPnSpmFeWk6ecCM9P7Jb\n" +
                "Y3aVtfIuiOWqy8WhwR3yK+WSjSb8/0q84Kz8CvHtgdnZlXVOs9N8MpDfxQKBgG2+wjgCznzclSPL\n" +
                "p/pUeTo/lidm0Y1Xs5eHwz2qb3StojWuwaETkj71fi4t7jFbUKv4gCk3e849qitFpl59SDBELSlT\n" +
                "Ct0nvOmJ4T6Hzpjr6W0fohL1KkgmR+IYbjrgEUgT3CFIigPenlfpYufVTMBkmjeFJ7iq+qC/1fg0\n" +
                "PkNJAoGBAOeLSYVlojXvJInY6NsWnDOaarPvNlQ1YF2kldEgdDX/brUAeUk519GXrIAP7t6GMsE0\n" +
                "hR2f3orXnk1k9WlYx8GAdnLdub/tIaIymgtWiORPqotsBUUsm8SIeQo3wGN99Lbi4pkorn4MJUbH\n" +
                "WwO8znorsfbrewC+Lsmcu1MGERGO";
        /*String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw8VNAbLz+nuyL1Nnw/tqR2hxhBEvt66z\n" +
                "iGqeqAECRVubAtuMfgnWRiXVNTtL3/eRtdpDvUFQJr2vOG6RplU72oPBvTl5c5ji+Z1hTYZcIfnA\n" +
                "I4g7vhxXUDc/bzd2odvTegoRJE8y86IKng4SBdcHqgvNL5re7h1oH1sJB3GZfPDmxiIodp+lImyT\n" +
                "s4GwoRtK0GVOgbe1QiIRDMRjN/3z+1/O1xrKJwK9jOGkc0Qzh8EhYzYVPCH4oU1NKvWbG7ZHdTl6\n" +
                "OprnksvTxadW3TNeBzoKI+2nClQlTXN3UQTrm933UezdlAdIlVGlZ/0rSnlrYGXBWB9lslzG1l/H\n" +
                "9x3ihQIDAQAB";*//*
        String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw8VNAbLz+nuyL1Nnw/tqR2hxhBEvt66z\n" +
                "iGqeqAECRVubAtuMfgnWRiXVNTtL3/eRtdpDvUFQJr2vOG6RplU72oPBvTl5c5ji+Z1hTYZcIfnA\n" +
                "I4g7vhxXUDc/bzd2odvTegoRJE8y86IKng4SBdcHqgvNL5re7h1oH1sJB3GZfPDmxiIodp+lImyT\n" +
                "s4GwoRtK0GVOgbe1QiIRDMRjN/3z+1/O1xrKJwK9jOGkc0Qzh8EhYzYVPCH4oU1NKvWbG7ZHdTl6\n" +
                "OprnksvTxadW3TNeBzoKI+2nClQlTXN3UQTrm933UezdlAdIlVGlZ/0rSnlrYGXBWB9lslzG1l/H\n" +
                "9x3ihQIDAQAB";

        String UPP_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCP2ShTLxG96nCLSO0Rx/7XnMxaeW8mDDXoOGfwaY" +
                "9O2EF4m5xxUPh95zdhTvPZGZJCReikZOQAZRObkuTvc48inzdXGHBq/aix6v20ZTkK+R/mSlYmYKH5MiP3qbmPAovF7y17" +
                "dJmXG45DrGOTq8i6HPYLm6C7GCvhO5r5pc5LqwIDAQAB";
*/

        System.err.println("原文：" + testData);
        String s2 = RSACrypt.publicEncrypt(testData, RSACrypt.getPublicKey(publicKey));
        System.err.println("加密：" + s2);
        String s = (new BASE64Encoder()).encode(s2.getBytes());
        System.err.println("base64 编码处理 ：" + s);
        String s3 = new String((new BASE64Decoder()).decodeBuffer(s), "utf-8");
        System.err.println("base64 解码处理 ：" + s3);
        String s1 = RSACrypt.privateDecrypt(s3, RSACrypt.getPrivateKey(privateKey));
        System.err.println("解密：" + s1);


        /*String tmp = "bzd2bmFIYlNLaERLWFVDTlBxRDVJb3dIRDNPZTN3YU9zb1UvWFBKRnFKUEVxWEJLcnNLazJtVFFlNnVuQjhZNEtsNHJ6M1VQMzFMelUxeWhSaVdnMkJwa2JiQldwNTk0dmhKZlJkaHlDc0IwN2d6dEk0eVVpaWtkUlpHeDRSWmdzQlI5MjZQNDYrZFFxTXc5VUJ6YkNwNWdVUGk1QzB0RWhkSUZSUzRXT2dzPQ==";
        String s3 = new String((new BASE64Decoder()).decodeBuffer(tmp), "utf-8");
        System.err.println("base64 解码处理 ："+s3);
        String s1 = RSACrypt.privateDecrypt(s3, RSACrypt.getPrivateKey(privateKey));
        System.err.println("解密："+s1);*/

    }

}
