package cn.wisenergy.chnmuseum.party.common.util;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;

/**
 * https://www.jianshu.com/p/62804d9c265c
 */
public final class RSAUtils {

    //加密算法KEY
    private static final String KEY_ALGORITHM = "RSA";
    //密钥长度:512bit/1024bit/2048bit/4096bit
    private static final int KEY_SIZE = 2048;
    //http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html
    //RSA签名算法:对RSA密钥的长度不限制,推荐使用2048位以上
    public static final String SIGNATURE_ALGORITHM_1 = "SHA1WithRSA";
    //RSA2签名算法:强制要求RSA密钥的长度至少为2048
    public static final String SIGNATURE_ALGORITHM_2 = "SHA256WithRSA";
    //密钥格式:PKCS#1/PKCS#8
    public static final String RSA_TYPE = "RSA/ECB/PKCS1Padding";

    private final static String PUBLIC_KEY_NAME = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsTVgqPjswjCKU7kjOfZO/jB1Oiz3zrW0kLfjBiRXHnXrm3Br4cgpA78snK/isaVykOBJYL549JkgHqTo17RSdqdS2i0rjzEUtKEotSpoP9XzNxt3ufKSq1MxBU5ZVuJTJ4Juat9RKcgNvhTc5NYMjF7PsBrrqPolgOd9Y5VyMaNRch+3owlDNzck0zWXAWas8KNJuV68ZH6hqu0Lb0QfrWETkm4o3ah7A1ss/UB//XSg0wipMa2em9WI6hUxW87JKR3W63dx89q9aP589UeE5XpamDhr1fglIpzeS/SdA61cY4K5v9v+mGxI3jePc6RSETvwc8P68/XNViSgT+uJAwIDAQAB";
    private final static String PRIVATE_KEY_NAME = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCxNWCo+OzCMIpTuSM59k7+MHU6LPfOtbSQt+MGJFcedeubcGvhyCkDvyycr+KxpXKQ4Elgvnj0mSAepOjXtFJ2p1LaLSuPMRS0oSi1Kmg/1fM3G3e58pKrUzEFTllW4lMngm5q31EpyA2+FNzk1gyMXs+wGuuo+iWA531jlXIxo1FyH7ejCUM3NyTTNZcBZqzwo0m5XrxkfqGq7QtvRB+tYROSbijdqHsDWyz9QH/9dKDTCKkxrZ6b1YjqFTFbzskpHdbrd3Hz2r1o/nz1R4TlelqYOGvV+CUinN5L9J0DrVxjgrm/2/6YbEjeN49zpFIRO/Bzw/rz9c1WJKBP64kDAgMBAAECggEAKf9ohSNZmTw/zE/YVWhWmE+LuNnncQoHXTT3jQEX1JRF3nTqXHw/nC+2tvvIUinP2R4OxereZ7nSrvCObnDCCnTlYefKpwkOyzBWoXkHc/mUxr6vxVYEBK6Ws5c5/SluY7K50IJUjf5no2D2aRWBq9LcjfQTXdzK/p3eTbehdLnHITIilY699lN0CIwPpvNjAIOwYzyoqI7xQ8S5bopl1A1gKK7MN9EQb6aqn7kNSQCkwRq+1dETlZpfl7He4Fc4Q7brEQf7Qzdk2s2ddnlOU8Q4An4A+m2NMow0abe7g1EnVlw4cag0E6/tbC21Hy6Oa/LpZmRMiZU4iuo/PvSYYQKBgQDXJUiM0ILUqGcv6FbKFEu6MjT2JgClK2xiMFv8MKPyDth+XxjhvqV1FKMVNXkdrtR8lw3qnTquaxqor4IQMLsZa775H2zefYuqiOnFD9W4NpjwxG7yWhtbpSgWIQSHXVDO6mIR6Qh0lAUk4AYwjEae1auZgBoM5YsKMxN7PFDUHwKBgQDS2+Jg9GP1j8cmO14J/f+wBqTme1oz4bkES9tGcFapdVdlkPf5ahVH+yDhrgGVCcO2w6sam06WrmxV4H4OV5wl94hr36NC6S2lmIIF9DuX21cZandWI5vJr6umxrlo3f5+tpSC9Ekgd7Yro44+DohlMrUbAFwm6+hSIin210hOnQKBgFMJEr02ZKhK7rhwxb5souWUJxixhiI5ZjVnULk+1KfBzxDHB0VpXVaYxnCTGNG5/kyvyDE6ycEzmTBtvJcfF7cx/J0N4ejlL5h8Cy2BdQ0KFXEnf2KOIGz4i0YvLB5Kh7u0fnFHLXpA/tNCm2D1YvQ+p2IxLNy5YzZmjChOGSwlAoGAYAyTYTDR+8DDqbGvU4PlHazZHrgfzbRLkB5xwvNXNpfh+L0BiBZ/nStSvhqCU/5/rVgSL+uA7/iCFthM84GK+mx4MxNLDiajiWQgDlBDeRgL4+Lwe0d7JnMkELNDL/a3f8bxfXmDegut6tD08WWzUc24W5VdZMlfKVKwpOVJ5a0CgYByPWRuA7yJ+cWkmZl9yMCpNziSJgm7A1TwI9SRpfgx+azrLVSPZAYKzwmpsiOmuD/SSyDS7YE2d4QT/C+jbGlGYa3pGN7XUqmJVPhn9sJzoCHQf5LPbS5cLRC0c0qjFwsOw5hTVa0pPTiyRjF7xKc4yzBotifs4VdhXSH5GurFJg==";
    private final static String SERVICE_PUBLIC_KEY_NAME = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsTVgqPjswjCKU7kjOfZO/jB1Oiz3zrW0kLfjBiRXHnXrm3Br4cgpA78snK/isaVykOBJYL549JkgHqTo17RSdqdS2i0rjzEUtKEotSpoP9XzNxt3ufKSq1MxBU5ZVuJTJ4Juat9RKcgNvhTc5NYMjF7PsBrrqPolgOd9Y5VyMaNRch+3owlDNzck0zWXAWas8KNJuV68ZH6hqu0Lb0QfrWETkm4o3ah7A1ss/UB//XSg0wipMa2em9WI6hUxW87JKR3W63dx89q9aP589UeE5XpamDhr1fglIpzeS/SdA61cY4K5v9v+mGxI3jePc6RSETvwc8P68/XNViSgT+uJAwIDAQAB";

