package com.hotent.base.util;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
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;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class RSAUtil {
	public static final String CHARSET = "UTF-8";
	public static final String RSA_ALGORITHM = "RSA";
	/**
	 * 公钥
	 */
	public static String publicKey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnhQ99yP-eEU2jXdQWc6j-wWbqNLqOLinEGBY11WJUCmzHiEycDXPc6-3YMOvrdAiHZcjkMCzU_eRnBLUqkcNw9nhQrCak-sTpEVlAV21LskD6KMf-6PsfttUvpXeCO5g3Hg48F_vbLKxb8s_lcvQgCpKBIpsUdYRcp_PgSg8BQIDAQAB";
	
	// 私钥
	private static String privateKey ="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIyeFD33I_54RTaNd1BZzqP7BZuo0uo4uKcQYFjXVYlQKbMeITJwNc9zr7dgw6-t0CIdlyOQwLNT95GcEtSqRw3D2eFCsJqT6xOkRWUBXbUuyQPoox_7o-x-21S-ld4I7mDceDjwX-9ssrFvyz-Vy9CAKkoEimxR1hFyn8-BKDwFAgMBAAECgYBt4JAco2Mi0sPyjrQuux43EE0fhtnYX54aHTfL8-dItrT-24pBKWFCyzkA58HBIuZB4jFZfyC0f-Yxh21HdA60T8EAmHcJubIFOREeQdFbvTDBYuwcrQSmTsJ17yw_qB-DZrbSd0YvpAnJnu1rBhzrcrskQPmaxM4Jv-08aKj8gQJBAMnqpcSi3onhUUVDYnQLJWIGqRjkPdWTgd4-k0JhBwxwGrIMgzsOBS21i7BfSSrKyFRhs6tgMYA0DsfIavwcP5ECQQCySCxbpvaxr8hBKXc0Xy50w4_F1IBNWY47v4kZGEf-DA8LmwtuWb-5Jdnoss8ZFx6rGxD2uZKzHWIwWbIOGmM1AkB3fRuyHOvyzlro0TKvF3V86BIjf9Yz0MH2wjMlAgRI0QKLQSpbxTU9UoUiz5Cd-TjJ7rODhD5m7uecRLdDEHhxAkBkpCGGS6aPf-_H-SR1yBVtjMgooWEcSSXlzKYUKITbgOstXgUB8mAsoJV2qxoFy-tgJBNbdD7AJiI7sSa2l2bRAkEAhjzNzzGqhHnBYrExOtuU7UZlEK-d-2DTS_6b6stokpbZncKI7G65-yX6CILZ798wGI6rzhkzYg1_bcj6b5jptQ";
	
	@Value("${RSA.privateKey:}")
	private String priKey;
	
	
    @PostConstruct
    public void setPort() {
    	if (StringUtil.isNotEmpty(this.priKey)) {
    		privateKey = this.priKey;
		}
    }
    

	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 = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
		// 得到私钥
		Key privateKey = keyPair.getPrivate();
		String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
		// map装载公钥和私钥
		Map<String, String> keyPairMap = new HashMap<String, String>();
		keyPairMap.put("publicKey", publicKeyStr);
		keyPairMap.put("privateKey", privateKeyStr);
		// 返回map
		return keyPairMap;
	}

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

	/**
	 * 得到私钥
	 * 
	 * @param privateKey
	 *            密钥字符串（经过base64编码）
	 * @throws Exception
	 */
	public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
		// 通过PKCS#8编码的Key指令获得私钥对象
		KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(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 Base64.encodeBase64URLSafeString(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, Base64.decodeBase64(data),
					privateKey.getModulus().bitLength()), CHARSET);
		} catch (Exception e) {
			throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
		}
	}

	
	/**
	 * 私钥解密
	 * 
	 * @param data
	 * @return
	 * @throws InvalidKeySpecException 
	 * @throws NoSuchAlgorithmException 
	 */
	public static String Decrypt(String data) throws NoSuchAlgorithmException, InvalidKeySpecException {
		if (StringUtil.isEmail(data)) {
			return "";
		}
		return RSAUtil.privateDecrypt(data, RSAUtil.getPrivateKey(privateKey));
	}

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

	public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
		try {
			Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
			// 每个Cipher初始化方法使用一个模式参数opmod，并用此模式初始化Cipher对象。此外还有其他参数，包括密钥key、包含密钥的证书certificate、算法参数params和随机源random。
			cipher.init(Cipher.ENCRYPT_MODE, privateKey);
			return Base64.encodeBase64URLSafeString(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, Base64.decodeBase64(data),
					publicKey.getModulus().bitLength()), CHARSET);
		} catch (Exception e) {
			throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
		}
	}

	// rsa切割解码 , ENCRYPT_MODE,加密数据 ,DECRYPT_MODE,解密数据
	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;
		}
		byte[] resultDatas = new byte[10];
		try(ByteArrayOutputStream out = new ByteArrayOutputStream();) {
			int offSet = 0;
			byte[] buff;
			int i = 0;
			while (datas.length > offSet) {
				if (datas.length - offSet > maxBlock) {
					// 可以调用以下的doFinal（）方法完成加密或解密数据：
					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;
			}
			resultDatas = out.toByteArray();
		} catch (Exception e) {
			throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
		}
		return resultDatas;
	}

	// 简单测试____________
	public static void main(String[] args) throws Exception {
		Map<String, String> keyMap = RSAUtil.createKeys(1024);
		String publicKey = keyMap.get("publicKey");
		String privateKey = keyMap.get("privateKey");
		System.out.println("公钥: \n\r" + publicKey);
		System.out.println("私钥： \n\r" + privateKey);

		System.out.println("公钥加密——私钥解密");
		String str = "123456789";
		System.out.println("\r明文：\r\n" + str);
		System.out.println("\r明文大小：\r\n" + str.getBytes().length);
		String encodedData = RSAUtil.publicEncrypt(str, RSAUtil.getPublicKey(publicKey)); // 传入明文和公钥加密,得到密文
		System.out.println("密文：\r\n" + encodedData);
		String decodedData = RSAUtil.privateDecrypt(encodedData, RSAUtil.getPrivateKey(privateKey)); // 传入密文和私钥,得到明文
		System.out.println("解密后文字: \r\n" + decodedData);

	}

}