package com.trs.project.util;

import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class RSAUtilGgzy {

    public static final String KEY_ALGORITHM = "RSA";

    private static final String PUBLIC_KEY = "RSAPublicKey";

    private static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIXX5mUam7SgTwxlmm1rFjJn+CaMs+TPHXNyEbRj04gQ4HHsA2vAkeGjnSr6Jg3cHkNJaraU1rk+tSPb7lCUxzvI4H3JGwnxeFCsPVthhJBd4lh9UvoR32D/qO4SaP2YpVVkIrE5gEJFcbUFMIV+HcQnbwfkwIqGea2kGtYOhAQTAgMBAAECgYA8TLa6GjDIQekbNrNKVJKdhwXfqEhr73vfRScA1HC6vo/AA4rPAuU2eRYYBLccS20+91aih4/tyXimfTFzEHRprRDipyAZQcVLhGdyE/HBdDZBvzLxPumnNJHpZ6FhS9N0UF4cA8CHFAfGkK4vM/Sfi84//Mo3FjCDCII71CpXEQJBAM8XdHJcvQ2dkxnNtGHjljZdwrReWyBcOTJKEKaGkWT3TWBrA70+4OUmQFdIjjBfy4WX+W9MYB9ifI4AFEV1IhcCQQClc+5W4CVfMZayZ2lOSZXkGZf5gN5BYCTIdBcQ0mjCCj9m7+AamwHKJunzPrAIS1YlhtcmhHMXWCyEU/pqnpdlAkAstBBz+2boAILXiBsPInSziavVCaU8KAxEuc48VEIgfG3ciV1GRcOAU7CB5uyirpsdXIAAsYjyXW0rrgKyN6XDAkEAmNcQfGaRf2+ys1m+ONjXQ6C0+IKoTiI/uIEtcZe9uEp5KWE8V7eLnq/1/IOTYAYgLYhzphxCqXUForRlJysb1QJAL0aFB/OLfA7b19h0As011vZuccYJ0Rnunf4a1k4VjfMCOkFRoZlr0LuOwhGqtXgQ2/NBgL2il7KKkzXdSZidbQ==";

	// 1024 bits 的 RSA 密钥对，最大加密明文大小
    private static final int MAX_ENCRYPT_BLOCK = 117;

    // 1024 bits 的 RSA 密钥对，最大解密密文大小
    private static final int MAX_DECRYPT_BLOCK = 128;

    // 生成密钥对
    public static Map<String, Object> initKey(int keysize) throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        // 设置密钥对的 bit 数，越大越安全
        keyPairGen.initialize(keysize);
        KeyPair keyPair = keyPairGen.generateKeyPair();

        // 获取公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    // 获取公钥字符串
    public static String getPublicKeyStr(Map<String, Object> keyMap) {
        // 获得 map 中的公钥对象，转为 key 对象
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        // 编码返回字符串
        return encryptBASE64(key.getEncoded());
    }

    // 获取私钥字符串
    public static String getPrivateKeyStr(Map<String, Object> keyMap) {
        // 获得 map 中的私钥对象，转为 key 对象
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        // 编码返回字符串
        return encryptBASE64(key.getEncoded());
    }

    // 获取公钥
    public static PublicKey getPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }

    // 获取私钥
    public static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * BASE64 编码返回加密字符串
     *
     * @param key 需要编码的字节数组
     * @return 编码后的字符串
     */
    public static String encryptBASE64(byte[] key) {
        return new String(Base64.getEncoder().encode(key));
    }

	/**
     * BASE64 解码，返回字节数组
     *
     * @param key 待解码的字符串
     * @return 解码后的字节数组
     */
    public static byte[] decryptBASE64(String key) {
        return Base64.getDecoder().decode(key);
    }

    /**
     * 公钥加密
     *
     * @param text         待加密的明文字符串
     * @param publicKeyStr 公钥
     * @return 加密后的密文
     */
    public static String encrypt1(String text, String publicKeyStr) {
        try {
            log.info("明文字符串为:[{}]", text);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr));
            byte[] tempBytes = cipher.doFinal(text.getBytes("UTF-8"));
            return Base64.getEncoder().encodeToString(tempBytes);
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e);
        }
    }

    /**
     * 私钥解密
     *
     * @param secretText    待解密的密文字符串
     * @return 解密后的明文
     */
    public static String decrypt1(String secretText) {
        try {
            String privateKeyStr = PRIVATE_KEY;
            // 生成私钥
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr));
            // 密文解码
            byte[] secretTextDecoded = Base64.getDecoder().decode(secretText.getBytes("UTF-8"));
            byte[] tempBytes = cipher.doFinal(secretTextDecoded);
            return new String(tempBytes);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e);
        }
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> keyMap;
        String cipherText;
        // 原始明文
        String content = "春江潮水连海平，海上明月共潮生。滟滟随波千万里，何处春江无月明。";

        // 生成密钥对
        keyMap = initKey(1024);
        String publicKey = getPublicKeyStr(keyMap);
        log.info("公钥:[{}]，长度:[{}]", publicKey, publicKey.length());
        String privateKey = getPrivateKeyStr(keyMap);
        log.info("私钥:[{}]，长度:[{}]", privateKey, privateKey.length());

        // 加密
/*        cipherText = encrypto(content, publicKey);
        log.info("加密后的密文:[{}]，长度:[{}]", cipherText, cipherText.length());

        // 解密
        String plainText = decrypt1(cipherText, privateKey);
        log.info("解密后明文:[{}]", plainText);*/
    }
}