    /**
     * 生成公、私钥
     * 根据需要返回String或byte[]类型
     *
     * @return
     */
    public static ArrayList<String> createRSAKeys() {
        ArrayList<String> array = new ArrayList<>();
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
            KeyPair keyPair = keyPairGenerator.generateKeyPair();

            PublicKey publicKey = keyPair.getPublic();
            PrivateKey privateKey = keyPair.getPrivate();

            //获取公、私钥值
            String publicKeyValue = Base64.getEncoder().encodeToString(publicKey.getEncoded());
            String privateKeyValue = Base64.getEncoder().encodeToString(privateKey.getEncoded());

            //存入
            array.add(publicKeyValue);
            array.add(privateKeyValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return array;
    }

    //获取本地RSA公钥
    public static PublicKey getPublicKey() {
        try {
            return getPublicKey(PUBLIC_KEY_NAME);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取本地RSA公钥
    public static String getPublicKeyString() {
        try {
            return PUBLIC_KEY_NAME;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取服务器RSA公钥
    public static PublicKey getServicePublicKey() {
        try {
            return getPublicKey(SERVICE_PUBLIC_KEY_NAME);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取RSA公钥 根据钥匙字段
    public static PublicKey getPublicKey(String key) {
        try {
            byte[] byteKey = Base64.getDecoder().decode(key);
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(byteKey);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return keyFactory.generatePublic(x509EncodedKeySpec);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取RSA私钥   根据钥匙字段
    private static PrivateKey getPrivateKey(String key) {
        try {
            byte[] byteKey = Base64.getDecoder().decode(key);
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(byteKey);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

            return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    //本地RSA私钥 签名
    public static String sign(String requestData) {
        String signature = null;
        byte[] signed;
        try {
            PrivateKey privateKey = getPrivateKey(PRIVATE_KEY_NAME);
            Signature Sign = Signature.getInstance(SIGNATURE_ALGORITHM_2);
            Sign.initSign(privateKey);
            Sign.update(requestData.getBytes());
            signed = Sign.sign();
            signature = Base64.getEncoder().encodeToString(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signature;
    }

    //公钥验证签名   base64签名 signature   签名内容requestData
    public static boolean verifySign(String requestData, String signature) {
        boolean verifySignSuccess = false;
        try {
            PublicKey publicKey = getServicePublicKey();
            Signature verifySign = Signature.getInstance(SIGNATURE_ALGORITHM_2);
            verifySign.initVerify(publicKey);
            verifySign.update(requestData.getBytes());

            verifySignSuccess = verifySign.verify(Base64.getDecoder().decode(signature));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return verifySignSuccess;
    }

    public static String encrypt(String clearText) {
        String encryptedBase64 = "";
        try {
            Key key = getServicePublicKey();
            final Cipher cipher = Cipher.getInstance(RSA_TYPE);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedBytes = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8));
            encryptedBase64 = Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedBase64;
    }

    public static String encrypt(String clearText, String publicKey) {
        String encryptedBase64 = "";
        try {
            Key key = getPublicKey(publicKey);
            final Cipher cipher = Cipher.getInstance(RSA_TYPE);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedBytes = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8));
            encryptedBase64 = Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encryptedBase64;
    }

    public static String decrypt(String encryptedBase64, String privateKey) {
        String decryptedString = "";
        try {
            Key key = getPrivateKey(privateKey);
            final Cipher cipher = Cipher.getInstance(RSA_TYPE);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            decryptedString = new String(decryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return decryptedString;
    }

}